[Block Pointer] Review Request

Hi, I made the requested corrections to the Block Pointer, I've added automated tests and its corresponding Jamfile. The tests can be found here: https://svn.boost.org/svn/boost/sandbox/block_ptr/libs/smart_ptr/test/block_... https://svn.boost.org/svn/boost/sandbox/block_ptr/libs/smart_ptr/test/block_... Once again the documentation can be found here: https://svn.boost.org/svn/boost/sandbox/block_ptr/libs/smart_ptr/doc/tutoria... Now I'm pretty sure I haven't forgotten anything and I am requesting a review. Thanks, -Phil

On 01/05/2011 19:44, Phil Bouchard wrote:
Hi,
I made the requested corrections to the Block Pointer, I've added automated tests and its corresponding Jamfile. The tests can be found here: https://svn.boost.org/svn/boost/sandbox/block_ptr/libs/smart_ptr/test/block_...
https://svn.boost.org/svn/boost/sandbox/block_ptr/libs/smart_ptr/test/block_...
Once again the documentation can be found here: https://svn.boost.org/svn/boost/sandbox/block_ptr/libs/smart_ptr/doc/tutoria...
Now I'm pretty sure I haven't forgotten anything and I am requesting a review.
The documentation doesn't seem to address the problems cycles cause with destructors, could you maybe write a bit about that? In C++/CLI, types that can be garbage collected and types that can't have separate structures for good reasons.

On 5/1/2011 10:52 AM, Mathias Gaunard wrote:
The documentation doesn't seem to address the problems cycles cause with destructors, could you maybe write a bit about that?
No problem. I just added a noted to the cyclic section: https://svn.boost.org/svn/boost/sandbox/block_ptr/libs/smart_ptr/doc/rationa...
In C++/CLI, types that can be garbage collected and types that can't have separate structures for good reasons.
I think garbage collected types need non-deterministic finalizers. -Phil

On 5/1/2011 11:39 AM, Phil Bouchard wrote:
No problem. I just added a noted to the cyclic section: https://svn.boost.org/svn/boost/sandbox/block_ptr/libs/smart_ptr/doc/rationa...
What say you? -Phil

On 01/05/2011 20:39, Phil Bouchard wrote:
On 5/1/2011 10:52 AM, Mathias Gaunard wrote:
The documentation doesn't seem to address the problems cycles cause with destructors, could you maybe write a bit about that?
No problem. I just added a noted to the cyclic section: https://svn.boost.org/svn/boost/sandbox/block_ptr/libs/smart_ptr/doc/rationa...
This is not what I meant. When you have a cycle, the order in which destructors are called cannot be specified. This makes it impossible to guarantee that the object held by a block_ptr won't have been destructed in the destructor of a class that has a block_ptr as a member. Meaning many uses of block_ptr could easily turn out to be invalid, and that not everything is suitable for use with block_ptr.

On 5/1/2011 3:05 PM, Mathias Gaunard wrote:
This is not what I meant. When you have a cycle, the order in which destructors are called cannot be specified.
Wrong because block_ptr knows in which order the memory blocks were instantiated, just as in a FIFO.
This makes it impossible to guarantee that the object held by a block_ptr won't have been destructed in the destructor of a class that has a block_ptr as a member.
I am not sure if I understand correctly what you are saying but block_ptr members to other classes will be invalidated if they point to an object that is currently being destructed. You can see an example in the 3rd test of the following file: https://svn.boost.org/svn/boost/sandbox/block_ptr/libs/smart_ptr/test/block_... -Phil

On 02/05/2011 02:20, Phil Bouchard wrote:
On 5/1/2011 3:05 PM, Mathias Gaunard wrote:
This is not what I meant. When you have a cycle, the order in which destructors are called cannot be specified.
Wrong because block_ptr knows in which order the memory blocks were instantiated, just as in a FIFO.
Order of instantiation is irrelevant outside of RAII management patterns, since an object may outlive its creator (that's the point).
I am not sure if I understand correctly what you are saying but block_ptr members to other classes will be invalidated if they point to an object that is currently being destructed. You can see an example in the 3rd test of the following file: https://svn.boost.org/svn/boost/sandbox/block_ptr/libs/smart_ptr/test/block_...
What do you mean by the block_ptr will be invalidated? The pointer will be set to zero? So that's something any user should be ready for, check block pointers are non-zero in destructors?

On 5/2/2011 10:09 AM, Mathias Gaunard wrote:
Order of instantiation is irrelevant outside of RAII management patterns, since an object may outlive its creator (that's the point).
If block A was instantiated first then block A is necessarily the parent of block B.
What do you mean by the block_ptr will be invalidated? The pointer will be set to zero?
Precisely.
So that's something any user should be ready for, check block pointers are non-zero in destructors?
It's already done implicitly in the destruction of the block_ptrs. Right before they'll be destroyed the pointer will still refer to the deallocated memory block. -Phil

On 02/05/2011 21:32, Phil Bouchard wrote:
On 5/2/2011 10:09 AM, Mathias Gaunard wrote:
Order of instantiation is irrelevant outside of RAII management patterns, since an object may outlive its creator (that's the point).
If block A was instantiated first then block A is necessarily the parent of block B.
What do you mean by the block_ptr will be invalidated? The pointer will be set to zero?
Precisely.
So that's something any user should be ready for, check block pointers are non-zero in destructors?
It's already done implicitly in the destruction of the block_ptrs. Right before they'll be destroyed the pointer will still refer to the deallocated memory block.
Right, but the user could still try do do some_block_ptr->some_member_function() if some_block_ptr is null, this will lead to an error. You should talk about this in the documentation.

On 5/2/2011 2:35 PM, Mathias Gaunard wrote:
Right, but the user could still try do do some_block_ptr->some_member_function()
if some_block_ptr is null, this will lead to an error.
You should talk about this in the documentation.
Like we were pointing out with the conversation with Emil, this will only occur in the destructor. I am adding as we speak a function that can be called explicitly to prevent this from happening. -Phil

On 5/2/2011 5:49 PM, Phil Bouchard wrote:
Like we were pointing out with the conversation with Emil, this will only occur in the destructor. I am adding as we speak a function that can be called explicitly to prevent this from happening.
I just added it to the advanced section: https://svn.boost.org/svn/boost/sandbox/block_ptr/libs/smart_ptr/doc/tutoria... -Phil

On 03/05/2011 04:16, Phil Bouchard wrote:
On 5/2/2011 5:49 PM, Phil Bouchard wrote:
Like we were pointing out with the conversation with Emil, this will only occur in the destructor. I am adding as we speak a function that can be called explicitly to prevent this from happening.
I just added it to the advanced section: https://svn.boost.org/svn/boost/sandbox/block_ptr/libs/smart_ptr/doc/tutoria...
Couldn't the test just be !p ?

On 03/05/2011 04:16, Phil Bouchard wrote:
On 5/2/2011 5:49 PM, Phil Bouchard wrote:
Like we were pointing out with the conversation with Emil, this will only occur in the destructor. I am adding as we speak a function that can be called explicitly to prevent this from happening.
I just added it to the advanced section: https://svn.boost.org/svn/boost/sandbox/block_ptr/libs/smart_ptr/doc/tutoria...
or if(p) rather sorry.

On 5/3/2011 7:47 AM, Phil Bouchard wrote:
On 5/3/2011 2:43 AM, Mathias Gaunard wrote:
or if(p) rather sorry.
I imagine this is more convenient, yes. I'll make the change in a bit.
The problem is this only happens in the destructor and operator bool is usually called many times outside of the destructor. If I check for cyclicism inside operator bool then this would create unnecessary overhead for the calls outside of the destructor. -Phil

On Tue, May 03, 2011 at 12:28:59PM -0700, Phil Bouchard wrote:
On 5/3/2011 7:47 AM, Phil Bouchard wrote:
On 5/3/2011 2:43 AM, Mathias Gaunard wrote:
or if(p) rather sorry.
I imagine this is more convenient, yes. I'll make the change in a bit.
The problem is this only happens in the destructor and operator bool is usually called many times outside of the destructor. If I check for cyclicism inside operator bool then this would create unnecessary overhead for the calls outside of the destructor.
I would be careful about calling it `operator bool' as it really is (as far as I can see from the source) an `operator unspecified-bool-type'. Your previous use of the term forced me to check the source to make sure that you hadn't fallen into the trap of accidentally converting to bool instead of something bool-like. -- Lars Viklund | zao@acc.umu.se

On 05/03/2011 12:28 PM, Phil Bouchard wrote:
On 5/3/2011 7:47 AM, Phil Bouchard wrote:
On 5/3/2011 2:43 AM, Mathias Gaunard wrote:
or if(p) rather sorry.
I imagine this is more convenient, yes. I'll make the change in a bit.
The problem is this only happens in the destructor and operator bool is usually called many times outside of the destructor. If I check for cyclicism inside operator bool then this would create unnecessary overhead for the calls outside of the destructor.
When you detect a cycle to be destroyed, would it be possible to zero all of the relevant pointers before invoking any destructors?

On 5/1/2011 10:52 AM, Mathias Gaunard wrote:
In C++/CLI, types that can be garbage collected and types that can't have separate structures for good reasons.
I don't expect much support from the garbage collection community... it's like telling astrophysicists there is no need for dark matter after proving it ;) http://www.wbabin.net/files/4291_fr-wbabin.pdf -Phil

On Sun, May 1, 2011 at 10:44 AM, Phil Bouchard <philippe@fornux.com> wrote:
Hi,
I made the requested corrections to the Block Pointer, I've added automated tests and its corresponding Jamfile. The tests can be found here: https://svn.boost.org/svn/boost/sandbox/block_ptr/libs/smart_ptr/test/block_... https://svn.boost.org/svn/boost/sandbox/block_ptr/libs/smart_ptr/test/block_...
Once again the documentation can be found here: https://svn.boost.org/svn/boost/sandbox/block_ptr/libs/smart_ptr/doc/tutoria...
Now I'm pretty sure I haven't forgotten anything and I am requesting a review.
If I understand correctly, the point of block_ptr is that it allows for cyclic references, since if shared_ptr is used instead, the objects that are part of a cycle will keep each other afloat. In my mind, the point of using a smart pointer is that the pointer is dereferenced, the pointee is guaranteed to be afloat. So, the limitations of shared_ptr in terms of not supporting cyclic references aren't arbitrary; they stem from this same guarantee. If object A points object B, and object B points object A, destroying either of them will leave a dangling pointer in the other. This is true regardless of whether we're using raw pointers or smart pointers. The way to deal with this situation using shared_ptr is to use weak_ptr to break the cycles. I suppose block_ptr solves this problem more elegantly, but how? Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode

On 5/1/2011 6:01 PM, Emil Dotchevski wrote:
The way to deal with this situation using shared_ptr is to use weak_ptr to break the cycles. I suppose block_ptr solves this problem more elegantly, but how?
The way it is being handled is explained here: https://svn.boost.org/svn/boost/sandbox/block_ptr/libs/smart_ptr/doc/rationa... If blocks of memory are found to still exist after a standard reference counter destruction and that the pointer is living on the stack or data segment then each memory blocks will be destructed and deallocated in the order they were constructed (FIFO). Everything is being handled implicitly. -Phil

On Sun, May 1, 2011 at 6:16 PM, Phil Bouchard <philippe@fornux.com> wrote:
On 5/1/2011 6:01 PM, Emil Dotchevski wrote:
The way to deal with this situation using shared_ptr is to use weak_ptr to break the cycles. I suppose block_ptr solves this problem more elegantly, but how?
The way it is being handled is explained here: https://svn.boost.org/svn/boost/sandbox/block_ptr/libs/smart_ptr/doc/rationa...
If blocks of memory are found to still exist after a standard reference counter destruction and that the pointer is living on the stack or data segment then each memory blocks will be destructed and deallocated in the order they were constructed (FIFO). Everything is being handled implicitly.
My point is that regardless of the order of destruction, if you have a cycle, necessarily you will end up with some dangling pointers (smart or otherwise). How does block_ptr protect against bugs arising from that? Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode

On 5/1/2011 6:39 PM, Emil Dotchevski wrote:
My point is that regardless of the order of destruction, if you have a cycle, necessarily you will end up with some dangling pointers (smart or otherwise). How does block_ptr protect against bugs arising from that?
When memory blocks are found to still exist after standard reference counting destruction then the remaining memory blocks are necessarily cyclic. Each block part of the cyclic set will then be destroyed one at a time but if any other block_ptr are found to reference a block within the same set then the destruction of the block_ptr in question will be invalidated. This is how an infinite destruction of a cyclic set is being prevented. (A set is a group of memory blocks). -Phil

On Sun, May 1, 2011 at 6:52 PM, Phil Bouchard <philippe@fornux.com> wrote:
On 5/1/2011 6:39 PM, Emil Dotchevski wrote:
My point is that regardless of the order of destruction, if you have a cycle, necessarily you will end up with some dangling pointers (smart or otherwise). How does block_ptr protect against bugs arising from that?
When memory blocks are found to still exist after standard reference counting destruction then the remaining memory blocks are necessarily cyclic. Each block part of the cyclic set will then be destroyed one at a time but if any other block_ptr are found to reference a block within the same set then the destruction of the block_ptr in question will be invalidated. This is how an infinite destruction of a cyclic set is being prevented.
I'm not concerned with how cycles are being broken. My question is, is there anything that protects all objects within a cycle from being able to dereference a pointer to an object from the cycle after that object has already been destroyed? Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode

On 5/1/2011 7:02 PM, Emil Dotchevski wrote:
I'm not concerned with how cycles are being broken.
My question is, is there anything that protects all objects within a cycle from being able to dereference a pointer to an object from the cycle after that object has already been destroyed?
A block_ptr pointing to a cyclic object that has been destroyed will be nullified. It won't be dereferenced. -Phil

On Sun, May 1, 2011 at 7:48 PM, Phil Bouchard <philippe@fornux.com> wrote:
On 5/1/2011 7:02 PM, Emil Dotchevski wrote:
I'm not concerned with how cycles are being broken.
My question is, is there anything that protects all objects within a cycle from being able to dereference a pointer to an object from the cycle after that object has already been destroyed?
A block_ptr pointing to a cyclic object that has been destroyed will be nullified. It won't be dereferenced.
Yes but how do I know not to dereference it? Dereferencing a null pointer is still undefined behavior. Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode

On 5/2/2011 10:54 AM, Emil Dotchevski wrote:
Yes but how do I know not to dereference it? Dereferencing a null pointer is still undefined behavior.
I was referring to the implicit destruction mechanism; the block_ptr will not be implicitly dereferenced if its pointee is already destroyed. If the pointer is explicitly dereferenced in the destructor then this will cause undefined behavior. -Phil

On Mon, May 2, 2011 at 12:19 PM, Phil Bouchard <philippe@fornux.com> wrote:
On 5/2/2011 10:54 AM, Emil Dotchevski wrote:
Yes but how do I know not to dereference it? Dereferencing a null pointer is still undefined behavior.
I was referring to the implicit destruction mechanism; the block_ptr will not be implicitly dereferenced if its pointee is already destroyed.
If the pointer is explicitly dereferenced in the destructor then this will cause undefined behavior.
Then, what is the benefit of block_ptr compared to using raw pointers as cyclic references? Maybe I'm missing something but aren't they equally (un)safe? Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode

On 5/2/2011 12:49 PM, Emil Dotchevski wrote:
Then, what is the benefit of block_ptr compared to using raw pointers as cyclic references? Maybe I'm missing something but aren't they equally (un)safe?
I haven't considered this scenario when block_ptr are explicitly dereferenced in the destructor. Maybe I can add a function that can be called explicitly to check whether the pointee is cyclic and has already been destroyed. But thanks, -Phil

On Mon, May 2, 2011 at 1:02 PM, Phil Bouchard <philippe@fornux.com> wrote:
On 5/2/2011 12:49 PM, Emil Dotchevski wrote:
Then, what is the benefit of block_ptr compared to using raw pointers as cyclic references? Maybe I'm missing something but aren't they equally (un)safe?
I haven't considered this scenario when block_ptr are explicitly dereferenced in the destructor. Maybe I can add a function that can be called explicitly to check whether the pointee is cyclic and has already been destroyed.
Keep in mind multi-threading issues too. The way the shared_ptr framework deals with this is that weak_ptr::lock() returns a shared_ptr, so if the object hasn't yet expired at the time lock() is called, it can't expire later (since the returned shared_ptr keeps it afloat.) This is what makes using weak_ptr for cyclic references safe. Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode

On 5/2/2011 1:08 PM, Emil Dotchevski wrote:
Keep in mind multi-threading issues too. The way the shared_ptr framework deals with this is that weak_ptr::lock() returns a shared_ptr, so if the object hasn't yet expired at the time lock() is called, it can't expire later (since the returned shared_ptr keeps it afloat.) This is what makes using weak_ptr for cyclic references safe.
Thanks for the notice. Right now block_ptr locks a mutex when a cyclic set is being destroyed. -Phil
participants (5)
-
Emil Dotchevski
-
Jeremy Maitin-Shepard
-
Lars Viklund
-
Mathias Gaunard
-
Phil Bouchard