[context][fiber] implementation comments

Hi Oliver, I have a couple of comments on implementation context: i noticed you migrate out of the context::create idiom and now context is noncopyable, so it seems right now the only way to have a recyclable context as a member is hold a pointer/ref and allocate on the heap. This might be good for most use cases (which is probably the boost design principles) but i think it leaves out cases where, due to performance constraints, one either wants to recycle a context without touching the heap. Which leaves me to my other comment; could you add an template parameter for a allocator class so all new/delete operations are delegated to it? fiber: this applies here too; can you make the fiber take template parameters for the stack type and possibly use an allocator parameter to delegate all new/delete operations? currently fiber is couple to boost::context::protected_stack again, thanks for your work on the context and fiber libraries Charles J. Quarra

Am 01.03.2011 16:45, schrieb Charlls Quarra:
context:
i noticed you migrate out of the context::create idiom and now context is noncopyable, so it seems right now the only way to have a recyclable context as a member is hold a pointer/ref and allocate on the heap. This might be good for most use cases (which is probably the boost design principles) but i think it leaves out cases where, due to performance constraints, one either wants to recycle a context without touching the heap.
Which leaves me to my other comment; could you add an template parameter for a allocator class so all new/delete operations are delegated to it?
you can not recycle the context - if it has finished its execution the instruction pointer has reached the last instruction of your function. you would have to recreate a new context and set the instruction pointer to the first instruction of you function (done by context impl). If you want to exchange context instance in your app you have to swap the pointers which point to context instances in the free-store. context * ctx1( new context(...)); context * ctx2( new context(...)); std::swap( ctx1, ctx2); In order to prevent allocations and deallocations of the stack you could move-out the stack from a context instance an pass it to a newly created one. context ctx1( ..., protected_stack(65536) ); ... protected_stack stk( ctx1.release_stack() ); ... context ctx2( ..., boost::move( stk) ); Allocators/deallocators are not used in favour of passing stack-objects to context-ctor. Oliver

--- El mar 1-mar-11, Oliver Kowalke <oliver.kowalke@gmx.de> escribió:
context:
i noticed you migrate out of the context::create idiom and now context is noncopyable, so it seems right now the only way to have a recyclable context as a member is hold a
Fecha: martes, 1 de marzo de 2011, 15:08 Am 01.03.2011 16:45, schrieb Charlls Quarra: pointer/ref and allocate on the heap. This might be good for most use cases (which is probably the boost design principles) but i think it leaves out cases where, due to performance constraints, one either wants to recycle a context without touching the heap.
Which leaves me to my other comment; could you add an
template parameter for a allocator class so all new/delete operations are delegated to it?
you can not recycle the context -
fair enough
In order to prevent allocations and deallocations of the stack you could move-out the stack from a context instance an pass it to a newly created one.
context ctx1( ..., protected_stack(65536) ); ... protected_stack stk( ctx1.release_stack() ); ... context ctx2( ..., boost::move( stk) );
Allocators/deallocators are not used in favour of passing stack-objects to context-ctor.
this is good, but there are other allocations happening inside both context and fiber that the user has no control over. This might be smaller in comparison to the stack but they are there nonetheless. It would be better to leave the user to choose the allocator for any heap requests happening in the library. Charles J. Quarra

Am 01.03.2011 20:20, schrieb Charlls Quarra:
this is good, but there are other allocations happening inside both context and fiber that the user has no control over. This might be smaller in comparison to the stack but they are there nonetheless.
It would be better to leave the user to choose the allocator for any heap requests happening in the library.
Charles J. Quarra
I don't get it - do you refer with 'other allocations' to the stack allocation? If yes you are free to implement your own stack and passing it as template arg to boost::context. Sorry - I'm not sure what you mean with 'choose the allocator for any heap requests happening in the library'?! regards, Oliver

--- El mar 1-mar-11, Oliver Kowalke <oliver.kowalke@gmx.de> escribió:
De: Oliver Kowalke <oliver.kowalke@gmx.de> Asunto: Re: [boost] [context] implementation comments Para: boost@lists.boost.org Fecha: martes, 1 de marzo de 2011, 15:44 Am 01.03.2011 20:20, schrieb Charlls Quarra:
this is good, but there are other allocations
happening inside both context and fiber that the user has no control over. This might be smaller in comparison to the stack but they are there nonetheless.
It would be better to leave the user to choose the
allocator for any heap requests happening in the library.
Charles J. Quarra
I don't get it - do you refer with 'other allocations' to the stack allocation? If yes you are free to implement your own stack and passing it as template arg to boost::context. Sorry - I'm not sure what you mean with 'choose the allocator for any heap requests happening in the library'?!
regards, Oliver
correction; there are no other allocations in context; only in fiber happens allocations other than the stack, for instance, boost/fiber/fiber.cpp at: 53 thanks, Charles

Am 01.03.2011 21:22, schrieb Charlls Quarra:
--- El mar 1-mar-11, Oliver Kowalke<oliver.kowalke@gmx.de> escribió:
De: Oliver Kowalke<oliver.kowalke@gmx.de> Asunto: Re: [boost] [context] implementation comments Para: boost@lists.boost.org Fecha: martes, 1 de marzo de 2011, 15:44 Am 01.03.2011 20:20, schrieb Charlls Quarra:
this is good, but there are other allocations happening inside both context and fiber that the user has no control over. This might be smaller in comparison to the stack but they are there nonetheless. It would be better to leave the user to choose the allocator for any heap requests happening in the library.
Charles J. Quarra
I don't get it - do you refer with 'other allocations' to the stack allocation? If yes you are free to implement your own stack and passing it as template arg to boost::context. Sorry - I'm not sure what you mean with 'choose the allocator for any heap requests happening in the library'?!
regards, Oliver
correction; there are no other allocations in context; only in fiber happens allocations other than the stack, for instance, boost/fiber/fiber.cpp at: 53
thanks, Charles
to which version you are referring to? the current implementation has asym_fiber.cpp and sym_fiber.cpp. Oliver

--- El mar 1-mar-11, Oliver Kowalke <oliver.kowalke@gmx.de> escribió:
to which version you are referring to? the current implementation has asym_fiber.cpp and sym_fiber.cpp.
Oliver
0.9.1 (latest on vault) boost/context/ actually has asym_fiber.hpp/fiber.hpp/sym_fiber.hpp also, both asym_fiber and sym_fiber have allocations; asym_fiber.hpp:51 return detail::asym_fiber_base::ptr( do_return ? new detail::asym_fiber_object< void(*)() >( fn, stacksize, detail::asym_fiber_base::do_return) : new detail::asym_fiber_object< void(*)() >( fn, stacksize, detail::asym_fiber_base::do_not_return) ); sym_fiber.hpp:51 return detail::sym_fiber_base::ptr( new detail::sym_fiber_object< void(*)() >( fn, stacksize) );

also, both asym_fiber and sym_fiber have allocations;
asym_fiber.hpp:51
return detail::asym_fiber_base::ptr( do_return ? new detail::asym_fiber_object< void(*)() >( fn, stacksize, detail::asym_fiber_base::do_return) : new detail::asym_fiber_object< void(*)() >( fn, stacksize, detail::asym_fiber_base::do_not_return) );
sym_fiber.hpp:51
return detail::sym_fiber_base::ptr( new detail::sym_fiber_object< void(*)() >( fn, stacksize) );
the allocation is necessary for: - support of efficient move semantics - in order to accept arbitrary functions with arbitrary arguements in the ctor of a fiber - the type asym_fiber and sym_fiber should not depend on the passed function (== the specific signature aof the function to be executed by the fiber doesn't influence the type of asym_fiber/sym_fiber) => you can store fiber objects which managing functions with different signature in the same container asym_fiber_object is a template storing the functor which will be executed by the fiber. asym_fiber_base is the interface asym_fiber_object dervies from. this pattern allows to hide the specific functor signature Oliver -- Schon gehört? GMX hat einen genialen Phishing-Filter in die Toolbar eingebaut! http://www.gmx.net/de/go/toolbar

--- El mié 2-mar-11, Oliver Kowalke <oliver.kowalke@gmx.de> escribió:
De: Oliver Kowalke <oliver.kowalke@gmx.de> Asunto: Re: [boost] [context] implementation comments Para: boost@lists.boost.org Fecha: miércoles, 2 de marzo de 2011, 2:52
also, both asym_fiber and sym_fiber have allocations;
asym_fiber.hpp:51
return detail::asym_fiber_base::ptr(
do_return
? new detail::asym_fiber_object< void(*)() >( fn, stacksize, detail::asym_fiber_base::do_return) : new detail::asym_fiber_object< void(*)() >( fn, stacksize, detail::asym_fiber_base::do_not_return) );
sym_fiber.hpp:51
return detail::sym_fiber_base::ptr(
new detail::sym_fiber_object< void(*)() >( fn, stacksize) );
the allocation is necessary for: - support of efficient move semantics - in order to accept arbitrary functions with arbitrary arguements in the ctor of a fiber - the type asym_fiber and sym_fiber should not depend on the passed function (== the specific signature aof the function to be executed by the fiber doesn't influence the type of asym_fiber/sym_fiber) => you can store fiber objects which managing functions with different signature in the same container
asym_fiber_object is a template storing the functor which will be executed by the fiber. asym_fiber_base is the interface asym_fiber_object dervies from. this pattern allows to hide the specific functor signature
Oliver
Thats fine ;-), i'm not discussing the reason of having heap allocations in there; all i'm saying is that there should be the option for the user to pass a custom allocator type (as a templater parameter, as is customary for std:: libs) that these (any) allocations can be delegated to Charles

Charlls Quarra wrote:
--- El mié 2-mar-11, Oliver Kowalke escribió:
De: Oliver Kowalke Asunto: Re: [boost] [context] implementation comments Para: boost@lists.boost.org Fecha: miércoles, 2 de marzo de 2011, 2:52
also, both asym_fiber and sym_fiber have allocations;
asym_fiber.hpp:51
return detail::asym_fiber_base::ptr(
do_return
? new detail::asym_fiber_object< void(*)() >( fn, stacksize, detail::asym_fiber_base::do_return) : new detail::asym_fiber_object< void(*)() >( fn, stacksize, detail::asym_fiber_base::do_not_return) );
sym_fiber.hpp:51
return detail::sym_fiber_base::ptr(
new detail::sym_fiber_object< void(*)() >( fn, stacksize) );
the allocation is necessary for: - support of efficient move semantics - in order to accept arbitrary functions with arbitrary arguements in the ctor of a fiber - the type asym_fiber and sym_fiber should not depend on the passed function (== the specific signature aof the function to be executed by the fiber doesn't influence the type of asym_fiber/sym_fiber) => you can store fiber objects which managing functions with different signature in the same container
asym_fiber_object is a template storing the functor which will be executed by the fiber. asym_fiber_base is the interface asym_fiber_object dervies from. this pattern allows to hide the specific functor signature
Oliver
Thats fine ;-), i'm not discussing the reason of having heap allocations in there; all i'm saying is that there should be the option for the user to pass a custom allocator type (as a templater parameter, as is customary for std:: libs) that these (any) allocations can be delegated to
The problem adding the Allocator as a template parameters is that it change the type. Note that Boost.Fiber follows here the design of Boost.Thread, which is on a std lib (C++0x) Best, Vicente -- View this message in context: http://boost.2283326.n4.nabble.com/context-fiber-implementation-comments-tp3... Sent from the Boost - Dev mailing list archive at Nabble.com.

--- El mié 2-mar-11, Vicente Botet <vicente.botet@wanadoo.fr> escribió:
De: Vicente Botet <vicente.botet@wanadoo.fr> Asunto: Re: [boost] [context] implementation comments Para: boost@lists.boost.org Fecha: miércoles, 2 de marzo de 2011, 13:28
Charlls Quarra wrote:
--- El mié 2-mar-11, Oliver Kowalke escribió:
De: Oliver Kowalke Asunto: Re: [boost] [context] implementation void(*)() >( fn, stacksize) );
the allocation is necessary for: - support of efficient move semantics - in order to accept arbitrary functions with
arguements in the ctor of a fiber - the type asym_fiber and sym_fiber should not depend on the passed function (== the specific signature aof
function to be executed by the fiber doesn't influence the type of asym_fiber/sym_fiber) => you can store fiber objects which managing functions with different signature in the same container
asym_fiber_object is a template storing the functor which will be executed by the fiber. asym_fiber_base is the interface asym_fiber_object dervies from. this pattern allows to hide the specific functor signature
Oliver
Thats fine ;-), i'm not discussing the reason of having heap allocations in there; all i'm saying is that there should be the
to pass a custom allocator type (as a templater
arbitrary the option for the user parameter, as is customary
for std:: libs) that these (any) allocations can be delegated to
The problem adding the Allocator as a template parameters is that it change the type. Note that Boost.Fiber follows here the design of Boost.Thread, which is on a std lib (C++0x)
Best, Vicente
what if it changes the type? i understand the point of making the type independent of the signature of the invoked handler, but i don't see why it needs to be extended to any sort of type parameter dependence. So it seems that boost.thread doesn't provide a allocator parameter and it sounds like a limitation in it (unless i'm missing some point here). consistency alone shouldn't be a good reason if it means constraining usability without sound reasons Charles

Am 02.03.2011 18:43, schrieb Charlls Quarra:
--- El mié 2-mar-11, Vicente Botet<vicente.botet@wanadoo.fr> escribió:
De: Vicente Botet<vicente.botet@wanadoo.fr> Asunto: Re: [boost] [context] implementation comments Para: boost@lists.boost.org Fecha: miércoles, 2 de marzo de 2011, 13:28
--- El mié 2-mar-11, Oliver Kowalke escribió:
De: Oliver Kowalke Asunto: Re: [boost] [context] implementation void(*)()>( fn, stacksize) );
the allocation is necessary for: - support of efficient move semantics - in order to accept arbitrary functions with arbitrary arguements in the ctor of a fiber - the type asym_fiber and sym_fiber should not depend on the passed function (== the specific signature aof
function to be executed by the fiber doesn't influence the type of asym_fiber/sym_fiber) => you can store fiber objects which managing functions with different signature in the same container
asym_fiber_object is a template storing the functor which will be executed by the fiber. asym_fiber_base is the interface asym_fiber_object dervies from. this pattern allows to hide the specific functor signature Oliver
Thats fine ;-), i'm not discussing the reason of having heap allocations in there; all i'm saying is that there should be the
to pass a custom allocator type (as a templater
Charlls Quarra wrote: the option for the user parameter, as is customary
for std:: libs) that these (any) allocations can be delegated to
The problem adding the Allocator as a template parameters is that it change the type. Note that Boost.Fiber follows here the design of Boost.Thread, which is on a std lib (C++0x)
Best, Vicente
what if it changes the type? i understand the point of making the type independent of the signature of the invoked handler, but i don't see why it needs to be extended to any sort of type parameter dependence.
So it seems that boost.thread doesn't provide a allocator parameter and it sounds like a limitation in it (unless i'm missing some point here). consistency alone shouldn't be a good reason if it means constraining usability without sound reasons
Charles
What you are referring to is a internal data-structure (namespace detail) and not intended to be used outside. Many of the stl container don't use their allocators - for instance std::list allocates its internal data structures with new (allocator can not applied) - therefore I believe it would not make sense to parametrize fiber with an allocator. Oliver

On 04.03.2011, at 19:16, Oliver Kowalke wrote:
Many of the stl container don't use their allocators - for instance std::list allocates its internal data structures with new (allocator can not applied) - therefore I believe it would not make sense to parametrize fiber with an allocator.
I don't know what std::list implementation you are referring to, but it's buggy. A standard container has to use its allocator for *all* allocations. That's what rebind is for. Sebastian

Am 04.03.2011 20:06, schrieb Sebastian Redl:
On 04.03.2011, at 19:16, Oliver Kowalke wrote:
Many of the stl container don't use their allocators - for instance std::list allocates its internal data structures with new (allocator can not applied) - therefore I believe it would not make sense to parametrize fiber with an allocator. I don't know what std::list implementation you are referring to, but it's buggy. A standard container has to use its allocator for *all* allocations. That's what rebind is for.
stl::set of gcc-4.4.5 does not use its allocator C++ standard 23.2.1: '... are called only for the container’s element type, not for internal types used by the container. [ Note: This means, for example, that a node-based container might need to construct nodes containing aligned buffers and call construct to place the element into the buffer. — end note ]...'

On 04.03.2011, at 21:18, Oliver Kowalke wrote:
Am 04.03.2011 20:06, schrieb Sebastian Redl:
On 04.03.2011, at 19:16, Oliver Kowalke wrote:
Many of the stl container don't use their allocators - for instance std::list allocates its internal data structures with new (allocator can not applied) - therefore I believe it would not make sense to parametrize fiber with an allocator. I don't know what std::list implementation you are referring to, but it's buggy. A standard container has to use its allocator for *all* allocations. That's what rebind is for.
stl::set of gcc-4.4.5 does not use its allocator
C++ standard 23.2.1: '... are called only for the container’s element type, not for internal types used by the container. [ Note: This means, for example, that a node-based container might need to construct nodes containing aligned buffers and call construct to place the element into the buffer. — end note ]...'
That clause only applies to construct() and destruct(), not to memory allocations. Here's the relevant citation, from C++0x 23.2.1p8: "Unless otherwise specified, all containers defined in this clause obtain memory using an allocator. [...] A copy of this allocator is used for any memory allocation performed, by these constructors and by all member functions, during the lifetime of each container object or until the allocator is replaced." If GCC's set uses plain new anywhere, it's broken. If it uses placement new to construct its internal structures, that's fine. Sebastian

On 04.03.2011, at 21:18, Oliver Kowalke wrote:
Am 04.03.2011 20:06, schrieb Sebastian Redl:
On 04.03.2011, at 19:16, Oliver Kowalke wrote:
Many of the stl container don't use their allocators - for instance std::list allocates its internal data structures with new (allocator can not applied) - therefore I believe it would not make sense to parametrize fiber with an allocator. I don't know what std::list implementation you are referring to, but it's buggy. A standard container has to use its allocator for *all* allocations. That's what rebind is for.
stl::set of gcc-4.4.5 does not use its allocator
C++ standard 23.2.1: '... are called only for the container’s element type, not for internal types used by the container. [ Note: This means, for example, that a node-based container might need to construct nodes containing aligned buffers and call construct to place the element into the buffer. — end note ]...' That clause only applies to construct() and destruct(), not to memory allocations. Here's the relevant citation, from C++0x 23.2.1p8: "Unless otherwise specified, all containers defined in this clause obtain memory using an allocator. [...] A copy of this allocator is used for any memory allocation performed, by these constructors and by all member functions, during the lifetime of each container object or until the allocator is replaced."
If GCC's set uses plain new anywhere, it's broken. If it uses placement new to construct its internal structures, that's fine.
Sebastian
Am 05.03.2011 12:00, schrieb Sebastian Redl: then Scott Meyers is completely wrong? 23.2.1 - 8 deals with swap() operation in my C++ standard document anyway I don't see relevant arguments that asym_fiber/sym_fiber should add an allocator to the interface. what about boost::thread and other constructs from the boost library?

On 04.03.2011, at 21:18, Oliver Kowalke wrote:
Am 04.03.2011 20:06, schrieb Sebastian Redl:
On 04.03.2011, at 19:16, Oliver Kowalke wrote:
Many of the stl container don't use their allocators - for instance std::list allocates its internal data structures with new (allocator can not applied) - therefore I believe it would not make sense to parametrize fiber with an allocator. I don't know what std::list implementation you are referring to, but it's buggy. A standard container has to use its allocator for *all* allocations. That's what rebind is for.
stl::set of gcc-4.4.5 does not use its allocator
C++ standard 23.2.1: '... are called only for the container’s element type, not for internal types used by the container. [ Note: This means, for example, that a node-based container might need to construct nodes containing aligned buffers and call construct to place the element into the buffer. — end note ]...' That clause only applies to construct() and destruct(), not to memory allocations. Here's the relevant citation, from C++0x 23.2.1p8: "Unless otherwise specified, all containers defined in this clause obtain memory using an allocator. [...] A copy of this allocator is used for any memory allocation performed, by these constructors and by all member functions, during the lifetime of each container object or until the allocator is replaced."
If GCC's set uses plain new anywhere, it's broken. If it uses placement new to construct its internal structures, that's fine.
Sebastian
Am 05.03.2011 12:00, schrieb Sebastian Redl: then Scott Meyers is completely wrong? I don't know. What did he say?
23.2.1 - 8 deals with swap() operation in my C++ standard document Try 23.1p8 in the C++03 standard.
anyway I don't see relevant arguments that asym_fiber/sym_fiber should add an allocator to the interface. what about boost::thread and other constructs from the boost library? I wasn't making that argument. I was just saying that an allocator, if
On 05.03.2011 15:34, Oliver Kowalke wrote: there was one, would be used even for internal data. Sebastian

then Scott Meyers is completely wrong? I don't know. What did he say?
'Effective STL' - page 52: '... final curiosity of STL allocatos, that most of the standard containers never make a single call to the allocators ... true for all the standard associative containers....' Oliver -- Empfehlen Sie GMX DSL Ihren Freunden und Bekannten und wir belohnen Sie mit bis zu 50,- Euro! https://freundschaftswerbung.gmx.de

then Scott Meyers is completely wrong? I don't know. What did he say? 'Effective STL' - page 52: '... final curiosity of STL allocatos, that most of the standard containers never make a single call to the allocators ... true for all the standard associative containers....' Effective STL is old, and many of the caveats Scott mentions in Item 10 no longer apply to implementations that conform to the C++0x draft. For example, implementations are no longer allowed to assume that allocator<T>::pointer is T*. They are no longer allowed to assume that any two instances of allocator<T> compare equal, or otherwise assume
On 07.03.2011 12:19, Oliver Kowalke wrote: that allocator state is irrelevant (and thus not keep a copy). Besides, your quote is incomplete. The full relevant quote is "that most of the standard containers never make a single call to the allocators *with which they were instantiated*" (emphasis mine), which just means that a std::list<int, allocator<int>> doesn't ever call allocator<int>::allocate. Instead, it calls allocator<_ListNode<int>>::allocate. But the correct C++0x form for such an allocation looks roughly like this: template <typename _T, typename _Alloc = std::allocator<_T>> class list { typedef typename _Alloc::template rebind<_ListNode<T>>::other _NodeAlloc; typedef typename _NodeAlloc::pointer _NodePtr; _Alloc _M_alloc; _NodePtr __alloc_node() { // Create an allocator for this allocation. Not 100% sure that the base allocator is passed, but pretty sure. _NodeAlloc __node_alloc(_M_alloc); _NodePtr mem = __node_alloc.allocate(1); new (mem) _ListNode<T>(); return mem; } }; It actually may look a bit different, since I haven't fully understood the scoped allocator model yet. But that's the gist of it. The memory comes from the allocator template you pass. It's just the concrete instantiation that may be different. Now, there may still be various implementations around that don't actually implement this correctly. But the standard in C++0x is quite clear. Sebastian

On 3/2/2011 9:28 AM, Vicente Botet wrote:
Charlls Quarra wrote:
[...]
Thats fine ;-), i'm not discussing the reason of having heap allocations in there; all i'm saying is that there should be the option for the user to pass a custom allocator type (as a templater parameter, as is customary for std:: libs) that these (any) allocations can be delegated to
The problem adding the Allocator as a template parameters is that it change the type. Note that Boost.Fiber follows here the design of Boost.Thread, which is on a std lib (C++0x)
Could the allocator be embedded into the implementation object and accessed through virtual functions? I *think* the specific type of the allocator would only show up in the fiber interface in the (template) constructor overload selected at construction, e.g., template< class Allocator > fiber::fiber(Allocator alloc)... I'm not promising that this would work, but, in the event that it could, I wouldn't expect a fiber object to do too many allocations (unlike, say, a std container), so this shouldn't incur a significant overhead... I only mention this as a compromise if adding an Allocator template parameter is d.o.a. - Jeff

Am 01.03.2011 16:45, schrieb Charlls Quarra:
this applies here too; can you make the fiber take template parameters for the stack type and possibly use an allocator parameter to delegate all new/delete operations? currently fiber is couple to boost::context::protected_stack
Hello Charlls, the use of boost::protected_stack inside boost::fiber is intended because if the stack is exceeded and context writes to memory not belonging to the stack then in best case you get an segmentation fault/access violation (if the memory does not belong to the process) or you write to memory of your application which result is unexpected behaviour of your app. Therefore protected_stack is used generating alsways and segmentation fault if sthe stack is exceeded. If you have other requirements you could use boost.context to create your own abstraction - that's the reason why I've move the context abstraction in a separate library. Oliver
participants (5)
-
Charlls Quarra
-
Jeffrey Lee Hellrung, Jr.
-
Oliver Kowalke
-
Sebastian Redl
-
Vicente Botet