自定义分配器与承诺和打包任务
Custom allocators vs. promises and packaged tasks
标准 promise
/packaged_task
的分配器获取构造函数是否应该仅将分配器用于状态对象本身,或者应该保证 all(内部)相关对象?
[futures.promise]: "...allocate memory for the shared state"
[futures.task.members]: "...allocate memory needed to store the internal data structures"
特别是以下错误或功能?
*MSVC 2013.4,提升 1.57,short_alloc.h by Howard Hinnant
示例 1
#define BOOST_THREAD_VERSION 4
#include <boost/thread/future.hpp>
#include "short_alloc.h"
#include <cstdio>
void *operator new( std::size_t s ) {
printf( "alloc %Iu\n", s );
return malloc( s );
}
void operator delete( void *p ) {
free( p );
}
int main() {
const int N = 1024;
arena< N > a;
short_alloc< int, N > al( a );
printf( "[promise]\n" );
auto p = boost::promise< int >( std::allocator_arg, al );
p.set_value( 123 );
printf( "[packaged_task]\n" );
auto q = boost::packaged_task< int() >( std::allocator_arg, al, [] { return 123; } );
q();
return 0;
}
输出:
...
[promise]
alloc 8
alloc 12
alloc 8
alloc 24
[packaged_task]
alloc 8
alloc 12
alloc 8
alloc 24
FWIW,默认分配器的输出是
...
[promise]
alloc 144
alloc 8
alloc 12
alloc 8
alloc 16
[packaged_task]
alloc 160
alloc 8
alloc 12
alloc 8
alloc 16
示例 2
AFAICT,MSVC 的 std::mutex
进行了不可避免的堆分配,因此使用它的 std::promise
也是如此。这是合规行为吗?
N.B。您的代码有几个问题。在 C++14 中,如果替换 operator delete(void*)
,则还必须替换 operator delete(void*, std::size)t)
。您可以使用功能测试宏来查看编译器是否需要:
void operator delete( void *p ) {
free( p );
}
#if __cpp_sized_deallocation
// Also define sized-deallocation function:
void operator delete( void *p, std::size_t ) {
free( p );
}
#endif
其次 size_t
的正确 printf 格式说明符是 zu
而不是 u
,因此您应该使用 %Izu
.
AFAICT, MSVC's std::mutex
does an unavoidable heap allocation, and therefore, so does std::promise
which uses it. Is this a conformant behaviour?
std::mutex
是否应该使用动态分配当然值得怀疑。它的构造函数不能,因为它必须是 constexpr
。它可能会延迟分配直到第一次调用 lock()
或 try_lock()
但 lock()
没有将获取资源失败列为有效的错误条件,这意味着 try_lock()
可以如果无法分配所需的资源,则无法锁定无竞争的互斥锁。如果你眯着眼睛看,这是允许的,但并不理想。
但是关于你的主要问题,正如你引用的那样,标准只针对 promise
:
The second constructor uses the allocator a
to allocate memory for the shared state.
这并没有说明 promise 所需的 other 资源。可以合理地假设任何同步对象(如互斥锁)都是共享状态的一部分,而不是承诺,但措辞并不要求分配器用于共享状态成员所需的内存,仅用于共享状态所需的内存本身。
对于packaged_task
,措辞更宽泛,暗示所有内部状态都应该使用分配器,尽管可以说这意味着分配器用于为存储的任务和共享状态获取内存,但是共享状态的成员不必使用分配器。
总而言之,我不认为标准是 100% 明确是否允许 MSVC 实现,但恕我直言,不需要来自 malloc
或 new
的额外内存的实现更好(这就是 libstdc++ <future>
实现的工作原理)。
标准 promise
/packaged_task
的分配器获取构造函数是否应该仅将分配器用于状态对象本身,或者应该保证 all(内部)相关对象?
[futures.promise]: "...allocate memory for the shared state"
[futures.task.members]: "...allocate memory needed to store the internal data structures"
特别是以下错误或功能?
*MSVC 2013.4,提升 1.57,short_alloc.h by Howard Hinnant
示例 1
#define BOOST_THREAD_VERSION 4
#include <boost/thread/future.hpp>
#include "short_alloc.h"
#include <cstdio>
void *operator new( std::size_t s ) {
printf( "alloc %Iu\n", s );
return malloc( s );
}
void operator delete( void *p ) {
free( p );
}
int main() {
const int N = 1024;
arena< N > a;
short_alloc< int, N > al( a );
printf( "[promise]\n" );
auto p = boost::promise< int >( std::allocator_arg, al );
p.set_value( 123 );
printf( "[packaged_task]\n" );
auto q = boost::packaged_task< int() >( std::allocator_arg, al, [] { return 123; } );
q();
return 0;
}
输出:
...
[promise]
alloc 8
alloc 12
alloc 8
alloc 24
[packaged_task]
alloc 8
alloc 12
alloc 8
alloc 24
FWIW,默认分配器的输出是
...
[promise]
alloc 144
alloc 8
alloc 12
alloc 8
alloc 16
[packaged_task]
alloc 160
alloc 8
alloc 12
alloc 8
alloc 16
示例 2
AFAICT,MSVC 的 std::mutex
进行了不可避免的堆分配,因此使用它的 std::promise
也是如此。这是合规行为吗?
N.B。您的代码有几个问题。在 C++14 中,如果替换 operator delete(void*)
,则还必须替换 operator delete(void*, std::size)t)
。您可以使用功能测试宏来查看编译器是否需要:
void operator delete( void *p ) {
free( p );
}
#if __cpp_sized_deallocation
// Also define sized-deallocation function:
void operator delete( void *p, std::size_t ) {
free( p );
}
#endif
其次 size_t
的正确 printf 格式说明符是 zu
而不是 u
,因此您应该使用 %Izu
.
AFAICT, MSVC's
std::mutex
does an unavoidable heap allocation, and therefore, so doesstd::promise
which uses it. Is this a conformant behaviour?
std::mutex
是否应该使用动态分配当然值得怀疑。它的构造函数不能,因为它必须是 constexpr
。它可能会延迟分配直到第一次调用 lock()
或 try_lock()
但 lock()
没有将获取资源失败列为有效的错误条件,这意味着 try_lock()
可以如果无法分配所需的资源,则无法锁定无竞争的互斥锁。如果你眯着眼睛看,这是允许的,但并不理想。
但是关于你的主要问题,正如你引用的那样,标准只针对 promise
:
The second constructor uses the allocator
a
to allocate memory for the shared state.
这并没有说明 promise 所需的 other 资源。可以合理地假设任何同步对象(如互斥锁)都是共享状态的一部分,而不是承诺,但措辞并不要求分配器用于共享状态成员所需的内存,仅用于共享状态所需的内存本身。
对于packaged_task
,措辞更宽泛,暗示所有内部状态都应该使用分配器,尽管可以说这意味着分配器用于为存储的任务和共享状态获取内存,但是共享状态的成员不必使用分配器。
总而言之,我不认为标准是 100% 明确是否允许 MSVC 实现,但恕我直言,不需要来自 malloc
或 new
的额外内存的实现更好(这就是 libstdc++ <future>
实现的工作原理)。