Boost logo

Boost :

Subject: Re: [boost] Looking for thoughts on a new smart pointer: shared_ptr_nonnull
From: Daniel James (daniel_at_[hidden])
Date: 2013-10-08 14:38:29


On 8 October 2013 02:09, Rob Stewart <robertstewart_at_[hidden]> wrote:
> On Oct 7, 2013, at 5:39 PM, Daniel James <daniel_at_[hidden]> wrote:
>
>> On 6 October 2013 12:04, Rob Stewart <robertstewart_at_[hidden]> wrote:
>>>
>>> If the exception is triggered by an unknown condition, such as you're advocating, then there can be no handler for it. It must, of necessity, unwind to the top. Code cannot rightly continue as if there had been no exception. In that case, there's little gained over an assertion, save for the possibility that other threads may be able to complete independent work.
>>
>> later:
>>
>>> The likely outcome of failure in this regard, in a non-debug build, is a null pointer dereference elsewhere. Unchecked, that means a segfault. IOW, the problem will be found.
>>
>> So you're saying that when it's an exception, it's triggered by an
>> "unknown condition" not by a null, which is surely a known condition.
>
> No

"No, there's an unknown condition", or "no, that's not what I'm saying"?

>> And you're claiming that after an exception the code will continue as if there was no exception.
>
> No, Pete, I think it was, advocated letting thousands of tasks continue while a few failed.

How is that continuing as if there was no exception? There are
strategies for recovering from such exceptions, a simple one is the
write the code in question in a pure functional style.

> If a null pointer occurs somewhere in your code and this class' ctor throws an exception, the handler has no idea of the source of the null pointer. Thus, the you have an unknown condition as far as the handler is concerned. (I had been discussing a handler in main() or a thread procedure, as opposed to one very near the object mis-construction.)

If you're doing individual tasks you know which one was associated
with the throw. For many cases that's all you need to know. This does
depend on the program, which is why the decision to catch logic errors
should be up to the programmer, who knows it better than we do. You
don't have to catch the exceptions if you don't want to.

> OTOH, if a null is actual stored in the object, then it will eventually be dereferenced, in the general case anyway, and the segfault will indicate which instance was bad. From that you should be able to discover its genesis, at least if the object's address is consistent or the pointee's type is unique. That makes finding the source of the null pointer somewhat more tractable without a debugger.

You'd get better data if the failure was in the constructor. It's more
likely that you could associate it with the source of the pointer,
which might have been destructed by the time the pointer is
dereferenced. (Remember that my main argument is that there should
always be a check).

And, of course, you don't always get the segfault data, you sometimes
don't even get notified that there is a bug. You're making the
assumption that after a bug is discovered, you'll be quickly notified
and nothing bad will happen in the meantime. Unfortunately, that isn't
true at all.

> Consider the case of taking the address of a static object.

I hope you're using a do-nothing custom deleter.

> There's no need to check for null, so there's no need to risk pipeline stalling on a failed branch prediction or cache invalidation due to the exception construction and throwing code you'd add to the ctor otherwise.

Well, there's premature optimization for you.

This is a class that's concerned with safety, not efficiency. If
there's a safe option and an unsafe option, then the safe option
should be the default, and the unsafe option should be the verbose
one.

Considering your concerns, why are repeatedly creating shared pointers
from a static object? This would have to be a very tight loop if a
null check on a value that you have to access anyway is going to make
a difference. In which case you might as well create one non-null
shared pointer and reuse that, as it will be faster.

But if you can't do that, what will this check really cost? The branch
prediction should be for the non-null case, if you're concerned about
that you can add a hint in some compilers. I'd actually hope that the
compiler would optimise the check away in many cases, especially when
initialising from a static object. And the exception creation code
should be separate the normal code anyway, so it shouldn't stress the
cache too much.

Then you've got the cost of reserving memory for that shared pointer's
count, that's expensive. You might try to avoid that by using
enable_shared_from_this, but then you've got the weak pointer check.
You're also no longer thread safe and have to have a shared_ptr
somewhere, so there's little argument against using a static non-null
shared pointer which will avoid both the weak pointer check and the
null check, which is surely preferable since you're so concerned about
performance.

But whatever you do creating this shared pointer is going to require
memory manipulation on the heap - that's not free either.

But really, the only way to tell is to try it out in a real program.


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk