Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Can I pass a shared_ptr to a thread as a temporary ?

103 views
Skip to first unread message

Bonita Montero

unread,
Feb 8, 2020, 7:32:31 AM2/8/20
to
I've this line in my code:
threads.emplace_back( scnThread, shared_ptr<wstring>( new wstring(
nextFilePattern ) ), nextPathLength );
Can I rely on that the thread gets its copy of the shared_ptr
before all shared_ptr-copies in the initiating thread are desroyed?

Melzzzzz

unread,
Feb 8, 2020, 8:37:02 AM2/8/20
to
You put object in container. You get object from container in other
thread, so shared ptr is still referenced.

--
press any key to continue or any other to quit...
U ničemu ja ne uživam kao u svom statusu INVALIDA -- Zli Zec
Svi smo svedoci - oko 3 godine intenzivne propagande je dovoljno da jedan narod poludi -- Zli Zec
Na divljem zapadu i nije bilo tako puno nasilja, upravo zato jer su svi
bili naoruzani. -- Mladen Gogala

Bonita Montero

unread,
Feb 8, 2020, 11:01:43 AM2/8/20
to
>> I've this line in my code:
>> threads.emplace_back( scnThread, shared_ptr<wstring>( new wstring(
>> nextFilePattern ) ), nextPathLength );
>> Can I rely on that the thread gets its copy of the shared_ptr
>> before all shared_ptr-copies in the initiating thread are desroyed?

> You put object in container.

Which containeer ?

> You get object from container in other thread, so shared ptr is still referenced.

You don't understand the question. Depending on the implementation it
could be like that the local copy of the shared_ptr might get destroyed
before the thread get its own copy.
But I asked for this on Stack Overflow also and got a more qualfied
answer, those I expected: the thread gets safely its own copy and
there is no such race-condition.

Melzzzzz

unread,
Feb 8, 2020, 11:08:44 AM2/8/20
to
On 2020-02-08, Bonita Montero <Bonita....@gmail.com> wrote:
>>> I've this line in my code:
>>> threads.emplace_back( scnThread, shared_ptr<wstring>( new wstring(
>>> nextFilePattern ) ), nextPathLength );
>>> Can I rely on that the thread gets its copy of the shared_ptr
>>> before all shared_ptr-copies in the initiating thread are desroyed?
>
>> You put object in container.
>
> Which containeer ?

Since you ask this question, I see you are totally clueless.

Bonita Montero

unread,
Feb 8, 2020, 12:01:08 PM2/8/20
to
>>>> I've this line in my code:
>>>> threads.emplace_back( scnThread, shared_ptr<wstring>( new wstring(
>>>> nextFilePattern ) ), nextPathLength );
>>>> Can I rely on that the thread gets its copy of the shared_ptr
>>>> before all shared_ptr-copies in the initiating thread are desroyed?

>>> You put object in container.

>> Which containeer ?

> Since you ask this question, I see you are totally clueless.

You are clueless - a shared_ptr<> isn't a container.
And I guessed the answer, but I wasn't absolutely sure and asked
fort certainty. As the question remained here so long without any
professional answer I'm not clueless.

James Kuyper

unread,
Feb 8, 2020, 12:40:37 PM2/8/20
to
On Saturday, February 8, 2020 at 12:01:08 PM UTC-5, Bonita Montero wrote:
> >>>> I've this line in my code:
> >>>> threads.emplace_back( scnThread, shared_ptr<wstring>( new wstring(
> >>>> nextFilePattern ) ), nextPathLength );
> >>>> Can I rely on that the thread gets its copy of the shared_ptr
> >>>> before all shared_ptr-copies in the initiating thread are desroyed?
>
> >>> You put object in container.
>
> >> Which containeer ?
>
> > Since you ask this question, I see you are totally clueless.
>
> You are clueless - a shared_ptr<> isn't a container.

He was referring to the container for which you called emplace_back(). You didn't actually tell us it was a container, it could be some user-defined type that just happens to have a member function mis-named emplace_back() - but it's a reasonable guess.

> And I guessed the answer, but I wasn't absolutely sure and asked
> fort certainty. As the question remained here so long without any
> professional answer I'm not clueless.

I suspect many people don't bother helping you, who could.

Bonita Montero

unread,
Feb 8, 2020, 1:09:12 PM2/8/20
to
> He was referring to the container for which you called emplace_back(). You didn't actually tell us it was a container, it could be some user-defined type that just happens to have a member function mis-named emplace_back() - but it's a reasonable guess.

That's wasn't part of the problem.

Chris M. Thomasson

unread,
Feb 8, 2020, 10:13:01 PM2/8/20
to
On 2/8/2020 4:32 AM, Bonita Montero wrote:
> I've this line in my code:
>     threads.emplace_back( scnThread, shared_ptr<wstring>( new wstring(
> nextFilePattern ) ), nextPathLength );
> Can I rely on that the thread gets its copy of the shared_ptr

How is that other thread getting its copy to begin with? Is this thread,
assuming scnThread, running? Or, do you start it up after adding its
"descriptor" the threads container via emplace_back?

> before all shared_ptr-copies in the initiating thread are desroyed?

How are you communicating between threads?

Chris M. Thomasson

unread,
Feb 8, 2020, 10:24:31 PM2/8/20
to
Are you creating scnThread in the constructor of the object that gets
added to the threads container? If so be careful. I have seen some nasty
nightmares that create a thread in the constructor that starts running
_before_ the damn ctor is even completed! The running thread just
blindly assumed that the ctor was finished, NOPE!

Öö Tiib

unread,
Feb 9, 2020, 8:23:30 AM2/9/20
to
It might be code of some "using namespace std;" tutorials/noobs or
it might be whatever. Just dump the attitude that you may get answers
to trash like that. C++ does not compile line by line. Post whole
compiling example or go troll elsewhere.

Paavo Helde

unread,
Feb 9, 2020, 10:28:52 AM2/9/20
to
The initial data passed to std::thread should be passed to the
std::thread constructor where it will be copied or moved appropriately
before the thread starts. Any time later one will need more elaborate
synchronization.

Your example does not really contain enough information to say if it is
correct or not, important types are missing. Assuming that 'threads' is
e.g. a std::vector<std::thread>, the code would be OK as emplace_back()
would indeed pass the data to the std::thread constructor.

Bonita Montero

unread,
Feb 9, 2020, 11:12:12 AM2/9/20
to
> It might be code of some "using namespace std;" tutorials/noobs or
> it might be whatever. Just dump the attitude that you may get answers
> to trash like that. C++ does not compile line by line. Post whole
> compiling example or go troll elsewhere.

I posted everything to understand the issue.
The code is sufficient and the description of the question as well.
And I see you didn't understand it.


Juha Nieminen

unread,
Feb 10, 2020, 7:11:11 AM2/10/20
to
Bonita Montero <Bonita....@gmail.com> wrote:
> I posted everything to understand the issue.
> The code is sufficient and the description of the question as well.
> And I see you didn't understand it.

The onus on the understandability of text is on the writer, not the reader.

Bonita Montero

unread,
Feb 10, 2020, 9:23:46 AM2/10/20
to
>> I posted everything to understand the issue.
>> The code is sufficient and the description of the question as well.
>> And I see you didn't understand it.

> The onus on the understandability of text is on the writer, not the reader.

The people on Stack Overflow understood it.

Chris M. Thomasson

unread,
Feb 10, 2020, 5:04:47 PM2/10/20
to
// pseudo code, sorry for any typos:


void foo(shared_ptr<bar> ptr)
{
shared_ptr<bar> tptr = ptr;

// create thread using tptr
}


// call foo with a temporary
foo(shared_ptr<bar>(new bar()));


iirc, works fine. the tptr in the function foo has a reference,
therefore the refcount is bumped accordingly. You can create a thread
using tptr.

I don't really use std::shared_ptr all that much, preferring "intrusive"
counts that embed the refcount within the object itself.

Chris M. Thomasson

unread,
Feb 10, 2020, 6:15:54 PM2/10/20
to
On 2/10/2020 2:04 PM, Chris M. Thomasson wrote:
> On 2/10/2020 6:23 AM, Bonita Montero wrote:
>>>> I posted everything to understand the issue.
>>>> The code is sufficient and the description of the question as well.
>>>> And I see you didn't understand it.
>>
>>> The onus on the understandability of text is on the writer, not the
>>> reader.
>>
>> The people on Stack Overflow understood it.
>>
>
> // pseudo code, sorry for any typos:
>
>
> void foo(shared_ptr<bar> ptr)
> {
>    shared_ptr<bar> tptr = ptr;
>
>    // create thread using tptr
> }
>
>
> // call foo with a temporary
> foo(shared_ptr<bar>(new bar()));
>
>
> iirc, works fine. the tptr in the function foo has a reference,
> therefore the refcount is bumped accordingly. You can create a thread
> using tptr.

You can package tptr up in a struct: a thread descriptor that has a
shared_ptr<bar> member to store it in. This maintains the reference
count, start the thread, and it has that reference, fine. The reference
was bumped _before_ the thread was started. You can store it in a
"message" struct and enqueue it such that other threads can dequeue it.
The reference was bumped _before_ another thread has a chance to grab
it. Just keep in mind that shared_ptr requires that a thread has a prior
reference before it can access it. In other words, shared_ptr provides
basic thread safety, not strong, at least last time I used it. pseudo-code:


This is bad wrt shared_ptr:


static shared_ptr<bar> g_bar; // nullptr


void multiple_threads()
{
for (;;)
{
// this does not work wrt the
// last time I looked at shared_ptr

shared_ptr<bar> local = g_bar;

local->foobar();
}

}


void single_thread()
{
shared_ptr<bar> local(new bar());
g_bar = local;

Chris M. Thomasson

unread,
Feb 10, 2020, 6:39:35 PM2/10/20
to
// it would be nice to check for nullptr...
if (! local) continue;

Paavo Helde

unread,
Feb 11, 2020, 1:37:43 AM2/11/20
to
On 11.02.2020 0:04, Chris M. Thomasson wrote:
> I don't really use std::shared_ptr all that much, preferring "intrusive"
> counts that embed the refcount within the object itself.

No need to avoid std::shared_ptr for this reason, std::make_shared() and
std::allocate_shared() effectively do exactly this.

One problem with home-grown intrusive refcounting schemas is that it
would be too easy/too tempting to create new smartpointers during the
main object construction and destruction, leading to conceptual problems
and race conditions. Using std::shared_ptr will avoid such problems, by
design.


Bonita Montero

unread,
Feb 11, 2020, 2:07:16 AM2/11/20
to
> No need to avoid std::shared_ptr for this reason, std::make_shared() and
> std::allocate_shared() effectively do exactly this.

That's a matter of taste.

Chris M. Thomasson

unread,
Feb 11, 2020, 5:24:09 PM2/11/20
to
On 2/10/2020 10:37 PM, Paavo Helde wrote:
> On 11.02.2020 0:04, Chris M. Thomasson wrote:
>> I don't really use std::shared_ptr all that much, preferring "intrusive"
>> counts that embed the refcount within the object itself.
>
> No need to avoid std::shared_ptr for this reason, std::make_shared() and
> std::allocate_shared() effectively do exactly this.
>
> One problem with home-grown intrusive refcounting schemas is that it
> would be too easy/too tempting to create new smartpointers during the
> main object construction and destruction, leading to conceptual problems
> and race conditions.

I am not exactly sure what you mean here? Can you give a simple example?


> Using std::shared_ptr will avoid such problems, by
> design.

Fwiw, shared_ptr does not provide strong thread safety, last time I
looked. This can be dangerous in certain scenarios that require it. Here
is the pattern, pseudo-code:

static shared_ptr<bar> g_bar; // nullptr


void multiple_threads()
{
for (;;)
{
// this does not work wrt the
// last time I looked at shared_ptr

shared_ptr<bar> local = g_bar;
if (! local) continue;

local->foobar();
}

}


void single_thread()
{
shared_ptr<bar> local(new bar());
g_bar = local;
}


Last time I checked, shared_ptr works only if a thread has a prior
reference. The multiple_threads() function violates this. Now, there is
a home-brew smart pointer than can easily handle such scenarios. Afaict,
its basically crowbar proof:

http://atomic-ptr-plus.sourceforge.net

This has a patent:

https://patents.google.com/patent/US5295262

expired but fee related. Humm...

Chris M. Thomasson

unread,
Feb 11, 2020, 5:29:19 PM2/11/20
to
On 2/9/2020 7:28 AM, Paavo Helde wrote:
> On 8.02.2020 14:32, Bonita Montero wrote:
>> I've this line in my code:
>>      threads.emplace_back( scnThread, shared_ptr<wstring>( new wstring(
>> nextFilePattern ) ), nextPathLength );
>> Can I rely on that the thread gets its copy of the shared_ptr
>> before all shared_ptr-copies in the initiating thread are desroyed?
>
> The initial data passed to std::thread should be passed to the
> std::thread constructor where it will be copied or moved appropriately
> before the thread starts. Any time later one will need more elaborate
> synchronization.

Afaict, this pretty much sums it all up.

Chris Vine

unread,
Feb 11, 2020, 7:17:41 PM2/11/20
to
It is simpler than that. The reason your code is defective is that the
variable g_bar in shared namespace scope is modified by single_thread()
without synchronization when it may also be read concurrently by
multiple_threads(). std::shared_ptr is designed to be as thread safe as
a raw pointer (or any other scalar): in other words its reference count
is thread safe but if you modify a shared_ptr instance
contemporaneously with another thread accessing or modifying that
same instance then you have undefined behaviour, unless you happen to
use the atomic access provided for in §20.8.2.6 of C++14. That is
normal and as it should be. It would be wrong, and horribly inefficient
in most uses, to take any other approach.

That is not to say that intrusive pointers are not a good idea. They
can make controlling object lifetime in multi-threaded code more
obvious and thus less error prone. In particular, objects held by
intrusive pointer can more easily hold a reference to themselves when
they need to guarantee their own existence when accessing their own
members in a logically atomic section of code, and so save additional
locking.

Chris M. Thomasson

unread,
Feb 11, 2020, 7:45:25 PM2/11/20
to
Does atomic<shared_ptr<foo>> work for this wrt strong thread safety?

There are different means to gain a true atomic smart pointer.
Accomplishing this in a lock-free manner can be tricky. DWCAS makes it
much easier. :^)

Chris Vine

unread,
Feb 11, 2020, 8:11:12 PM2/11/20
to
On Tue, 11 Feb 2020 16:45:13 -0800
"Chris M. Thomasson" <chris.m.t...@gmail.com> wrote:
> On 2/11/2020 4:17 PM, Chris Vine wrote:
> > It is simpler than that. The reason your code is defective is that the
> > variable g_bar in shared namespace scope is modified by single_thread()
> > without synchronization when it may also be read concurrently by
> > multiple_threads(). std::shared_ptr is designed to be as thread safe as
> > a raw pointer (or any other scalar): in other words its reference count
> > is thread safe but if you modify a shared_ptr instance
> > contemporaneously with another thread accessing or modifying that
> > same instance then you have undefined behaviour, unless you happen to
> > use the atomic access provided for in §20.8.2.6 of C++14.
>
> Does atomic<shared_ptr<foo>> work for this wrt strong thread safety?
>
> There are different means to gain a true atomic smart pointer.
> Accomplishing this in a lock-free manner can be tricky. DWCAS makes it
> much easier. :^)

I don't know what you mean by "strong thread safety" that goes beyond
"thread safe" for this case, but yes you could do it with the standard's
provided atomic operations for shared_ptr without incurring undefined
behaviour ("Concurrent access to a shared_ptr object from multiple
threads does not introduce a data race if the access is done
exclusively via the [atomic] functions in this section ...").

All you have got in your case is atomic loads and an atomic store
with at least acquire/release memory ordering. This may or may not be
lock free, however.

Chris M. Thomasson

unread,
Feb 11, 2020, 8:30:06 PM2/11/20
to
Well, the multiple_threads function needs to be able to atomically
increment a reference _and_ atomically load the pointer at once. Basic
thread safety is the same as a raw pointer, or int. Strong is full blown
concurrent read write access no matter what. Check this out:

http://www.1024cores.net/home/lock-free-algorithms/object-life-time-management/differential-reference-counting


This requires strong thread safety because these threads do not own
prior references to the global g_bar.

Cholo Lennon

unread,
Feb 11, 2020, 9:50:03 PM2/11/20
to
Wow, you can't avoid trolling in every single answer that you receive.
For sure you need a mental health doctor. You used to be a civilized
participant, but nowadays you have serious problem.

This newsgroup is a disaster: religious nuts, trolls like you, and only
a few people interested in sharing their knowledge with respect. And
when someone new appears is attacked with things like "this is
off-topic" (because he/she dared to comment something related to
Windows/QT or whatever... a dead group with more or less 10 regular
participants has the luxury to expel newcomers, totally ridiculous)


--
Cholo Lennon
Bs.As.
ARG

Ian Collins

unread,
Feb 11, 2020, 11:12:16 PM2/11/20
to
On 12/02/2020 15:49, Cholo Lennon wrote:
> On 2/11/20 4:07 AM, Bonita Montero wrote:
>>> No need to avoid std::shared_ptr for this reason, std::make_shared()
>>> and std::allocate_shared() effectively do exactly this.
>>
>> That's a matter of taste.
>
> Wow, you can't avoid trolling in every single answer that you receive.
> For sure you need a mental health doctor. You used to be a civilized
> participant, but nowadays you have serious problem.

It has always been rude.

--
Ian.

Chris M. Thomasson

unread,
Feb 12, 2020, 2:21:14 AM2/12/20
to
Working with him way, back on comp.programming.threads.

Bonita Montero

unread,
Feb 12, 2020, 2:28:46 AM2/12/20
to
>>> That's a matter of taste.

>> Wow, you can't avoid trolling in every single answer that you receive.
>> For sure you need a mental health doctor. You used to be a civilized
>> participant, but nowadays you have serious problem.

> It has always been rude.

What's rude about writing, that using make_shared, make_unique etc.
is a matter of taste ?

Jorgen Grahn

unread,
Feb 12, 2020, 7:32:41 AM2/12/20
to
That's not what you wrote. And I note that you now snipped the
context.

/Jorgen

--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .

Bonita Montero

unread,
Feb 12, 2020, 8:06:23 AM2/12/20
to
> That's not what you wrote. ...

Chris Vine

unread,
Feb 12, 2020, 10:01:39 AM2/12/20
to
On Tue, 11 Feb 2020 17:29:54 -0800
The shared_ptr atomic functions provided by C++11 onwards must support
both atomic increment of the reference (which is anyway provided by
std::shared_ptr irrespective of the atomic functions) and atomic load
and store of the pointer. Otherwise they could not honour the
requirement not to create a data race.

Whether they manage to do it without a mutex (ie lock-free) is another
matter.

You keep on writing example code (as above, again) which does not use
the shared_ptr atomic functions in a case where they are necessary: of
course in that case your code does not work. You need to use the right
tool for the job - try reading §20.8.2.6 of C++14.

Chris M. Thomasson

unread,
Feb 12, 2020, 7:16:06 PM2/12/20
to
I did not know that one could wrap shared_ptr<bar> in atomic<>. So the
following should be Kosher?
______________________________________
static atomic<shared_ptr<bar>> g_bar; // nullptr

void multiple_threads()
{
for (;;)
{
shared_ptr<bar> local = g_bar;
if (! local) continue;

local->foobar();
}
}

void single_thread()
{
shared_ptr<bar> local(new bar());
g_bar = local;
}
______________________________________

The default loads and stores wrt atomic should be seq_cst. I apologize
for my ignorance wrt never using atomic<shared_ptr<T>>.

Shi% happens.

Chris Vine

unread,
Feb 12, 2020, 8:29:08 PM2/12/20
to
On Wed, 12 Feb 2020 16:15:52 -0800
"Chris M. Thomasson" <chris.m.t...@gmail.com> wrote:
[snip]
> I did not know that one could wrap shared_ptr<bar> in atomic<>. So the
> following should be Kosher?
> ______________________________________
> static atomic<shared_ptr<bar>> g_bar; // nullptr
>
> void multiple_threads()
> {
> for (;;)
> {
> shared_ptr<bar> local = g_bar;
> if (! local) continue;
>
> local->foobar();
> }
> }
>
> void single_thread()
> {
> shared_ptr<bar> local(new bar());
> g_bar = local;
> }
> ______________________________________
>
> The default loads and stores wrt atomic should be seq_cst. I apologize
> for my ignorance wrt never using atomic<shared_ptr<T>>.
>
> Shi% happens.

It would be great if you could read §20.8.2.6 of C++14, as I have
suggested before. Are you having trouble finding it?

Chris M. Thomasson

unread,
Feb 12, 2020, 8:35:10 PM2/12/20
to
On 2/12/2020 5:28 PM, Chris Vine wrote:
> On Wed, 12 Feb 2020 16:15:52 -0800
> "Chris M. Thomasson" <chris.m.t...@gmail.com> wrote:
> [snip]
>> I did not know that one could wrap shared_ptr<bar> in atomic<>. So the
>> following should be Kosher?
>> ______________________________________
>> static atomic<shared_ptr<bar>> g_bar; // nullptr
[...]
>> ______________________________________
>>
>> The default loads and stores wrt atomic should be seq_cst. I apologize
>> for my ignorance wrt never using atomic<shared_ptr<T>>.
>>
>> Shi% happens.
>
> It would be great if you could read §20.8.2.6 of C++14, as I have
> suggested before. Are you having trouble finding it?
>

I can reading it right now:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4296.pdf

Snippet:
_________________
20.8.2.6 shared_ptr atomic access [util.smartptr.shared.atomic]
1 Concurrent access to a shared_ptr object from multiple threads does
not introduce a data race if the access
is done exclusively via the functions in this section and the instance
is passed as their first argument.
2 The meaning of the arguments of type memory_order is explained in 29.3.
template<class T>
bool atomic_is_lock_free(const shared_ptr<T>* p);
3 Requires: p shall not be null.
4 Returns: true if atomic access to *p is lock-free, false otherwise.
5 Throws: Nothing.
template<class T>
shared_ptr<T> atomic_load(const shared_ptr<T>* p);
6 Requires: p shall not be null.
7 Returns: atomic_load_explicit(p, me
_________________

Okay! Thanks for your patience.

Chris M. Thomasson

unread,
Feb 12, 2020, 8:36:57 PM2/12/20
to
On 2/12/2020 5:34 PM, Chris M. Thomasson wrote:
> On 2/12/2020 5:28 PM, Chris Vine wrote:
>> On Wed, 12 Feb 2020 16:15:52 -0800
>> "Chris M. Thomasson" <chris.m.t...@gmail.com> wrote:
[...]
>> It would be great if you could read §20.8.2.6 of C++14, as I have
>> suggested before.  Are you having trouble finding it?
>>
>
> I can reading it right now:
^^^^

I _am_ reading it.

>
> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4296.pdf
[...]
0 new messages