Fully thread-safe shared_ptr implementation

后端 未结 9 1450
面向向阳花
面向向阳花 2021-02-02 17:02

Does anybody know of a fully thread-safe shared_ptr implementation? E.g. boost implementation of shared_ptr is thread-safe for the targets (refcounting

相关标签:
9条回答
  • 2021-02-02 17:46

    I dont think this so easy, it is not enough to wrap your sh_ptr classes with a CS. It is true that if you maintain one single CS for all shared pointers it can ensure to avoid mutual the access and deletion of sh_ptr objects among different threads. But this would be terrible, one CS object for every shared pointer would be a real bottleneck. It would be suitable if every wrappable new ptr -s have different CS s' but this way we should create our CS dinamically, and ensure the copy ctors of sh_ptr classes to transmit this shared Cs. Now we arrived to the same problem: who quaranties that this Cs ptr is already deleted or not. We can be a little more smarty with volatile m_bReleased flags per instance but this way we cannot stuck the safety gaps between checking the flag and using the shared Cs. I can't see completely safe resolution for this problem. Maybe that terrible global Cs would be the minor bad as killing the app. (sorry for my english)

    0 讨论(0)
  • 2021-02-02 17:47

    Adding the necessary barriers for such a fully thread-safe shared_ptr implementation would likely impact performance. Consider the following race (note: pseudocode abounds):

    Thread 1: global_ptr = A;

    Thread 2: global_ptr = B;

    Thread 3: local_ptr = global_ptr;

    If we break this down into its constituent operations:

    Thread 1:

    A.refcnt++;
    tmp_ptr = exchange(global_ptr, A);
    if (!--tmp_ptr.refcnt) delete tmp_ptr;
    

    Thread 2:

    B.refcnt++;
    tmp_ptr = exchange(global_ptr, B);
    if (!--tmp_ptr.refcnt) delete tmp_ptr;
    

    Thread 3:

    local_ptr = global_ptr;
    local_ptr.refcnt++;
    

    Clearly, if thread 3 reads the pointer after A's swap, then B goes and deletes it before the reference count can be incremented, bad things will happen.

    To handle this, we need a dummy value to be used while thread 3 is doing the refcnt update: (note: compare_exchange(variable, expected, new) atomically replaces the value in variable with new if it's currently equal to new, then returns true if it did so successfully)

    Thread 1:

    A.refcnt++;
    tmp_ptr = global_ptr;
    while (tmp_ptr == BAD_PTR || !compare_exchange(global_ptr, tmp_ptr, A))
        tmp_ptr = global_ptr;
    if (!--tmp_ptr.refcnt) delete tmp_ptr;
    

    Thread 2:

    B.refcnt++;
    while (tmp_ptr == BAD_PTR || !compare_exchange(global_ptr, tmp_ptr, A))
        tmp_ptr = global_ptr;
    if (!--tmp_ptr.refcnt) delete tmp_ptr;
    

    Thread 3:

    tmp_ptr = global_ptr;
    while (tmp_ptr == BAD_PTR || !compare_exchange(global_ptr, tmp_ptr, BAD_PTR))
        tmp_ptr = global_ptr;
    local_ptr = tmp_ptr;
    local_ptr.refcnt++;
    global_ptr = tmp_ptr;
    

    You've now had to add a loop, with atomics in it in the middle of your /read/ operation. This is not a good thing - it can be extremely expensive on some CPUs. What's more, you're busy-waiting as well. You can start to get clever with futexes and whatnot - but by that point you've reinvented the lock.

    This cost, which has to be borne by every operation, and is very similar in nature to what a lock would give you anyway, is why you generally don't see such thread-safe shared_ptr implementations. If you need such a thing, I would recommend wrapping a mutex and shared_ptr into a convenience class to automate locking.

    0 讨论(0)
  • 2021-02-02 17:47

    You can easily do this by including a mutex object with every shared pointer, and wrapping increment/decrement commands with the lock.

    0 讨论(0)
提交回复
热议问题