thread safe about shared_ptr of copy and assign.

Is the code below thread safe? According to my understand, It should be.

But In Windows and Bash on Ubuntu on Windows, The code below eventually run into Segmentation fault, or other unknown status.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <memory>
#include <string>
#include <thread>
#include <iostream>
#include <functional>

#include <cstdlib>
#include <cstring>

class A {
public:
	std::shared_ptr<std::string> str;
};

int main()
{
	class A a;
	auto func = [&]{
		int j;
		while(true){
			std::shared_ptr<std::string> str(a.str);
			if(!str){
				std::cout<<"null str" << std::endl;
				continue;
			}
			std::cout<<*str<<std::endl;
			j+=1;
			j=j&0xff;
		}

	};
	std::thread b(func);

	long long int i=0;
	char buff[128];
	do{
		sprintf( buff, "string[%d]", i );
		a.str = std::make_shared<std::string>( buff );
		if(i>38400){
			i=0;
		}
	}while(++i);
	return 0;
}
Last edited on
No, it's not. Line 21 is atomic, but line 38 isn't. An std::shared_ptr can be copied (i.e. have its refcount incremented) atomically, and the copy can be destructed atomically (i.e. decrement its refcount), but it can't be repointed atomically, as that involves changing two datums: the refcount and the pointer.
cppreference wrote:
All member functions (including copy constructor and copy assignment) can be called by multiple threads on different instances of shared_ptr without additional synchronization even if these instances are copies and share ownership of the same object. If multiple threads of execution access the same shared_ptr without synchronization and any of those accesses uses a non-const member function of shared_ptr then a data race will occur; the shared_ptr overloads of atomic functions can be used to prevent the data race.
https://en.cppreference.com/w/cpp/memory/shared_ptr

In your code the non-const member function is the assignment operator on line 38.

If you want to use the same shared_ptr instance from multiple threads you can use std::atomic_load, std::atomic_store, etc.

1
2
//std::shared_ptr<std::string> str(a.str);
std::shared_ptr<std::string> str = std::atomic_load(&a.str);
1
2
//a.str = std::make_shared<std::string>( buff );
std::atomic_store(&a.str, std::make_shared<std::string>(buff));
https://en.cppreference.com/w/cpp/memory/shared_ptr/atomic

Note that these functions will be deprecated in C++20 in favour of std::atomic<std::shared_ptr<T>>.
https://en.cppreference.com/w/cpp/memory/shared_ptr/atomic2
Last edited on
Thank you guys for replying.

Now I don't think either line 21 and line 38 are thread safe.

All member functions (including copy constructor and copy assignment) can be called by multiple threads on different instances of shared_ptr without additional synchronization even if these instances are copies and share ownership of the same object
just means situation below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <memory>
#include <random>
#include <string>
#include <thread>
#include <iostream>
#include <functional>

#include <cstdlib>
#include <cstring>

void func(std::shared_ptr<std::string> str) {
	const auto len = 5;
	std::random_device r;
	std::default_random_engine el(r());
	std::uniform_int_distribution<int> u(1,4);
	std::shared_ptr<std::string> array[len];
	while (true) {
		for (auto &i : array) {
			//All member functions (including copy constructor and copy assignment) 
			//can be called by multiple threads on different instances of shared_ptr 
			//without additional synchronization even if these instances are copies 
			//and share ownership of the same object.
			int j = u(el);
			if (j == 1)
				i.reset();
			if (j == 2)
				i = str;
			if (j == 3)
				i = std::shared_ptr<std::string>(str);
			if (j == 4)
				(void*)i.get();
                        //get() is thread safe, but not for accessing the std::string obj.

			//(The variable 'str' and array 'array' can only be access by 'this thread'.)
			//(Accessing 'str' or 'array' by multi-thread without additional synchronization
			// is not thread safe.)
		}
	}

}

int main()
{

	auto str = std::make_shared<std::string>("I'm a string.");
	std::thread a(func, str);
	std::thread b(func, str);
	func(str);
	return 0;
}



Last edited on
I hope I understand it correctly.
Topic archived. No new replies allowed.