I'm just wondering, if a thread is in a critical section, can it be preempted?
Thread A: Enter CR
Thread A: Get suspended
Thread B: Wants to enter CR but can't, because Thread A has the lock
If Thread A preempted, and so the mutex lock is stuck with Thread A, what can be done about this?
Suppose thread A is preempted by higher priority thread C. Now suppose thread B is in fact higher priority than C. If B becomes runnable, you have a classic case of priority inversion; Thread B (high priority) is stuck waiting for a resource held by Thread A (low priority). One cure for this is called priority inheritance.
With priority inheritance, when B blocks for the resource held by A (the critical section), thread A temporarily 'inherits' the priority of thread B. This allows A to preempt that bothersome middle-priority thread C, and when A is done with the resource, A goes back to its original priority. This gets A out of B's way, so to speak, eliminating the dead-lock.
Of course it can be preempted. Otherwise how the other threads could try to enter that critical section, if the only thread that is allowed to run within the process is the thread that owns the critical section?
Thread B in your example will wait until thread A is rescheduled and is finished with the crtical section. No surprise here. And if thread A, while in a critical section, also waits for a mutex owned by thead B, then it's a deadlock which you must resolve by revising your logic.
Related
Background:
The Little Book of Semaphores by Allen B. Downey talks about assumptions needed to prevent thread starvation.
He states that the scheduler needs to guarantee the following:
Property 2: if a thread is ready to run, then the time it waits until it runs is bounded.
And a weak semaphore guarantees:
Property 3: if there are threads waiting on a semaphore when a thread executes signal, then one of the waiting threads has to be woken.
However, he states that even with these properties, the following code when run for 3 or more threads (Thread A,B,C) can cause starvation:
while True:
mutex.wait()
# critical section
mutex.signal()
The argument is that if A executes first, then wakes up B, A could wait on the mutex again before B releases it. At this point, the A could be woken up again reacquire the mutex and repeat this cycle with B. C would be starved.
Question:
Wouldn't Property 2 guarantee that C would have to be woken up by the scheduler in some finite amount of time? If so, then Thread C couldn't be starved. Even if weak semaphore does not guarantee that Thread C will be woken up, shouldn't the scheduler run it?
I thought about it a little bit more and realized that Property 2 is guarantees that Threads in a RUNNABLE state will be scheduled in a finite amount of time.
The argument in the book states that Thread C would never get to a RUNNABLE state so Property 2 and 3 do not guarantee no starvation.
I'm motivated with this citation from "Concepts in Programming Languages" by John C. Mitchell:
"Atomicity prevents individual statements of one wait procedure from being
interleaved with individual statements of another wait on the same semaphore."
Wait and signal operations need to be atomic which is often enforced by some "lower" level mechanism of acquiring lock - disabling interrupts, disabling preemption, test and set ... But, conceptually, how these locks can be in some way "private" for each semaphore instance?
In other words, is it allowed for example that one thread acquires lock at the beginning and later be preempted in the middle of executing wait operation on one semaphore, and after that another thread acquires lock at the beginning of wait operation on some other semaphore and enters in the body of its wait operation, so that two thread are in the wait operations on different semaphores at the same time? Or, shortly, whether the wait operations on two different semaphores mutually exclusive?
My point is, if thread acquires lock in wait operation on one semaphore s1, is it allowed for another thread to acquire lock at the same time in wait operation on another semaphore s2? I'm emphasizing that these are two different semaphore instances, not the same one.
For example:
class Semaphore {
...
public:
void wait();
...
}
void Semaphore::wait(){
lock();
//POINT OF CONTINUATION FOR THREAD 2!//
if(--val<0){
//POINT OF PREEMPTION FOR THREAD 1!//
block();
}
unlock();
}
Semaphore s1;
Semaphore s2:
...
So...
Is it allowed at some point of execution that one thread be preempted while executing wait operation on semaphore s1 at //POINT OF PREEMPTION FOR THREAD 1!// , and control transfers to another thread which executes wait operation of semaphore s2 at //POINT OF CONTINUATION FOR THREAD 2!//...
...or...
Is it allowed for instructions of wait operation from one semaphore to be interleaved with instruction of wait operation from another semaphore?
..or...
Is it allowed for more than one threads to be in wait operations on different semaphores at the same time?
Sorry for my wordiness but I really struggle to clarify my question. Thanks in advance.
Yes, it's allowed. One of the reasons you would use two different locks, rather than using the same lock for everything, is to avoid unnecessary dependencies like this.
Is it allowed for instructions of wait operation from one semaphore to be interleaved with instruction of wait operation from another semaphore?
Absolutely.
Is it allowed for more than one threads to be in wait operations on different semaphores at the same time?
Absolutely.
Prohibiting any of these things would hurt performance significantly for no benefit. Contention is the enemy of multi-threaded performance.
Could you have the following scenario in concurrent programs?
suppose a thread acquires a lock to execute a critical section.Then before the critical section is executed the processor preempts the thread. The new thread that comes for execution needs the lock from the old thread (that was preempted). So the current thread can't proceed (hangs until it get preempted). Is there a mechanism in Operating systems to not let threads preempted until the lock is released?
It is possible for a thread holding a mutex to be preempted while executing a critical section. If the thread that the OS switches to tries to acquire that mutex and finds that it is already locked, then that thread should be context switched out immediately. The thread scheduler should be smart enough to not switch back to that thread until it has switched back to the thread holding the mutex and the mutex is released.
If you are writing Kernel code then yes, there are mechanisms for preventing a thread to preempt.
For standard code there is no such thing. Some operations are atomic and are ensured atomic by the compiler and kernel but right after those operations the thread may be preempted and it can remain preempted for an undetermined amount of time (unless the system is a real-time sistem).
Imagine I have a mutex locked. There is unlimited number of other threads waiting to lock the mutex. When I unlock the mutex, one of those threads will be chosen to enter the critical section. However I have no control over which one. What if I want specific thread to enter the critical section?
I am prety sure this cannot be done using POSIX mutex, however, can I emulate the behaviour using different synchronisation object(s)?
You can use a mutex, a condition variable and a thread id to achive that.
Before unlocking the mutex the thread sets the target thread id, broadcasts the condition variable and releases the mutex. The waiting threads wake up, lock the mutex and check whether the target thread id equals to this thread id. If not the thread goes back to wait.
An optimization to this method to avoid waking up all waiting threads just to check the target thread id and then go back to wait would be to use a separate condition variable for each waiting thread. This way the signaling thread would notify the condition variable of the particular target thread.
Another option is to use signals sent to a particular thread. Let's say we use SIGRTMINfor this purpose. First, all threads block this signal at the start, so that the signal becomes pending and doesn't get lost when the thread isn't waiting for it. When a thread wants to lock the mutex it first calls sigwait() which atomically unblocks SIGRTMIN and waits for it or delivers an already pending one. Once the thread received the signal it can proceed and lock the mutex. The signaling thread uses pthread_kill(target_thread_id, SIGRTMIN) to wake up a particular thread.
Does semaphore satisfies bounded waiting or they are just for providing mutual exclusion??
Answer
It may break bounded waiting condition theoretically as you'll see below. Practically, it depends heavily on which scheduling algorithm is used.
The classic implementation of wait() and signal() primitive is as:
//primitive
wait(semaphore* S)
{
S->value--;
if (S->value < 0)
{
add this process to S->list;
block();
}
}
//primitive
signal(semaphore* S)
{
S->value++;
if (S->value <= 0)
{
remove a process P from S->list;
wakeup(P);
}
}
When a process calls the wait() and fails the "if" test, it will put itself into a waiting list. If more than one processe are blocked on the same semaphore, they're all put into this list(or they are somehow linked together as you can imagine). When another process leaves critical section and calls signal(), one process in the waiting list will be chosen to wake up, ready to compete for CPU again. However, it's the scheduler who decides which process to pick from the waiting list. If the scheduling is implemented in a LIFO(last in first out) manner for instance, it's possible that some process are starved.
Example
T1: thread 1 calls wait(), enters critical section
T2: thread 2 calls wait(), blocked in waiting list
T3: thread 3 calls wait(), blocked in waiting list
T4: thread 1 leaves critical section, calls signal()
T5: scheduler wakes up thread 3
T6: thread 3 enters critical section
T7: thread 4 calls wait(), blocked in waiting list
T8: thread 3 leaves critical section, calls signal()
T9: scheduler wakes up thread 4
..
As you can see, although you implements/uses the semaphore correctly, thread 2 has a unbounded waiting time, even possibly starvation, caused by continuous entering of new processes.