Gustorn
Gustorn

Reputation: 455

Linking pthread disables lock-free shared_ptr implementation

The title pretty much conveys all relevant information, but here's a minimal repro:

#include <atomic>
#include <cstdio>
#include <memory>

int main() {
    auto ptr = std::make_shared<int>(0);
    bool is_lockless = std::atomic_is_lock_free(&ptr);
    printf("shared_ptr is lockless: %d\n", is_lockless);
}

Compiling this with the following compiler options produces a lock-free shared_ptr implementation:

g++ -std=c++11 -march=native main.cpp

While this doesn't:

g++ -std=c++11 -march=native -pthread main.cpp

GCC version: 5.3.0 (on Linux, using libstdc++), tested on multiple machines that should have the necessary atomic instructions to make this work.

Is there any way to force the lock-free implementation (I'd need the lock-free version, regardless of performance)?

Upvotes: 1

Views: 831

Answers (2)

T.C.
T.C.

Reputation: 137330

There are two separate things:

  • Manipulation of the reference counter in the control block (or equivalent thing) is typically implemented with lock-free atomics whenever possible. This is not what std::atomic_is_lock_free tells you.

    • libstdc++'s __shared_ptr is templated on the lock policy, so you can explicitly use

      template<typename T>
      using shared_ptr_unsynchronized = std::__shared_ptr<T, __gnu_cxx::_S_single>;
      

      if you know what you are doing.

  • std::atomic_is_lock_free tells you whether the atomic access functions (std::atomic_{store, load, exchange, compare_exchange} etc.) on shared_ptr are lock-free. Those functions are used to concurrently access the same shared_ptr object, and typical implementations will use a mutex.

Upvotes: 11

Mats Petersson
Mats Petersson

Reputation: 129364

If you use shared_ptr in a threaded environment, you NEED to have locks [of some kind - they could be implemented as atomic increment and decrement, but there may be places where a "bigger" lock is required to ensure no races]. The lockless version only works when there is only one thread. If you are not using threads, don't link with -lpthread.

I'm sure there is some tricky way to convince the compiler that you are not REALLY using the threads for your shared pointers, but you are REALLY in fragile territory if you do - what happens if a shared_ptr is passed to a thread? You may be able to guarantee that NOW, but someone will probably accidentally or on purpose introduce one into something that runs in a different thread, and it all breaks.

Upvotes: 2

Related Questions