Question

Could someone explain to me why there will be a deadlock when one thread tries to grab same spinlock twice ? (Assuming that spinlock is non-recursive)

Regular spinlock usage:

lock = false; 
while(acquiring_lock(lock)) {} //sit in the loop and wait for lock to become available
....critical section....
release_lock(lock);  

However, I don't understand why second call to acquiring_lock(lock) would cause deadlock ?

Was it helpful?

Solution

It seems like this question really depends on threading library in question, as well as the specification of the different functions. So, let's consider a few possibilities.

Locking mechanisms may be blocking or nonblocking as well as re-entrant and not re-entrant.

/** 
* This function performs a busy wait, blocking until it acquires the lock
* This is not re-entrant.
**/
void blocking_acquire_lock(bool thingToLock);

If you try using the preceding function, your code will deadlock. This is because the thread will not continue execution until it has acquired the lock. You would not use this in the condition position of a while loop, since it does not return a bool. Secondly, It is not re-entrant meaning if you attempt to re-acquire the lock after having acquired it even with the same thread, it will continue to wait for the lock to be released.

/** 
* This function performs a non-blocking busy wait, blocking for up to X milleseconds, 
* until it acquires the lock. This is not re-entrant.
* returns true if lock acquired, false if lock not acquired
**/
bool non_blocking_acquire_lock(bool thingToLock, int timeoutInMilliseconds);

This version makes some sense to use in a while loop. You can try to get acquire a lock, but if it does not have success within an allotted time, you can decide what to do. Perhaps, you will decide to work on something else for a while and then re-try to acquire the lock.

Since it is not re-entrant, it will not allow the same thread to acquire it twice, without first releasing it. Due to this, your code will deadlock.

Here is one last example which will seem to deadlock on your code.

/** 
* This function performs a non-blocking busy wait, blocking for up to X milleseconds, 
* until it acquires the lock. This is re-entrant.
* returns true if lock acquired, false if lock not acquired
**/
bool non_blocking_reentrant_acquire_lock(bool thingToLock, int timeoutInMilliseconds);

This lock is re-entrant, so if a thread owns the lock, it can re-acquire it. However, if you use this inside of a while loop like the following, you will notice something interesting.

my_lock = false; 
while(acquiring_lock(my_lock, 1000)) { ; }
    ....critical section....
release_lock(lock);  

Since the lock returns true if it acquires the lock within 1 second, and this is the only thread, it should likely acquire the lock without problem. It returns true. In which case, it will stay in the while loop. The while loop is empty, so it immediately tries re-acquiring the lock.

Since it is re-entrant, I would expect it to continually succeed. It will continue to successfully re-acquire the lock, and never get past the while loop.

I would expect the following code with an extra "!" to be what you intended.

my_lock = false; 
while( !acquiring_lock(my_lock, 1000)) { ; }
    ....critical section....
release_lock(lock);  

This version, will actually stop trying to acquire the lock, i.e. exit the while loop, once it has acquired the lock.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top