Reputation: 7873
I have am reviewing a colleague's Visual Studio 2008 C++03 application application and I've come across an implementation of a thread synchronization primitive (below).
Assuming SyncObject
is implemented correctly, is the use of a boolean in the below code to know if the resource is locked or unlocked thread-safe? If no, can you walk through a "ThreadA" does this and "ThreadB" does that situation so I understand your logic?
class CMyLock
{
public:
CMyLock(SyncObject* object)
: object_(object), acquired_(false)
{
};
// return true if the resource is locked within the given timeout period
bool Lock(DWORD dwTimeOut = INFINITE)
{
acquired_ = object_->Lock(dwTimeOut);
return acquired_;
};
// return true if the resource is unlocked
bool Unlock()
{
if (acquired_)
acquired_ = !object_->Unlock();
return !acquired_;
};
// return true if the resource is locked
bool IsLocked() { return acquired_; };
private:
bool acquired_;
// some thread synchronization primitive
SyncObject* object_;
};
Upvotes: 2
Views: 277
Reputation: 101506
I see several serious problems with this code. In a code review, I would reject it.
It is not clear what this class is intended to be. It might be a thin proxy to a primitive. It might be an automatic locker. In either case, the design is wrong, and the documentation (none) does not elaborate.
It does not use RAII. This is a good idea no matter what this object is intended to be, but in the case of an automatic locker it is especially important.
It retains it's own state, which could be out of sync with other instances in the same thread. If, for example, you create 2 instances of this object on thread A, set one to locked
and check the state of the other one, it should say locked
but it won't.
Possibly most importantly, it is re-inventing the wheel at best.
Upvotes: 2
Reputation: 10828
no it's not - at least from what I can see. What may happen is that one thread calls lock and gets the lock and another thread accesses m_bAcquired before it's updated by the thread the causes the lock.
That's why you need a lock for reading as well asl Matthieu M. stated.
A: Lock after m_pObject is locked but before m_bAcquired is set B: IsLocked --> returns false A - still in Lock: m_pObject = true
so B has false information.
Other problem: Unlock relies on m_bAcquired.
I think this object is meant to be used from within one single thread. So each thread has its own CSingleLock instance but they all use the same SyncObject. In this case, only SyncObject needs to be thread safe and it works.
Upvotes: 1
Reputation: 6566
It is not threadsafe.
directly after m_pObject->Unlock()
returns, another thread waiting on m_pObject->Lock(dwTimeOut)
can return and set m_bAcquired
to true, then the unlocking thread sets m_bAcquired
to false and overwrites the locked state incorrectly (IsLocked will return false while the object is locked).
Upvotes: 2
Reputation: 300419
Short answer: no
You need to lock for reading too, or you risk seeing a stale state.
Upvotes: 1