Desperado17
Desperado17

Reputation: 1015

std::shared_ptr::unique(), copying and thread safety

I have a shared_ptr stored in a central place which can be accessed by multiple threads through a method getPointer(). I want to make sure that only one thread uses the pointer at one time. Thus, whenever a thread wants to get the pointer I test if the central copy is the only one via std::shared_ptr::unique() method. If it returns yes, I return the copy assuming that unique()==false as long as that thread works on the copy. Other threads trying to access the pointer at the same time receive a nullptr and have to try again in the future.

Now my question:

Is it theoretically possible that two different threads calling getPointer() can get mutual access to the pointer despite the mutex guard and the testing via unique() ?

std::shared_ptr<int> myPointer; // my pointer is initialized somewhere else but before the first call to getPointer()
std::mutex myMutex;

std::shared_ptr<int> getPointer()
{
    std::lock_guard<std::mutex> guard(myMutex);
    std::shared_ptr<int> returnValue;

    if ( myPointer.unique() )
        returnValue = myPointer;
    else
        returnValue = nullptr;

    return returnValue;
}

Regards

Upvotes: 1

Views: 301

Answers (2)

xaxxon
xaxxon

Reputation: 19781

Only one "active" copy can exist at a time.

It is protected by the mutex until after a second shared_ptr is created at which point a subsequent call (once it gets the mutex after the first call has exited) will fail the unique test until the initial caller's returned shared_ptr is destroyed.

As noted in the comments, unique is going away in c++20, but you can test use_count == 1 instead, as that is what unique does.

Upvotes: 2

Solomon Slow
Solomon Slow

Reputation: 27190

Your solution seems overly complicated. It exploits the internal workings of the shared pointer to deduce a flag value. Why not just make the flag explicit?

std::shared_ptr<int> myPointer;
std::mutex myMutex;
bool myPointerIsInUse = false;

bool GetPermissionToUseMyPointer() {
    std::lock_guard<std::mutex guard(myMutex);
    auto r = (! myPointerIsInUse);
    myPointerIsInUse ||= myPointerIsInUse;
    return r;
}

bool RelinquishPermissionToUseMyPointer() {
    std::lock_guard<std::mutex guard(myMutex);
    myPointerIsInUse = false;
}

P.S., If you wrap that in a class with a few extra bells and whistles, it'll start to look a lot like a semaphore.

Upvotes: -1

Related Questions