I've encountered the dining savages problem and solution, but I don't seem to understad how the solution is handling the situation when a savage tries to eat from the pot while it's still empty.
The problem and solution from the little book of semaphores:
A tribe of savages eats communal dinners from a large pot that can
hold M servings of stewed missionary. When a savage wants to eat, he
helps himself from the pot, unless it is empty. If the pot is empty,
the savage wakes up the cook and then waits until the cook has
refilled the pot.
Any number of savage threads run the following Unsynchronized savage code:
while True :
getServingFromPot()
eat()
And one cook thread runs this Unsynchronized cook code:
while True:
putServingsInPot(M)
The synchronization constraints are:
Savages cannot invoke getServingFromPot if the pot is empty.
The cook can invoke putServingsInPot only if the pot is empty.
Puzzle: Add code for the savages and the cook that satisfies the
synchronization
Solution (cook):
while True:
emptyPot.wait()
putServingsInPot(M)
fullPot.signal()
Solution (savages):
while True:
mutex.wait()
if servings == 0:
emptyPot.signal ()
fullPot.wait ()
servings = M
servings -= 1
getServingFromPot ()
mutex.signal()
eat()
My question - when we first reach servings = 0, after M savages ate form the pot, we get to emptyPot.signal() and then fullPot.wait() - Both semaphores; then, from my point of view there might be a corner case where we may get to getServingsFromPot before the cook has the chance to get context and fill the pot. Am I missing something?
I'm assuming that signal and wait are the usual V and P semaphore operations.
I think you might be missing the point of each semaphore.
emptyPot: cook will not fill the pot unless a savage has signaled that the pot it empty;
fullPot: the savage who signaled that the pot is empty will not get a serving unless the cook has signaled that the pot is full;
mutex: a savage who wants to eat will not proceed unless another savage has signaled that they're done serving themselves.
Therefore:
multiple savages can be eating at the same time;
no two savages will try to serve themselves at the same time;
a savage will not try to serve themselves if the pot is empty;
after waking up the cook, a savage will not try to serve themselves unless the pot is full.
Thus, there can be no race condition. Due to calling fullPot.wait(), a savage cannot get to getServingsFromPot until the cook has woken up, filled the pot and called fullPot.signal().
Related
I've been recently taught, the concept of monitors. My prof, said "only one thread per time can be in the monitor". I am not sure I get this that's why I did my research. Wikipaideia states what i wrote as a title. My book states though, that inside monitor there are queues, for threads that are waiting , until some defined condition is met. What really confused me is a pseudocode we were given as solution to the bounded buffer problem with monitors.
My question is : If a process is not stopped by a wait() inside the monitor, does monitor structure guaruntee us that it will be permitted to execute the whole method without being interrupted by a context switch or just that, while it is executing the method nobody else produce or consumer is executing their according method?? .
Because in this, slide:
It seems like we only wake up a consumer if the buffer was empty, and we just produced an item.
Everytime a producer that reaches that part of code, has produced an item. Why don't we signal everytime? I supposed that , we (may) consider that: if the buffer wasn't empty, then they may be "active" consumers waiting, to be resumed because they were interrupted by a context switch, but then I thought to myself is this possible? Is it possible to be interrupted inside a method (not because you are "waited") but by a context switch?
My program has many parallel processes each on their own queue. I'd like to be able to visualize/measure the back pressure of the queues.
One approach is to count every block that enters and exits, but I'm sure GCD has this information already. Is there a better approach to measuring back pressure?
There is no API for querying the number of pending blocks in a GCD queue. That said, since you're asking about a queue that you "own", you can wrap it in a way that lets you keep track of that. For instance, you could make your own wrappers around dispatch_[a]sync that would increment a counter whenever you enqueued a block, and also wrapped the block to decrement the counter when the block completes. This really wouldn't be that hard to implement. (dispatch_queue_get_specific would likely be a good place to start...)
FWIW, a few years back, I would've suggested you use NSOperationQueue, but even NSOperationQueue has deprecated its operationCount property, so it's probably fair to say that Apple is unlikely to provide this functionality going forward, so implementing it yourself is probably your best option, if you really need this functionality.
This is probably not immediately helpful to you, but if you need to measure "back pressure" on GCD queues, you're probably not using them in the right way. If you're enqueuing work items that block during execution, that's bad, and will eventually lead to thread starvation. If you're enqueuing tons of work items that you might want to cancel, say if the user changed screens or something, then you should work on a pattern for cancellable work items (or use NSOperation). I'm struggling to think of a use case that would demand that you be able to measure "back pressure" that couldn't be solved with your own code.
If you want to visualize what is going on, you can use OSLog, post .begin and .end events and watch it in Instruments’ “Points of Interest”. (See WWDC 2019 Getting Started with Instruments.)
So, import os.log:
import os.log
Then create a log:
private let pointsOfInterestLog = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: .pointsOfInterest)
Enqueue 100 tasks:
for i in 0..<100 {
enqueueTask(i) {
print("done \(i)")
}
}
Where the routine posts a .begin event to the points of interest log before dispatching the task, and posts a .end event when the task actually starts, e.g.
func enqueueTask(_ index: Int, completion: #escaping () -> Void) {
let id = OSSignpostID(log: pointsOfInterestLog)
os_signpost(.begin, log: pointsOfInterestLog, name: "backlog", signpostID: id, "queued %d", index)
queue.async {
os_signpost(.end, log: pointsOfInterestLog, name: "backlog", signpostID: id, "started %d", index)
...
completion()
}
}
Then profile the app (with command+i or “Product” » “Profile”) and choose, for example, “Time Profiler” (which includes the “Points of Interest” tool). Start a recording an you will see a visual representation of your backlog:
(I expanded the “Points of Interest” to be big enough to show all 100 backlogged tasks.)
This is one way to visualize your backlog. I must confess that I generally use “Points of Interest” not to show the backlog, but rather to show when these tasks are actually running (i.e. .begin when the dispatched task actually starts running and .end when the dispatched task finishes). Or I use the “thread” view in Instruments to see how my worker threads are being used. Or I use the “CPU” view in Instruments to see how my CPUs are being used.
Regardless, as you can see, Instruments can be used to visualize whatever time ranges you want, in this case, the time between the adding of the task to the queue and when it starts running.
I am trying to better understand a chapter and have been confused about what happens if a thread is in the critical section or is entering the critical section. May someone explain or give me an idea on the process of what the thread undergoes in such circumstances? Thank you.
For an example, let's assume that you have an array, and multiple threads that read and write to the array; and if different threads are reading and writing to the array at the same time they'd see inconsistent data and it'd cause problems. To prevent those problems you protect the array with some kind of lock - before doing anything with the array a thread acquires the array's lock, and when it's finished using the array the thread releases the array's lock.
For example:
acquire_array_lock();
/** Critical section (code that does something with the array) **/
release_array_lock();
There's nothing special about the code in the critical section. It does whatever it was designed to do (maybe sorting the array, maybe adding up all the numbers in the array, maybe displaying the array, etc) using code that's no different to code that you might use to do the same thing in a single-threaded system without locks.
The only special parts are the code to acquire and release the lock.
There are many types of locks (spinlocks, mutexes, semaphores), but they all have the same fundamental principle - when acquiring it you have something (e.g. a variable) to determine if a thread can/can't continue, then either (if the thread can't continue) some kind of waiting or (if the thread can continue) some kind of change to let others know they need to wait; and when releasing you have something to let others know they can stop waiting.
The main difference between different kinds of locks is the implementation details - what kind of data is used to determine if a thread can/can't continue, and how a thread waits.
For the simplest kind of lock (a spinlock) you might just have a single "yes/no" flag, a little bit like this (but not literally like this):
acquire_lock(void) {
while(myLock == 0) {
// do nothing then retry
}
myLock = 1;
}
release_lock(void) {
myLock = 0;
}
However this won't work because two or more threads can see that myLock == 0 at the same time and think they can both continue (and then do the myLock = 1 after it's too late). To fix this you need assembly language or special language support for atomic operations (e.g. a special function for "test and set" or "compare and exchange").
The reason this is called a "spinlock" is that (if a thread needs to wait) it wastes CPU time continually checking ("spinning") to see if it can continue. Instead of doing this (to avoid wasting CPU time), a thread could tell a scheduler not to give it any CPU time until the lock is released; and this is how a mutex works.
Recently I see this problem which is pretty similar to First reader/writer problem.
Implementing an N process barrier using semaphores
I am trying to modify it to made sure that it can be reuse and work correctly.
n = the number of threads
count = 0
mutex = Semaphore(1)
barrier = Semaphore(0)
mutex.wait()
count = count + 1
if (count == n){ barrier.signal()}
mutex.signal()
barrier.wait()
mutex.wait()
count=count-1
barrier.signal()
if(count==0){ barrier.wait()}
mutex.signal()
Is this correct?
I'm wondering if there exist some mistakes I didn't detect.
Your pseudocode correctly returns barrier back to initial state. Insignificant suggestion: replace
barrier.signal()
if(count==0){ barrier.wait()}
with IMHO more readable
if(count!=0){ barrier.signal()} //if anyone left pending barrier, release it
However, there may be pitfalls in the way, barrier is reused. Described barrier has two states:
Stop each thread until all of them are pending.
Resume each thread until all of them are running
There is no protection against mixing them: some threads are being resumed, while other have already hit the first stage again. For example, you have bunch of threads, which do some stuff and then sync up on barrier. Each thread body would be:
while (!exit_condition) {
do_some_stuff();
pend_barrier(); // implementation from current question
}
Programmer expects, that number of calls for do_some_stuff() will be the same for all threads. What may (or may not) happen depending on timing: the first thread released from barrier finishes do_some_stuff() calculations before all threads have left the barrier, so it reentered pending for the second time. As a result he will be released along with other threads in the current barrier release iteration and will have (at least) one more call to do_some_stuff().
I have a processing thread that I use to fill a data buffer. Elsewhere a piece of hardware triggers a callback which reads from this data buffer. The processing thread then kicks in and refills the buffer.
When the buffer fills up I am currently telling the thread to wait by:
while( [self FreeWriteSpace] < mProcessBufferSize && InActive) {
[NSThread sleepForTimeInterval:.0001];
}
However when I profile I am getting a lot of CPU time spent in sleep. Is there a better way to wait? Do I even care if the profiles says time is spent in sleep?
Time spent in sleep is effectively free. In Instruments, look at "running samples" rather than "all samples." But this still isn't an ideal solution.
First, your sleep interval is crazy. Do you really need .1µs granularity? The system almost certainly isn't giving you because the processor isn't that fast. I have to believe you could up this to .1 or .01. But that's still busy-waiting which is not ideal if you can help it.
The better solution is to use an NSCondition. In this thread, wait on the condition, and in your processing thread, trigger the condition when there's room to write.
Do be careful with your naming. Do not name methods with leading caps (that indicates that it's a class name). And avoid accessing ivars directly (InActive) like this. "InActive" is also a very confusing name. Does it mean the system is active (In Active) or not active (inactive). Naming in Objective-C is extremely important. The compiler will not protect you the way it does in C# and C++. Good naming is how you keep your programs working, and many parts of ObjC rely on it.
You may also want to investigate Grand Central Dispatch, which is particularly designed for these kinds of problems. Look at dispatch_async() to run things when new data comes in.
However when I profile I am getting a
lot of CPU time spent in sleep. Is
there a better way to wait? Do I even
care if the profiles says time is
spent in sleep?
Yes -- never, never poll. Polling eats CPU, makes your app less responsive, eats battery, and is an all around waste.
Notify instead.
The easiest way is to use one of the variants of "perform selector on main thread" (see NSThread's documentation). Or dispatch to a queue (including something like dispatch_async(dispatch_get_main_queue(), ^{ ... yo, data be ready ...});).