Definition: Bounded waiting refers to a process P_i that keeps waiting forever to enter critical section (CS) while other processes P_j keep entering CS although P_i has shown interest to enter CS.
Now, I understand why lock variable mechanism is not bounded waiting because if a process entered a non-critical section, then another process might come and take CS, so a process might starve.
Algorithm:
NCS (Non-critical Section)
DISABLE INTERRUPTS
CS
ENABLE INTERRUPTS
NCS
Edit: no more details are given about schedulers, etc. The question is to get a glance whether this satisfies bounded waiting or not.
Question: can you please explain why disabled interrupts synchronization mechanism satisfies bounded waiting please (a process can not starve to enter CS as in lock variable mechanism)?
Your question has two contexts to consider:
Are interrupt handlers entering the Critical Section?
Is there an asynchronous scheduler involved?
No,No
As soon as P_i releases the CS, the release mechanism can accept P_j, thus starvation is averted.
No,Yes
Despite P_j's desire, once interrupts are released, the scheduler could be invoked, and decide that P_j is not to be executed next, so in at least a pathological case, could spend forever trying to enter the CS while others are selected.
Yes,No
As soon as P_i release the interrupt, any pending interrupts will execute immediately. If they are to enter the CS, they will first (otherwise the system has halted [*]), so with the right timing a set of interrupts could keep P_j starved forever.
Yes,Yes
Here, the starvation could happen for either of the reasons No,Yes or Yes,No.
[*] - Interrupts have no a-priori way of deferring work, so any resources required to complete the interrupt handler must be available when the handler runs. The handlers context is effectively nested; and a nested context cannot wait for the completion of its superior context.
Is it guaranteed that by using FCFS scheduling, the 'system' will not be in deadlock?
Thanks in advance!
The four conditions for a deadlock are:
Mutual exclusion: Irrespective of the scheduling algorithm, resources can be possessed by one process without sharing.
Hold and wait: In this condition, processes can wait for other resources while holding onto one resource. This is possible with any scheduling algorithm.
No preemption: FCFS is non-preemptive. That is, processes executing a critical section of their code cannot be forced to stop.
Circular wait: Processes are waiting for another process to release a resource in a circular fashion. This, again, is irrespective of the scheduling algorithm
Hence, FCFS does not guarantee that the system will not be in deadlock. If the four conditions are met, a deadlock will occur.
Deadlocks are caused by resource locking, not scheduling order. FCFS doesn’t guarantee that your threads will always grab resources in sequence, so the answer to your question is no.
Deadlock-
A deadlock is a situation in which two or more competing actions are each waiting for the other to finish, and thus neither ever does.
For deadlock to happen all these four conditions must hold simultaneously
Mutual exclusion
Hold and wait
No preemption
Circular wait
we apply deadlock detection algorithm to check whether the system is in deadlock or not. But if any of the above criterion fails(For example No preemption fails, so some resource is being released) which incur the system to be deadlock free. So what I think, if the deadlock detection algorithm finds the state to be unsafe and all the above four criterion holds true simultaneously then we can say the system is in deadlock.
Unsafe state may or may not lead to deadlock.
But unsafe state with all these 4 conditions holding simultaneously must incur deadlock.
Am I thinking right?
I have another question in my mind. How can we say deadlock has occurred definitely because the next moment some process may release their resources to get rid of deadlock.
Am I thinking right?
Yes, you are correct.
See this link to see why unsafe may not lead to deadlock.
I have another question in my mind. How can we say deadlock has occurred definitely because the next moment some process may release their resources to get rid of deadlock.
Say deadlock has occurred. All processes causing the deadlock are waiting for some resource to be acquired. And because of "No preemption" no such process will get preempted and therefore release resources. Also because of "Hold and wait" property, process needs some more resources to continue but is not going to give up or release whatever it is holding now and will wait till its required resources are met. Once there is deadlock, nothing can happen (there cannot be any progress) until you break one of the above condition. Breaking a condition will make some other process to meet its requirements and ensure progress and completion.
I was reading Critical Section Problem from Operating System Concepts by Peter B. Galvin.
According to it
1) Progress is : If no process is executing in its critical section and some processes wish to enter their critical sections, then only those processes that are not executing in their remainder section can participate in deciding which will enter its critical section next, and this selection cannot be postponed indefinitely.
And
2) Bounded waiting is : There exists a bound, or limit, on the number of times other processes are allowed to enter their critical sections after a process has made request to enter its critical section and before that request is granted.
I am not understanding what the author wants to say in both the cases.
Could you please make me understand by giving a proper example related to this definition.
Thank You.
First, let me introduce some terminology. A critical section (CS) is a sequence of instructions that can be executed by at most one process at the same time. When using critical sections, the code can be broken down into the following sections:
// Some arbitrary code (such as initialization).
EnterCriticalSection(cs);
// The code that constitutes the CS.
// Only one process can be executing this code at the same time.
LeaveCriticalSection(cs);
// Some arbitrary code. This is called the remainder section.
The first section contains some code such as initialization code. We don't have a name for that section. The second section is the code that tries to enter the CS. The third section is the CS itself. The fourth section is the code that leaves the critical section. The fifth and last section is called the remainder section which can contain any code. Note that the CS itself can be different between processes (consider for example a process that that receives requests from a client and insert them in a queue and another process that processes these requests).
To make sure that an implementation of critical sections works properly, there are three conditions that must be satisfied. You mentioned two of them (which I will explain next). The third is mutual exclusion which is obviously vital. It's worth noting that mutual exclusion applies only to the CS and the leave section. However, the other three sections are not exclusive.
The first condition is progress. The purpose of this condition is to make sure that either some process is currently in the CS and doing some work or, if there was at least one process that wants to enter the CS, it will and then do some work. In both cases, some work is getting done and therefore all processes are making progress overall.
Progress: If no process is executing in its critical section and
some processes wish to enter their critical sections, then only those
processes that are not executing in their remainder section can
participate in deciding which will enter its critical section next,
and this selection cannot be postponed indefinitely.
Let's understand this definition sentence by sentence.
If no process is executing in its critical section
If there is a process executing in its critical section (even though not stated explicitly, this includes the leave section as well), then this means that some work is getting done. So we are making progress. Otherwise, if this was not the case...
and some processes wish to enter their critical sections
If no process wants to enter their critical sections, then there is no more work to do. Otherwise, if there is at least one process that wishes to enter its critical section...
then only those processes that are not executing in their remainder section
This means we are talking about those processes that are executing in either of the first two sections (remember, no process is executing in its critical section or the leave section)...
can participate in deciding which will enter its critical section next,
Since there is at least one process that wishes to enter its CS, somehow we must choose one of them to enter its CS. But who's going to make this decision? Those process who already requested permission to enter their critical sections have the right to participate in making this decision. In addition, those processes that may wish to enter their CSs but have not yet requested the permission to do so (this means that they are in executing in the first section) also have the right to participate in making this decision.
and this selection cannot be postponed indefinitely.
This states that it will take a limited amount of time to select a process to enter its CS. In particular, no deadlock or livelock will occur. So after this limited amount of time, a process will enter its CS and do some work, thereby making progress.
Now I will explain the last condition, namely bounded waiting. The purpose of this condition is to make sure that every process gets the chance to actually enter its critical section so that no process starves forever. However, please note that neither this condition nor progress guarantees fairness. An implementation of a CS doesn't have to be fair.
Bounded waiting: There exists a bound, or limit, on the number of
times other processes are allowed to enter their critical sections
after a process has made request to enter its critical section and
before that request is granted.
Let's understand this definition sentence by sentence, starting from the last one.
after a process has made request to enter its critical section and
before that request is granted.
In other words, if there is a process that has requested to enter its CS but has not yet entered it. Let's call this process P.
There exists a bound, or limit, on the number of
times other processes are allowed to enter their critical sections
While P is waiting to enter its CS, other processes may be waiting as well and some process is executing in its CS. When it leaves its CS, some other process has to be selected to enter the CS which may or may not be P. Suppose a process other than P was selected. This situation might happen again and again. That is, other processes are getting the chance to enter their CSs but never P. Note that progress is being made, but by other processes, not by P. The problem is that P is not getting the chance to do any work. To prevent starvation, there must be a guarantee that P will eventually enter its CS. For this to happen, the number of times other processes enter their CSs must be limited. In this case, P will definitely get the chance to enter its CS.
I would like to mention that the definition of a CS can be generalized so that at most N processes are executing in their critical sections where N is any positive integer. There are also variants of reader-writer critical sections.
Mutual exclusion
No two process can be simultaneously present inside critical section at any point in time, only one process can enter into a critical section at any point in time.
Image for Progress:
Progress
No process running outside the critical section should block the other interesting process from entering into a critical section when in fact the critical section is free.
In this image, P1 (which is running outside of critical section )is blocking P2 from entering into the critical section where in fact critical section is free.
Bounded waiting
No process should have to wait forever to enter into the critical section.
there should be a boundary on getting chances to enter into the critical section.
If bounded waiting is not satisfied then there is a possibility of starvation.
Note
No assumption is related to H/W or processing speed.
Overall, a solution to the critical section problem must satisfy three conditions:
Mutual Exclusion: Exclusive access of each process to the shared memory. Only one process can be in it's critical section at any given time.
Progress: If no process is in its critical section, and if one or more threads want to execute their critical section then any one of these threads must be allowed to get into its critical section.
Bounded Waiting: After a process makes a request for getting into its critical section, there is a limit for how many other processes can get into their critical section, before this process's request is granted. So after the limit is reached, system must grant the process permission to get into its critical section. The purpose of this condition is to make sure that every process gets the chance to actually enter its critical section so that no process starves forever.
Requirements to tell synchronisation solution is correct or not
1). Mutual exclusion:-at any point of time only one process should be present inside critical section.
2). Progress:-the process which is outside critical section and who do not want to enter critical section then such process should not stop the other interested process to enter into its critical section. If a process is getting success to stop other interested process then the progress is not guaranteed or else it is guaranteed. Critical section should be free.
3). Bounded waiting:-the waiting time of a process outside a critical section should be Limited.
4). Architectural neutral:-there is no assumption regarding hardware
(Definition in simple words)
Bounded Waiting :- when only single process gets the turn to enter into critical section every time indeed other process are also interested to enter into critical section.
I've heard the phrase 'priority inversion' in reference to development of operating systems.
What exactly is priority inversion?
What is the problem it's meant to solve, and how does it solve it?
Imagine three (3) tasks of different priority: tLow, tMed and tHigh. tLow and tHigh access the same critical resource at different times; tMed does its own thing.
tLow is running, tMed and tHigh are presently blocked (but not in critical section).
tLow comes along and enters the critical section.
tHigh unblocks and since it is the highest priority task in the system, it runs.
tHigh then attempts to enter the critical resource but blocks as tLow is in there.
tMed unblocks and since it is now the highest priority task in the system, it runs.
tHigh can not run until tLow gives up the resource. tLow can not run until tMed blocks or ends. The priority of the tasks has been inverted; tHigh though it has the highest priority is at the bottom of the execution chain.
To "solve" priority inversion, the priority of tLow must be bumped up to be at least as high as tHigh. Some may bump its priority to the highest possible priority level. Just as important as bumping up the priority level of tLow, is dropping the priority level of tLow at the appropriate time(s). Different systems will take different approaches.
When to drop the priority of tLow ...
No other tasks are blocked on any of the resources that tLow has. This may be due to timeouts or the releasing of resources.
No other tasks contributing to the raising the priority level of tLow are blocked on the resources that tLow has. This may be due to timeouts or the releasing of resources.
When there is a change in which tasks are waiting for the resource(s), drop the priority of tLow to match the priority of the highest priority level task blocked on its resource(s).
Method #2 is an improvement over method #1 in that it shortens the length of time that tLow has had its priority level bumped. Note that its priority level stays bumped at tHigh's priority level during this period.
Method #3 allows the priority level of tLow to step down in increments if necessary instead of in one all-or-nothing step.
Different systems will implement different methods depending upon what factors they consider important.
memory footprint
complexity
real time responsiveness
developer knowledge
Hope this helps.
Priority inversion is a problem, not a solution. The typical example is a low priority process acquiring a resource that a high priority process needs, and then being preempted by a medium priority process, so the high priority process is blocked on the resource while the medium priority one finishes (effectively being executed with a lower priority).
A rather famous example was the problem experienced by the Mars Pathfinder rover: http://www.cs.duke.edu/~carla/mars.html, it's a pretty interesting read.
Suppose an application has three threads:
Thread 1 has high priority.
Thread 2 has medium priority.
Thread 3 has low priority.
Let's assume that Thread 1 and Thread 3 share the same critical section code
Thread 1 and thread 2 are sleeping or blocked at the beginning of the example. Thread 3 runs and enters a critical section.
At that moment, thread 2 starts running, preempting thread 3 because thread 2 has a higher priority. So, thread 3 continues to own a critical section.
Later, thread 1 starts running, preempting thread 2. Thread 1 tries to enter the critical section that thread 3 owns, but because it is owned by another thread, thread 1 blocks, waiting for the critical section.
At that point, thread 2 starts running because it has a higher priority than thread 3 and thread 1 is not running. Thread 3 never releases the critical section that thread 1 is waiting for because thread 2 continues to run.
Therefore, the highest-priority thread in the system, thread 1, becomes blocked waiting for lower-priority threads to run.
It is the problem rather than the solution.
It describes the situation that when low-priority threads obtain locks during their work, high-priority threads will have to wait for them to finish (which might take especially long since they are low-priority). The inversion here is that the high-priority thread cannot continue until the low-priority thread does, so in effect it also has low priority now.
A common solution is to have the low-priority threads temporarily inherit the high priority of everyone who is waiting on locks they hold.
[ Assume, Low process = LP, Medium Process = MP, High process = HP ]
LP is executing a critical section. While entering the critical section, LP must have acquired a lock on some object, say OBJ.
LP is now inside the critical section.
Meanwhile, HP is created. Because of higher priority, CPU does a context switch, and HP is now executing (not the same critical section, but some other code). At some point during HP's execution, it needs a lock on the same OBJ (may or may not be on the same critical section), but the lock on OBJ is still held by LP, since it was pre-empted while executing the critical section. LP cannot relinquish now because the process is in READY state, not RUNNING. Now HP is moved to BLOCKED / WAITING state.
Now, MP comes in, and executes its own code. MP does not need a lock on OBJ, so it keeps executing normally. HP waits for LP to release lock, and LP waits for MP to finish executing so that LP can come back to RUNNING state (.. and execute and release lock). Only after LP has released lock can HP come back to READY (and then go to RUNNING by pre-empting the low priority tasks.)
So, effectively it means that until MP finishes, LP cannot execute and hence HP cannot execute. So, it seems like HP is waiting for MP, even though they are not directly related through any OBJ locks. -> Priority Inversion.
A solution to Priority Inversion is Priority Inheritance -
increase the priority of a process (A) to the maximum priority of any
other process waiting for any resource on which A has a resource lock.
Let me make it very simple and clear. (This answer is based on the answers above but presented in crisp way).
Say there is a resource R and 3 processes. L, M, H. where p(L) < p(M) < p(H) (where p(X) is priority of X).
Say
L starts executing first and catch holds on R. (exclusive access to R)
H comes later and also want exclusive access to R and since L is holding it, H has to wait.
M comes after H and it doesn't need R. And since M has got everything it wants to execute it forces L to leave as it has high priority compared to L. But H cannot do this as it has a resource locked by L which it needs for execution.
Now making the problem more clear, actually the M should wait for H to complete as p(H) > p(M) which didn't happen and this itself is the problem. If many processes such as M come along and don't allow the L to execute and release the lock H will never execute. Which can be hazardous in time critical applications
And for solutions refer the above answers :)
Priority inversion is where a lower priority process gets ahold of a resource that a higher priority process needs, preventing the higher priority process from proceeding till the resource is freed.
eg:
FileA needs to be accessed by Proc1 and Proc2.
Proc 1 has a higher priority than Proc2, but Proc2 manages to open FileA first.
Normally Proc1 would run maybe 10 times as often as Proc2, but won't be able to do anything because Proc2 is holding the file.
So what ends up happening is that Proc1 blocks until Proc2 finishes with FileA, essentially their priorities are 'inverted' while Proc2 holds FileA's handle.
As far as 'Solving a problem' goes, priority inversion is a problem in itself if it keeps happening.
The worst case (most operating systems won't let this happen though) is if Proc2 wasn't allowed to run until Proc1 had. This would cause the system to lock as Proc1 would keep getting assigned CPU time, and Proc2 will never get CPU time, so the file will never be released.
Priority inversion occurs as such:
Given processes H, M and L where the names stand for high, medium and low priorities,
only H and L share a common resource.
Say, L acquires the resource first and starts running. Since H also needs that resource, it enters the waiting queue.
M doesn't share the resource and can start to run, hence it does. When L is interrupted by any means, M takes the running state since it has higher priority and it is running on the instant that interrupt happens.
Although H has higher priority than M, since it is on the waiting queue, it cannot acquire the resource, implying a lower priority than even M.
After M finishes, L will again take over CPU causing H to wait the whole time.
Priority Inversion can be avoided if the blocked high priority thread transfers its high priority to the low priority thread that is holding onto the resource.
A scheduling challenge arises when a higher-priority process needs to read or modify kernel data that are currently being accessed by a lower-priority process—or a chain of lower-priority processes. Since kernel data are typically protected with a lock, the higher-priority process will have to wait for a lower-priority one to finish with the resource. The situation becomes more complicated if the lower-priority process is preempted in favor of another process with a higher priority. As an example, assume we have three processes—L, M, and H—whose priorities follow the order L < M < H. Assume that process H requires resource R,which is currently being accessed by process L.Ordinarily,process H would wait for L to finish using resource R. However, now suppose that process M becomes runnable, thereby preempting process L. Indirectly, a process with a lower priority—process M—has affected how long process H must wait for L to relinquish resource R. This problem is known as priority inversion.It occurs only in systems with more than two priorities,so one solution is to have only two priorities.That is insufficient for most general-purpose operating systems, however. Typically these systems solve the problem by implementing a priority-inheritance protocol. According to this protocol, all processes that are accessing resources needed by a higher-priority process inherit the higher priority until they are finished with the resources in question.When they are finished,their priorities revert to their original values. In the example above, a priority-inheritance protocol would allow process L to temporarily inherit the priority of process H,thereby preventing process M from preempting its execution. When process L had finished using resource R,it would relinquish its inherited priority from H and assume its original priority.Because resource R would now be available, process H—not M—would run next.
Reference :ABRAHAM SILBERSCHATZ
Consider a system with two processes,H with high priority and L with low priority. The scheduling rules are such that H is run whenever it is in ready state because of its high priority. At a certain moment, with L in its critical region, H becomes ready to run (e.g., an I/O operation completes). H now begins busy waiting, but since L is never scheduled while H is running, L never gets the chance to leave the critical section. So H loops forever.
This situation is called Priority Inversion. Because higher priority process is waiting on lower priority process.