Does a thread own a mutex after pthread_cond_timedwait times out? - multithreading

After a thread calls pthread_cond_timedwait, and it returns ETIMEDOUT, does the thread own the mutex?
I would initially think NO, but it appears that we must call pthread_mutex_unlock even after pthread_cond_timedwait returns ETIMEDOUT.
The documentation says:
Upon successful return, the mutex shall have been locked and shall be owned by the calling thread.
So, upon nonsuccessful return (return value != 0), the mutex is NOT owned, I would think.
But, if we do not call pthread_mutex_unlock after ETIMEDOUT, the mutex appears to be in a broken state (ie I cannot get another thread to acquire it, it just stalls).
The documentation also hints at this as well, as they always unlock the mutex regardless of the return value of pthread_cond_timedwait:
(void) pthread_mutex_lock(&t.mn);
t.waiters++;
clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec += 5;
rc = 0;
while (! mypredicate(&t) && rc == 0)
rc = pthread_cond_timedwait(&t.cond, &t.mn, &ts);
t.waiters--;
if (rc == 0) setmystate(&t);
(void) pthread_mutex_unlock(&t.mn);
So, does the thread ALWAYS acquire the mutex after pthread_cond_timedwait? It does not really make sense because the call would have to block more than the specificed time in order to acquire the mutex again.

You are looking at an older issue of POSIX. Issue 7 has this clarified text:
When such timeouts occur, pthread_cond_timedwait() shall nonetheless release and re-acquire the mutex referenced by mutex, and may consume a condition signal directed concurrently at the condition variable.
If it didn't reacquire the mutex in this case you'd have to re-acquire it in the calling code anyway so you could re-test the condition after a timeout, because you might have consumed a condition signal. It's only if the condition you're waiting for hasn't occurred and a timeout occurred that you should treat it as the timeout case.
The timeout isn't guarding against the mutex being held for overly long, it's guarding against a condition signal not arriving in a timely manner (generally, the mutex should only be held for short, relatively deterministic periods whereas the condition being waited for might be influenced by external inputs).

Related

why does std::condition_variable::wait need mutex?

TL;DR
Why does std::condition_variable::wait needs a mutex as one of its variables?
Answer 1
You may look a the documentation and quote that:
wait... Atomically releases lock
But that's not a real reason. That's just validate my question even more: why does it need it in the first place?
Answer 2
predicate is most likely query the state of a shared resource and it must be lock guarded.
OK. fair.
Two questions here
Is it always true that predicate query the state of a shared resource? I assume yes. I t doesn't make sense to me to implement it otherwise
What if I do not pass any predicate (it is optional)?
Using predicate - lock makes sense
int i = 0;
void waits()
{
std::unique_lock<std::mutex> lk(cv_m);
cv.wait(lk, []{return i == 1;});
std::cout << i;
}
Not Using predicate - why can't we lock after the wait?
int i = 0;
void waits()
{
cv.wait(lk);
std::unique_lock<std::mutex> lk(cv_m);
std::cout << i;
}
Notes
I know that there are no harmful implications to this practice. I just don't know how to explain to my self why it was design this way?
Question
If predicate is optional and is not passed to wait, why do we need the lock?
When using a condition variable to wait for a condition, a thread performs the following sequence of steps:
It determines that the condition is not currently true.
It starts waiting for some other thread to make the condition true. This is the wait call.
For example, the condition might be that a queue has elements in it, and a thread might see that the queue is empty and wait for another thread to put things in the queue.
If another thread were to intercede between these two steps, it could make the condition true and notify on the condition variable before the first thread actually starts waiting. In this case, the waiting thread would not receive the notification, and it might never stop waiting.
The purpose of requiring the lock to be held is to prevent other threads from interceding like this. Additionally, the lock must be unlocked to allow other threads to do whatever we're waiting for, but it can't happen before the wait call because of the notify-before-wait problem, and it can't happen after the wait call because we can't do anything while we're waiting. It has to be part of the wait call, so wait has to know about the lock.
Now, you might look at the notify_* methods and notice that those methods don't require the lock to be held, so there's nothing actually stopping another thread from notifying between steps 1 and 2. However, a thread calling notify_* is supposed to hold the lock while performing whatever action it does to make the condition true, which is usually enough protection.
TL;DR
If predicate is optional and is not passed to wait, why do we need the lock?
condition_variable is designed to wait for a certain condition to come true, not to wait just for a notification. So to "catch" the "moment" when the condition becomes true you need to check the condition and wait for the notification. And to avoid a race condition you need those two to be a single atomic operation.
Purpose Of condition_variable:
Enable a program to implement this: do some action when a condition C holds.
Intended Protocol:
Condition producer changes state of the world from !C to C.
Condition consumer waits for C to happen and takes the action while/after condition C holds.
Simplification:
For simplicity (to limit number of cases to think of) let's assume that C never switches back to !C. Let's also forget about spurious wakeups. Even with this assumptions we'll see that the lock is necessary.
Naive Approach:
Let's have two threads with an essential code summarized like this:
void producer() {
_condition = true;
_condition_variable.notify_all();
}
void consumer() {
if (!_condition) {
_condition_variable.wait();
}
action();
}
The Problem:
The problem here is a race condition. A problematic interleaving of the threads is following:
The consumer reads condition, checks it to be false and decides to wait.
A thread scheduler interrupts consumer and resumes producer.
The producer updates condition to become true and invokes notify_all().
The consumer is resumed.
The consumer actually does wait(), but is never notified and waken up (a liveness hazard).
So without locking the consumer may miss the event of the condition becoming true.
Solution:
Disclaimer: this code still does not handle spurious wakeups and possibility of condition becoming false again.
void producer() {
{ std::unique_lock<std::mutex> l(_mutex);
_condition = true;
}
_condition_variable.notify_all();
}
void consumer() {
{ std::unique_lock<std::mutex> l(_mutex);
if (!_condition) {
_condition_variable.wait(l);
}
}
action();
}
Here we check condition, release lock and start waiting as a single atomic operation, preventing the race condition mentioned before.
See Also
Why Lock condition await must hold the lock
You need a std::unique_lock when using std::condition_variable for the same reason you need a std::FILE* when using std::fwrite and for the same reason a BasicLockable is necessary when using std::unique_lock itself.
The feature std::fwrite gives you, entire the reason it exists, is to write to files. So you have to give it a file. The feature std::unique_lock provides you is RAII locking and unlocking of a mutex (or another BasicLockable, like std::shared_mutex, etc.) so you have to give it something to lock and unlock.
The feature std::condition_variable provides, the entire reason it exists, is the atomically waiting and unlocking a lock (and completing a wait and locking). So you have to give it something to lock.
Why would someone want that is a separate question that has been discussed already. For example:
When is a condition variable needed, isn't a mutex enough?
Conditional Variable vs Semaphore
Advantages of using condition variables over mutex
And so on.
As has been explained, the pred parameter is optional, but having some sort of a predicate and testing it isn't. Or, in other words, not having a predicate doesn't make any sense inn a manner similar to how having a condition variable without a lock doesn't making any sense.
The reason you have a lock is because you have shared state you need to protect from simultaneous access. Some function of that shared state is the predicate.
If you don't have a predicate and you don't have a lock you really don't need a condition variable just like if you don't have a file you really don't need fwrite.
A final point is that the second code snippet you wrote is very broken. Obviously it won't compile as you define the lock after you try to pass it as an argument to condition_variable::wait(). You probably meant something like:
std::mutex mtx_cv;
std::condition_variable cv;
...
{
std::unique_lock<std::mutex> lk(mtx_cv);
cv.wait(lk);
lk.lock(); // throws std::system_error with an error code of std::errc::resource_deadlock_would_occur
}
The reason this is wrong is very simple. condition_variable::wait's effects are (from [thread.condition.condvar]):
Effects:
— Atomically calls lock.unlock() and blocks on *this.
— When unblocked, calls lock.lock() (possibly blocking on the lock), then returns.
— The function will unblock when signaled by a call to notify_one() or a call to notify_all(), or spuriously
After the return from wait() the lock is locked, and unique_lock::lock() throws an exception if it has already locked the mutex it wraps ([thread.lock.unique.locking]).
Again, why would someone want coupling waiting and locking the way std::condition_variable does is a separate question, but given that it does - you cannot, by definition, lock a std::condition_variable's std::unique_lock after std::condition_variable::wait has returned.
It's not stated in the documentation (and could be implemented differently) but conceptually you can imagine the condition variable has another mutex to both protect its own data but also coordinate the condition, waiting and notification with modification of the consumer code data (e.g. queue.size()) affecting the test.
So when you call wait(...) the following (logically) happens.
Precondition: The consumer code holds the lock (CCL) controlling the consumer condition data (CCD).
The condition is checked, if true, execution in the consumer code continues still holding the lock.
If false, it first acquires its own lock (CVL), adds the current thread to the waiting thread collection releases the consumer lock and puts itself to waiting and releases its own lock (CVL).
That final step is tricky because it needs to sleep the thread and release the CVL at the same time or in that order or in a way that threads notified just before going to wait are able to (somehow) not go to wait.
The step of acquiring the CVL before releasing the CCD is key. Any parallel thread trying to update the CCD and notify will be blocked either by the CCL or CVL. If the CCL was released before acquiring the CVL a parallel thread could acquire the CCL, change the data and then notify before the the to-be-waiting thread is added to the waiters.
A parallel thread acquires the CCL, modifies the data to make the condition true (or at least worth testing) and then notifies. Notification acquires the the CVL and identifies a blocked thread (or threads) if any to unwait. The unwaited threads then seek to acquire the CCL and may block there but won't leave wait and re-perform the test until they've acquired it.
Notification must acquire the CVL to make sure threads that have found the test false have been added to the waiters.
It's OK (possibly preferable for performance) to notify without holding the CCL because the hand-off between the CCL and CVL in the wait code is ensuring the ordering.
It may be preferrable because notifying when holding the CCL may mean all the unwaited threads just unwait to block (on the CCL) while the thread modifying the data is still holding the lock.
Notice that even if the CCD is atomic you must modify it holding the CCL or that Lock CVL, unlock CCL step won't ensure the total ordering required to make sure notifications aren't sent when threads are in the process of going to wait.
The standard only talks about atomicity of operations and another implementation may have a way of blocking notification before completing the 'add to waiters' step has completed following a failed test. The C++ Standard is careful to not dictate an implementation.
In all that, to answer some of the specific questions.
Must the state be shared? Sort of. There could be an external condition like a file being in a directory and the wait is timed to re-try after a time-period. You can decide for yourself whether you consider the file system or even just the wall-clock to be shared state.
Must there be any state? Not necessarily. A thread can wait on notification.
That could be tricky to coordinate because there has to be enough sequencing to stop the other thread notifying out of turn. The commonest solution is to have some boolean flag set by the notifying thread so the notified thread knows if it missed it. The normal use of void wait(std::unique_lock<std::mutex>& lk) is when the predicate is checked outside:
std::unique_lock<std::mutex> ulk(ccd_mutex)
while(!condition){
cv.wait(ulk);
}
Where the notifying thread uses:
{
std::lock_guard<std::mutex> guard(ccd_mutex);
condition=true;
}
cv.notify();
The reason is that in some times the waiting-thread holds the m_mutex:
#include <mutex>
#include <condition_variable>
void CMyClass::MyFunc()
{
std::unique_lock<std::mutex> guard(m_mutex);
// do something (on the protected resource)
m_condiotion.wait(guard, [this]() {return !m_bSpuriousWake; });
// do something else (on the protected resource)
guard.unluck();
// do something else than else
}
and a thread should never go to sleep while holding a m_mutex. One doesn't want to lock everybody out, while sleeping. So, atomically: {guard is unlocked and the thread go to sleep}. Once it waked up by the other-thread (m_condiotion.notify_one(), let's say) guard is locked again, and then the thread continue.
Reference (video)
Because if not so, there's a race condition before the waiting thread noticing the change of the shared state and the wait() call.
Assume we got a shared state of type std::atomic state_, there's still a fair chance for the waiting thread to miss a notification:
T1(waiting) | T2(notification)
---------------------------------------------- * ---------------------------
1) for (int i = state_; i != 0; i = state_) { |
2) | state_ = 0;
3) | cv.notify();
4) cv.wait(); |
5) }
6) // go on with the satisfied condition... |
Note that the wait() call failed to notice the latest value of state_ and may keep waiting forever.

Posix Thread Synchronization Primitives: pthread_cond_signal() and pthread_cond_wait()

I was writing a multithreading code using pthread_cond in conjuction with mutexes, which made me wonder:
is the signal one time, so if the signal is sent before the other thread is waiting for it, the other thread will keep waiting indefinitely?
Since cond_wait() unlocks the mutex, is it a thumb rule to write this statement JUST before mutex_unlock(), (I realise this makes the latter redundant, but I do that just for clarity) or are there many scenarios where you would want to write the function outside the mutex lock?
Make this your mantra:
Only ever wait for something ...
Waiting should almost always look like this:
if (pthread_mutex_lock(...) != 0) {
/* something terrible happened, panic */
}
while (test-condition) {
pthread_cond_wait(...)
}
pthread_mutex_unlock(...)
If the exclusive check of test-condition fails, and so a context enters pthread_cond_wait the associated mutex will be atomically unlocked.
This means another context can enter code that looks like:
if (pthread_mutex_lock(...) != 0) {
/* panic */
}
test-condition = false;
pthread_cond_signal(...);
pthread_mutex_unlock(...);
Changing the predicate and atomically waking the first context that is in the call to pthread_cond_wait, which in turn checks the predicate test-condition and can jump past the loop.
If we just look at the waiting code again:
if (pthread_mutex_lock(...) != 0) {
/* something terrible happened, panic */
}
while (test-condition) {
pthread_cond_wait(...)
}
pthread_mutex_unlock(...)
Between the call to wait and unlock, there is always exclusivity; Either because the mutex was acquired exclusively (the predicated wait loop was not entered), or because before returning from a call topthread_cond_wait the mutex was re-acquired atomically.
Synchronization is hard to get right, and is costly for a multi-threaded application; One should attempt to keep critical sections simple to squeeze the margins for error to their minimum size.
Another important thing to do is check the return values of all these pthread_* calls; The return value is important information about state that you always need to know, and nearly always need to act upon.
Some useful man pages (for return values):
pthread_mutex_lock
pthread_cond_wait
pthread_cond_signal

How to come out of a deadlock in linux

On a multi threading system, if two threads want to work on a shared memory after locking a mutex.
Thread A:
pthread_mutex_lock(&mutex)
....... //Memory corruption or Assert and thread exits
pthread_mutex_unlock(&mutex)
Thread B:
pthread_mutex_lock(&mutex)
.......
pthread_mutex_unlock(&mutex)
If Thread A acquires the mutex first and exits due to memory corruption or assert, Thread B will be waiting forever causing a deadlock.
Is there a way/method i can use to come out from this kind of deadlock, once it happened?
Is there any other safer method similar to mutex that I can use?
You can set the ROBUST attribute on a mutex. With a robust mutex, if the thread that acquired it exits for some reason without unlocking it, the mutex enters a special state where the next thread that attempts to lock it will get EOWNERDEAD.
It is then the responsibility of that thread to cleanup any inconsistent state. If recovery is possible, the thread shall call pthread_mutex_consistent(3) any time before pthread_mutex_unlock(3), so that the other threads can use it as before. If recovery is not possible, the mutex should be unlocked without calling pthread_mutex_consistent(3), causing it to enter an unusable state where the only permissible operation is to destroy it.
Note that the mutex is locked even if EOWNERDEAD was returned (I think it's the only condition under which pthread_mutex_lock(3) returns with an error but locks the mutex).
To set the ROBUST attribute, use pthread_mutexattr_setrobust(3) after initializing the mutex attributes instance. Remember that this must be done before initializing the mutex. So, something like:
pthread_mutex_t mutex;
pthread_mutexattr_t mutex_attrs;
if (pthread_mutexattr_init(&mutex_attrs) != 0) {
/* Handle error... */
}
if (pthread_mutexattr_setrobust(&mutex_attrs, PTHREAD_MUTEX_ROBUST) != 0) {
/* Handle error... */
}
if (pthread_mutex_init(&mutex, &mutex_attrs) != 0) {
/* Handle error... */
}
Then you can use it like:
int lock_res = pthread_mutex_lock(&mutex);
if (lock_res == EOWNERDEAD) {
/* Someone died before unlocking the mutex
* We assume there's no cleanup to do
*/
if (pthread_mutex_consistent(&mutex) != 0) {
/* Handle error... */
}
} else if (lock_res != 0) {
/* Some other error, handle it here */
}
/* mutex is locked here, do stuff... */
if (pthread_mutex_unlock(&mutex) != 0) {
/* Handle error */
}
For more info you can see the manpage for pthread_mutex_consistent(3) and pthread_mutex_getrobust(3) / pthread_mutex_setrobust(3)
If you want threads to clean up after themselves, you generally need to ask for it or do it yourself.
You can for example use pthread_cleanup_push() and pthread_cleanup_pop() to ensure cleanup happens if the thread exits using pthread_exit() or if it is cancelled (by way of pthread_cancel()).
The downsides of this API are that each pair of calls must be in the same function and in the same lexical nesting level (e.g. In your thread's entry function).
I can't tell if you're allowed to cancel a thread from within a signal handler, so you might not be able to handle this using that API, and these types of errors and assertions typically make the entire process exit (Although, presumably if you're implementing your own assert variant, it could call pthread_exit()).
There do exist other lock types that unlock automatically (e.g. flock() locks, or robust mutexes as shown by Filipe Gonçalves) when the owner dies, but you have little guarantee of state consistency in cases where someone lost a lock and will need to clean up after them

When is a condition variable needed, isn't a mutex enough?

I'm sure mutex isn't enough that's the reason the concept of condition variables exist; but it beats me and I'm not able to convince myself with a concrete scenario when a condition variable is essential.
Differences between Conditional variables, Mutexes and Locks question's accepted answer says that a condition variable is a
lock with a "signaling" mechanism. It is used when threads need to
wait for a resource to become available. A thread can "wait" on a CV
and then the resource producer can "signal" the variable, in which
case the threads who wait for the CV get notified and can continue
execution
Where I get confused is that, a thread can wait on a mutex too, and when it gets signalled, is simply means the variable is now available, why would I need a condition variable?
P.S.: Also, a mutex is required to guard the condition variable anyway, when makes my vision more askew towards not seeing condition variable's purpose.
Even though you can use them in the way you describe, mutexes weren't designed for use as a notification/synchronization mechanism. They are meant to provide mutually exclusive access to a shared resource. Using mutexes to signal a condition is awkward and I suppose would look something like this (where Thread1 is signaled by Thread2):
Thread1:
while(1) {
lock(mutex); // Blocks waiting for notification from Thread2
... // do work after notification is received
unlock(mutex); // Tells Thread2 we are done
}
Thread2:
while(1) {
... // do the work that precedes notification
unlock(mutex); // unblocks Thread1
lock(mutex); // lock the mutex so Thread1 will block again
}
There are several problems with this:
Thread2 cannot continue to "do the work that precedes notification" until Thread1 has finished with "work after notification". With this design, Thread2 is not even necessary, that is, why not move "work that precedes" and "work after notification" into the same thread since only one can run at a given time!
If Thread2 is not able to preempt Thread1, Thread1 will immediately re-lock the mutex when it repeats the while(1) loop and Thread1 will go about doing the "work after notification" even though there was no notification. This means you must somehow guarantee that Thread2 will lock the mutex before Thread1 does. How do you do that? Maybe force a schedule event by sleeping or by some other OS-specific means but even this is not guaranteed to work depending on timing, your OS, and the scheduling algorithm.
These two problems aren't minor, in fact, they are both major design flaws and latent bugs. The origin of both of these problems is the requirement that a mutex is locked and unlocked within the same thread. So how do you avoid the above problems? Use condition variables!
BTW, if your synchronization needs are really simple, you could use a plain old semaphore which avoids the additional complexity of condition variables.
Mutex is for exclusive access of shared resources, while conditional variable is about waiting for a condition to be true. They are tw different types of kernel resource. Some people might think they can implement conditional variable by themselves with mutex, a common pattern is "flag + mutex":
lock(mutex)
while (!flag) {
sleep(100);
}
unlock(mutex)
do_something_on_flag_set();
but it doesn't work, because you never release the mutex during the wait, no one else can set the flag in a thread-safe way. This is why we need kernel support for conditional variables, so when you're waiting on a condition variable, the associated mutex is not hold by your thread until it's signaled.
I was thinking about this too and the most important information which I think was missing everywhere is that mutex can be owned (or changed) by only one thread at a time. So if you have one producer and more consumers, the producer would have to wait on mutex to produce. With cond. variable it can produce at any time.
You need condition variables, to be used with a mutex (each cond.var. belongs to a mutex) to signal changing states (conditions) from one thread to another one. The idea is that a thread can wait till some condition becomes true. Such conditions are program specific (i.e. "queue is empty", "matrix is big", "some resource is almost exhausted", "some computation step has finished" etc). A mutex might have several related condition variables. And you need condition variables because such conditions may not always be expressed as simply as "a mutex is locked" (so you need to broadcast changes in conditions to other threads).
Read some good posix thread tutorials, e.g. this tutorial or that or that one. Better yet, read a good pthread book. See this question.
Also read Advanced Unix Programming and Advanced Linux Programming
P.S. Parallelism and threads are difficult concepts to grasp. Take time to read and experiment and read again.
The conditional var and the mutex pair can be replaced by a binary semaphore and mutex pair. The sequence of operations of a consumer thread when using the conditional var + mutex is:
Lock the mutex
Wait on the conditional var
Process
Unlock the mutex
The producer thread sequence of operations is
Lock the mutex
Signal the conditional var
Unlock the mutex
The corresponding consumer thread sequence when using the sema+mutex pair is
Wait on the binary sema
Lock the mutex
Check for the expected condition
If the condition is true, process.
Unlock the mutex
If the condition check in the step 3 was false, go back to the step 1.
The sequence for the producer thread is:
Lock the mutex
Post the binary sema
Unlock the mutex
As you can see the unconditional processing in the step 3 when using the conditional var is replaced by the conditional processing in the step 3 and step 4 when using the binary sema.
The reason is that when using sema+mutex, in a race condition, another consumer thread may sneak in between the step 1 and 2 and process/nullify the condition. This won't happen when using conditional var. When using the conditional var, the condition is guarantied to be true after the step 2.
The binary semaphore can be replaced with the regular counting semaphore. This may result in the step 6 to step 1 loop a few more times.
Slowjelj is right, but to shed some light on the problem, look at the python code below. We have a buffer, a producer, and a consumer. And think if you could rewrite it just with mutexes.
import threading, time, random
cv = threading.Condition()
buffer = []
MAX = 3
def put(value):
cv.acquire()
while len(buffer) == MAX:
cv.wait()
buffer.append(value)
print("added value ", value, "length =", len(buffer))
cv.notify()
cv.release()
def get():
cv.acquire()
while len(buffer) == 0:
cv.wait()
value = buffer.pop()
print("removed value ", value, "length =", len(buffer))
cv.notify()
cv.release()
def producer():
while True:
put(0) # it doesn't mater what is the value in our example
time.sleep(random.random()/10)
def consumer():
while True:
get()
time.sleep(random.random()/10)
if __name__ == '__main__':
cs = threading.Thread(target=consumer)
pd = threading.Thread(target=producer)
cs.start()
pd.start()
cs.join()
pd.join()
I think it is implementation defined.
The mutex is enough or not depends on whether you regard the mutex as a mechanism for critical sections or something more.
As mentioned in http://en.cppreference.com/w/cpp/thread/mutex/unlock,
The mutex must be locked by the current thread of execution, otherwise, the behavior is undefined.
which means a thread could only unlock a mutex which was locked/owned by itself in C++.
But in other programming languages, you might be able to share a mutex between processes.
So distinguishing the two concepts may be just performance considerations, a complex ownership identification or inter-process sharing are not worthy for simple applications.
For example, you may fix #slowjelj's case with an additional mutex (it might be an incorrect fix):
Thread1:
lock(mutex0);
while(1) {
lock(mutex0); // Blocks waiting for notification from Thread2
... // do work after notification is received
unlock(mutex1); // Tells Thread2 we are done
}
Thread2:
while(1) {
lock(mutex1); // lock the mutex so Thread1 will block again
... // do the work that precedes notification
unlock(mutex0); // unblocks Thread1
}
But your program will complain that you have triggered an assertion left by the compiler (e.g. "unlock of unowned mutex" in Visual Studio 2015).

How does Wait/Signal (semaphore) implementation pseudo-code "work"?

Wait(semaphore sem) {
DISABLE_INTS
sem.val--
if (sem.val < 0){
add thread to sem.L
block(thread)
}
ENABLE_INTS
Signal(semaphore sem){
DISABLE_INTS
sem.val++
if (sem.val <= 0) {
th = remove next
thread from sem.L
wakeup(th)
}
ENABLE_INTS
If block(thread) stops a thread from executing, how, where, and when does it return?
Which thread enables interrupts following the Wait()?
the thread that called block() shouldn’t return until another thread has called wakeup(thread)!
but how does that other thread get to run?
where exactly does the thread switch occur?
block(thread) works that way:
Enables interrupts
Uses some kind of waiting mechanism (provided by the operating system or the busy waiting in the simplest case) to wait until the wakeup(thread) on this thread is called. This means that in this point thread yields its time to the scheduler.
Disables interrupts and returns.
Yes, UP and DOWN are mostly useful when called from different threads, but it is not impossible that you call these with one thread - if you start semaphore with a value > 0, then the same thread can entry the critical section and execute both DOWN (before) and UP (after). Value which initializes the semaphore tells how many threads can enter the critical section at once, which might be 1 (mutex) or any other positive number.
How are the threads created? That is not shown on the lecture slide, because that is only a principle how semaphore works using a pseudocode. But it is a completely different story how you use those semaphores in your application.

Resources