C++ Logo

std-proposals

Advanced search

[std-proposals] A non-owning but self zeroing smart pointer for single ownership

From: JOHN MORRISON <inglesflamenco_at_[hidden]>
Date: Mon, 17 Feb 2025 17:26:28 +0000
Responding to Tiago Freire
Hi Tiago,

You wrote:
I don’t think it needs to compromise on single-ownership.
I can see 2 solutions for this:
1. Give up on thread safety. The owning pointer only takes the responsibility to notify any weak pointer constructed from it. It works, just not in a multithreaded setup.
2. Locking the destructor of an owning pointer if a weak pointer acquires some sort of lock. Can lead to a dead lock. Would definitely be the most dangerous of the bunch.
In either case it requires an intermediate object to connect between the owning pointer and the weak pointer such that communication can take place, you can’t do it with a regular unique_ptr.

This is exactly how I see it. You either accept that singly owned objects cannot be safely visible to multiple threads as we have always done or you put a lock on destruction when you make use of ptr_to_unique.

The second will ensure that single ownership destructors don't get called out of sequence but the potential for deadlock with that lock is a bit scary. It will require more overhead and if a background thread holds it locked for a long time while working on it, I could end up with my GUI thread frozen and a very frustrated user. I think it would be more of a specialist solution that you would construct and use when needed with the proviso that you never hold on to that lock for very long.

The first which is encapsulated by the proposed ptr_to_unique doesn't mean not worrying about or ignoring thread safety. You worry about it in the same way that you always have done. Data that is visible to multiple threads must have shared ownership and singly owned objects must never be visible to other threads. Following those rules, as we have always done, will ensure that you never hold a ptr_to_unique to a unique_ptr that is exposed to the actions of another thread and the ptr_to_uniques you have will work safely. It does use an intermediate object, a reference counted control block, and it works with a regular unique_ptr declared with a deleter hook.

You also wrote:
It could work in theory, but given the compromises, it’s already rare that you get to use weak_ptr, it could be hard to find a set of conditions where you would use it but also couldn’t just use a shared_ptr

I suspect that the low use of weak_ptr might be largely down to laziness and the perception that its only use is to “break cycles”. A lot of shared_ptrs should probably be weak_ptr but it is a lot more trouble to code. Things still work but the proliferation of shared_ptrs can result in a lot of unintended memory retention. Doing this effectively builds a prompt but slightly leaky garbage collector.

The proposed ptr_to_unique is intended to help with the many things that can happen in a single thread with single ownership. Below is an extract from an earlier post:

My need for this arose in the development of event driven GUI applications which are a form of cooperative multitasking all running in one thread. A lot of lifetimes can come and go but all the event handlers that you write are running in the same thread. Typically I will be displaying a polymorphic collection of objects and may run a procedure to pick out some that require special attention. I don't want to run that procedure every time the screen is repainted so I hold pointers to those objects. If the pointer is still valid, I use it. If not then its do nothing or run the procedure again. It was a necessary optimisation to prevent screen lag and I used to write a lot of very intrusive and fragile spaghetti code to make sure that those pointers were zeroed if their pointee was deleted.
And you wrote:
When you delete that owned object, do you actually need it to be deleted at that exact moment?

The answer is absolutely yes. This is single ownership design and RAII. When I delete an object from a collection I want it gone and its destructor called now. I don't want it living on as a phantom object being kept alive by some forgotten shared reference somewhere and its destructor called at some indeterminate time in the future.



Received on 2025-02-17 17:26:34