[英]SEGFAULT when using shared_ptr
I'm trying to implement the Lazy Concurrent List-based Set in C++ by using shared_ptr
. 我正在尝试使用
shared_ptr
在C ++中实现基于Lazy Concurrent List的Set 。 My reasoning is that unreachable nodes
will be automatically freed by the last shared_ptr
. 我的理由是,
unreachable nodes
将被最后一个shared_ptr
自动释放。 As per my understanding, increment and decrement operation on a shared_ptr's reference count
is atomic. 根据我的理解,对
shared_ptr's reference count
递增和递减操作是原子的。 Which means only the last shared_ptr with reference to the node should call delete/free for that node. 这意味着只有最后一个引用该节点的shared_ptr应该为该节点调用delete / free 。 I ran the program for multiple threads , but my program is crashing with the error
double free called
or just Segmentation Fault(SIGSEGV) . 我为多个线程运行程序,但我的程序崩溃时出现错误
double free called
或只是分段错误(SIGSEGV) 。 I don't understand how this is possible. 我不明白这是怎么可能的。 Given below is my code for the implementation, with the method names signifying their intended operation.
下面给出了我的实现代码,方法名称表示它们的预期操作。
#include<thread>
#include<iostream>
#include<mutex>
#include<climits>
using namespace std;
class Thread
{
public:
std::thread t;
};
int n=50,ki=100,kd=100,kc=100;`/*no of threads, no of inserts,deletes & searches*/`
class Node
{
public:
int key;
shared_ptr<Node> next;
bool marked;
std::mutex nodeLock;
Node() {
key=0;
next = nullptr;
marked = false;
}
Node(int k) {
key = k;
next = nullptr;
marked = false;
}
void lock() {
nodeLock.lock();
}
void unlock() {
nodeLock.unlock();
}
~Node()
{
}
};
class List {
shared_ptr<Node> head;
shared_ptr<Node> tail;
public:
bool validate(shared_ptr<Node> pred, shared_ptr<Node> curr) {
return !(pred->marked) && !(curr->marked) && ((pred->next) == curr);
}
List() {
head=make_shared<Node>(INT_MIN);
tail=make_shared<Node>(INT_MAX);
head->next=tail;
}
bool add(int key)
{
while(true)
{
/*shared_ptr<Node> pred = head;
shared_ptr<Node> curr = pred->next;*/
auto pred = head;
auto curr = pred->next;
while (key>(curr->key))
{
pred = curr;
curr = curr->next;
}
pred->lock();
curr->lock();
if (validate(pred,curr))
{
if (curr->key == key)
{
curr->unlock();
pred->unlock();
return false;
}
else
{
shared_ptr<Node> newNode(new Node(key));
//auto newNode = make_shared<Node>(key);
//shared_ptr<Node> newNode = make_shared<Node>(key);
newNode->next = curr;
pred->next = newNode;
curr->unlock();
pred->unlock();
return true;
}
}
curr->unlock();
pred->unlock();
}
}
bool remove(int key)
{
while(true)
{
/*shared_ptr<Node> pred = head;
shared_ptr<Node> curr = pred->next;*/
auto pred = head;
auto curr = pred->next;
while (key>(curr->key))
{
pred = curr;
curr = curr->next;
}
pred->lock();
curr->lock();
if (validate(pred,curr))
{
if (curr->key != key)
{
curr->unlock();
pred->unlock();
return false;
}
else
{
curr->marked = true;
pred->next = curr->next;
curr->unlock();
pred->unlock();
return true;
}
}
curr->unlock();
pred->unlock();
}
}
bool contains(int key) {
//shared_ptr<Node> curr = head->next;
auto curr = head->next;
while (key>(curr->key)) {
curr = curr->next;
}
return curr->key == key && !curr->marked;
}
}list;
void test(int curr)
{
bool test;
int time;
int val, choice;
int total,k=0;
total=ki+kd+kc;
int i=0,d=0,c=0;
while(k<total)
{
choice = (rand()%3)+1;
if(choice==1)
{
if(i<ki)
{
val = (rand()%99)+1;
test = list.add(val);
i++;
k++;
}
}
else if(choice==2)
{
if(d<kd)
{
val = (rand()%99)+1;
test = list.remove(val);
d++;
k++;
}
}
else if(choice==3)
{
if(c<kc)
{
val = (rand()%99)+1;
test = list.contains(val);
c++;
k++;
}
}
}
}
int main()
{
int i;
vector<Thread>thr(n);
for(i=0;i<n;i++)
{
thr[i].t = thread(test,i+1);
}
for(i=0;i<n;i++)
{
thr[i].t.join();
}
return 0;
}
I'm not able to figure out what's wrong with the above code. 我无法弄清楚上面的代码有什么问题。 The errors differ every time, some of which are just
SEGFAULTS
or 错误每次都不同,其中一些只是
SEGFAULTS
或
pure virtual method called
terminate called without an active exception
Aborted (core dumped)
Could you please point out what I'm doing wrong in the above code? 你能否指出我在上面的代码中做错了什么? And how to fix that error?
以及如何解决这个错误?
EDIT: Added a very crude test function
which randomly calls the three list methods
. 编辑:添加了一个非常粗略的
test function
,随机调用三个list methods
。 Also, number of threads and number of each operations are declared globally. 此外,全局声明线程数和每个操作的数量。 Crude programming, but it recreates the SEGFAULT .
粗体编程,但它重新创建了SEGFAULT 。
The issue is that you're not using the atomic store and load operations for your shared_ptr
s. 问题是您没有使用
shared_ptr
的原子库和加载操作。
It is true that the reference count in the control block (to which each shared_ptr
participating in ownership of a particular shared object has a pointer to) of a shared_ptr
is atomic, however, the data members of the shared_ptr
itself aren't. 这是事实,在控制块的引用计数(对每个
shared_ptr
参与特定共享对象的所有权有一个指针指向)一个的shared_ptr
是原子,然而,的数据成员shared_ptr
本身都没有。
Thus it is safe to have multiple threads each with their own shared_ptr
to a shared object, but it is not save to have multiple threads access the same shared_ptr
as soon as at least one of them is using a non-const member function, which is what you're doing when reassigning the next
pointer. 因此,将多个线程各自拥有自己的
shared_ptr
到共享对象是安全的,但是只要至少其中一个线程使用非const成员函数,就不shared_ptr
多个线程访问同一个shared_ptr
在重新分配next
指针时你正在做什么。
Let's look at a the (simplified and prettified) copy-constructor of libstdc++'s shared_ptr
implementation: 让我们看一下libstdc ++的
shared_ptr
实现的(简化和美化)复制构造函数:
shared_ptr(const shared_ptr& rhs)
: m_ptr(rhs.m_ptr),
m_refcount(rhs.m_refcount)
{ }
Here m_ptr
is just a raw pointer to the shared object, and m_refcount
is a class that does the reference counting and also handles eventual deletion of the object m_ptr
points to. 这里
m_ptr
只是一个指向共享对象的原始指针,而m_refcount
是一个执行引用计数的类,并且还处理m_ptr
指向的对象的最终删除。
Just one example of what can go wrong: Assume that currently a thread is trying to figure out whether a particular key is contained in the list. 可能出现问题的一个例子:假设当前一个线程试图弄清楚列表中是否包含特定键。 It starts with the copy-initialization
auto curr = head->next
in List::contains
. 它从
List::contains
的copy-initialization auto curr = head->next
开始。 Just after it managed to initialize curr.m_ptr
the OS scheduler decides this thread has to pause and schedules in another thread. 在它设法初始化
curr.m_ptr
,OS调度程序决定该线程必须暂停并在另一个线程中进行调度。
That other thread is removing the successor of head
. 那个其他线程正在移除
head
的继任者。 Since the ref-count of head->next
still is 1 (after all, the ref-count of head->next
wasn't modified by thread 1 yet), when the second thread is done removing the node it is being deleted. 由于
head->next
的ref-count仍然是1(毕竟, head->next
的ref-count尚未被线程1修改),当第二个线程完成删除节点时,它将被删除。
Then some time later the first thread continues. 然后一段时间后第一个线程继续。 It completes the initialization of
curr
, but since m_ptr
was already initialized before thread 2 started the deletion, it still points to the now deleted node. 它完成了
curr
的初始化,但由于m_ptr
在线程2开始删除之前已经初始化,它仍然指向现在删除的节点。 When trying to compare key > curr->key
thread 1 will access invalid memory. 当尝试比较
key > curr->key
线程1将访问无效的内存。
std::atomic_load
and std::atomic_store
prevent the issue from occurring by locking a mutex before the call to the copy-constructor/copy-assignment-operator of the shared_ptr
that is passed in by pointer. std::atomic_load
和std::atomic_store
通过在调用指针传入的shared_ptr
的copy-constructor / copy-assignment-operator之前锁定互斥锁std::atomic_store
防止发生此问题。 If all reads from and writes to shared_ptr
s that are shared across multiple threads are through std::atomic_load
/ std::atomic_store
resp. 如果对多个线程共享的
shared_ptr
的所有读取和写入都是通过std::atomic_load
/ std::atomic_store
resp。 it can never happen that one thread has only modified m_ptr
but not the reference count at the time another thread starts reading or modifying the same shared_ptr
. 在另一个线程开始读取或修改相同的
shared_ptr
,一个线程只修改了m_ptr
而不是引用计数。
With the necessary atomic loads and stores the List
member functions should read as follows: 使用必要的原子加载和存储,
List
成员函数应如下所示:
bool validate(Node const& pred, Node const& curr) {
return !(pred.marked) && !(curr.marked) &&
(std::atomic_load(&pred.next).get() == &curr);
}
bool add(int key) {
while (true) {
auto pred = std::atomic_load(&head);
auto curr = std::atomic_load(&pred->next);
while (key > (curr->key)) {
pred = std::move(curr);
curr = std::atomic_load(&pred->next);
}
std::scoped_lock lock{pred->nodeLock, curr->nodeLock};
if (validate(*pred, *curr)) {
if (curr->key == key) {
return false;
} else {
auto new_node = std::make_shared<Node>(key);
new_node->next = std::move(curr);
std::atomic_store(&pred->next, std::move(new_node));
return true;
}
}
}
}
bool remove(int key) {
while (true) {
auto pred = std::atomic_load(&head);
auto curr = std::atomic_load(&pred->next);
while (key > (curr->key)) {
pred = std::move(curr);
curr = std::atomic_load(&pred->next);
}
std::scoped_lock lock{pred->nodeLock, curr->nodeLock};
if (validate(*pred, *curr)) {
if (curr->key != key) {
return false;
} else {
curr->marked = true;
std::atomic_store(&pred->next, std::atomic_load(&curr->next));
return true;
}
}
}
}
bool contains(int key) {
auto curr = std::atomic_load(&head->next);
while (key > (curr->key)) {
curr = std::atomic_load(&curr->next);
}
return curr->key == key && !curr->marked;
}
Additionally, you should also make Node::marked
a std::atomic_bool
. 另外,您还应该将
Node::marked
为std::atomic_bool
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.