Test and set lock(TSL) are deadlock free? - operating-system

The concept of priority inversion is a case when TSL cannot be deadlock free then how one can say the TSL is deadlock free?

Lets first look at the definition of Deadlock.
Definition :- A set of processes are said to be in Deadlock, if they wait for happening of event caused by other processes in the same set. Here the processes in the set are in waiting/blocked state.
Now lets understand the Priority Inversion. Lets process A is a lower priority process running in critical section. and process B be higher priority process.
When the process B goes to ready state,it gets scheduled (goes to running state) and process A gets pre-empted by schedular as it has low priority. Here Process A goes to waiting/blocked state but it has already locked the critical section and process B is in running state which then goes in Busy waiting because the critical section is already locked by process A !!. This causes an infinite waiting. This case is basically called as SpinLock since process B is in running state and process A is in waiting/blocked state but according to definition of Deadlock all process should be in waiting/blocked state.
This is why TSL is said to be Deadlock free but its NOT free from SpinLock.
I hope this will be helpful :)

Related

What happens to a process and/or thread while it waits on a mutex?

When a process and/or thread waits on mutex in which state the process and/or thread is? Is it in WAIT or READY or some other state? I tried to search the answer over the web but could not find a clear, definitive answer, maybe there isn't one or maybe there is, to find out that I am posting this question here.
tl;dr: Nothing happens when it is waiting; it is simply a kernel data structure.
Without loss of generality, all operating systems have some sort of model where a unit of execution (task) moves between the states : Ready, Running, Waiting. This Task has a data structure associated with it, where its state and registers (among other things) are recorded.
When a task moves from Ready to Running, its saved registers are loaded on a cpu, and it continues execution from its last saved state. Initially, its saved registers are set to reasonable values for the program to start.
From Running to Waiting or Ready, its registers are stored in its task data structure, and this structure is placed on either a list of Ready or Waiting tasks.
From Waiting to Ready, the task data structure is removed from the Waiting list and appended to the Ready list.
When a task tries to acquire a mutex that is unavailable, it moves from Running (how else could it try to get the mutex) to Waiting. If the mutex was available, it remains Running, and the mutex becomes unavailable.
When a task releases a mutex, and another task is Waiting for that mutex, the Waiting task becomes Ready, and acquires the mutex. If many tasks are Waiting, one is chosen to acquire the mutex and become Ready, the rest remain Waiting.
This is a very abstract description; real systems are complicated by both a plurality of synchronization mechanisms (mailbox, queue, semaphore, pipe, ...), a desire to optimise the various paths, and the utilization of multiple CPUs.

How scheduler knows a Task is in blocking state?

I am reading "Embedded Software Primer" by David E.Simon.
In it discusses RTOS and its building blocks Scheduler and Task. It says each Task is either in Ready State, Running State, or Blocking State. My question is how the scheduler determines a Task is in Blocking State? Assume it's waiting for a Semaphore. Then it likely Semaphore is in a state it can't return. Does Scheduler see if a function does not return, then mark its state as Blocking?
The implementation details will vary by RTOS. Generally, each task has a state variable that identifies whether the task is ready, running, or blocked. The scheduler simply reads the task's state variable to determine whether the task is blocked.
Each task has a set of parameters that determine the state and context of the task. These parameters are often stored in a struct and called the "task control block" (although the implementation varies by RTOS). The ready/run/block state variable may be a part of the task control block.
When the task attempts to get the semaphore and the semaphore is not available then the task will be set to the blocked state. More specifically, the semaphore-get function will change the task from running to blocked. And then the scheduler will be called to determine which task should run next. The scheduler will read through the task state variables and will not run those tasks that are blocked.
When another task eventually sets the semaphore then the task that is blocked on the semaphore will be changed from the blocked to the ready state and the scheduler may be called to determine if a context switch should occur.
As I'm writing a RTOS ( http://distortos.org/ ), I thought that I may chime in.
The variable which holds the state of each thread is indeed usually implemented in RTOSes, and this includes mine version:
https://github.com/DISTORTEC/distortos/blob/master/include/distortos/ThreadState.hpp#L26
https://github.com/DISTORTEC/distortos/blob/master/include/distortos/internal/scheduler/ThreadControlBlock.hpp#L329
However this variable usually is used only as a debugging aid or for additional checks (like preventing you from starting a thread that is already started).
In RTOSes targeted at deeply embedded systems the distinction between ready/blocked is usually made using the containers that hold the threads. Usually the threads are "chained" in linked lists, usually also sorted by priority and insertion time. The scheduler has its own list of threads that are "ready" ( https://github.com/DISTORTEC/distortos/blob/master/include/distortos/internal/scheduler/Scheduler.hpp#L340 ). Each synchronization object (like a semaphore) also has its own list of threads which are "blocked" waiting for this object ( https://github.com/DISTORTEC/distortos/blob/master/include/distortos/Semaphore.hpp#L244 ) . When a thread attempts to use a semaphore that is currently not available, it is simply moved from the scheduler's "ready" list to semaphores's "blocked" list ( https://github.com/DISTORTEC/distortos/blob/master/source/synchronization/Semaphore.cpp#L82 ). The scheduler doesn't need to decide anything, as now - from scheduler's perspective - this thread is just gone. When this semaphore is now released by another thread, first thread which was waiting on this semaphore's "blocked" list is moved back to scheduler's "ready" list ( https://github.com/DISTORTEC/distortos/blob/master/source/synchronization/Semaphore.cpp#L39 ).
Usually there's no need to make special distinction between threads that are ready and the thread that is actually running. As the amount of threads that can actually run is fixed and equal to the number of available CPU cores, then all you need is a pointer for each CPU core which points to the thread from the "ready" list which is running at that core at that moment. In my system I do the same - the thread that is at the head of the "ready" list is the one that is running, but I also manage an iterator which points to that thread ( https://github.com/DISTORTEC/distortos/blob/master/include/distortos/internal/scheduler/Scheduler.hpp#L337 ). You could have a separate list for running threads, but in most cases it would be a waste of space (there's usually just one) and makes other things slightly more complicated.
I've actually wrote an article about thread states and their transitions if you're interested - http://distortos.org/documentation/task-states/ This article has no special distinction between the thread that is "ready" and the one that is actually running. I don't consider this distinction to be actually useful for anything, as long as you have other means to tell which of the "ready" threads is running.

Process state kept in PCB and thread state kept in TCB

Assume process p0 has been created. P0 has two threads (main thread) t0 and t1. So somewhere there is PCB0 for p0, TCB0 for t0 and TCB1 for t1.
(5 execution states for simplicity: created, ready, running, blocked, terminated)
The p0 is in running state when at least one of it's threads is in running state?
For sure in TCB there must be a field for thread state. If the p0 state can be directly calculated from the state of it's threads: Is there a field of execution state in PCB and why?
If the answer in 2 is yes: Each time a thread state is changing do we have to check the state of all process' threads in order to keep the PCB.state up to date? This isn't redundant?
The p0 is in running state when at least one of it's threads is in
running state?
Not everytime. Process state and thread state can be different. So it is not necessary when thread is in running p0 remains in running it can be in block or ready state. Some of the possible situation between thread and process :
The process is in ready state, one of the process’s threads is in running state.
The process is blocked in blocked state, one of the process’s threads is in running state.
e.g. when threaded application starts as a process containing single thread . At that time process is in running state. Then application uses user level thread library to create additional threads. Now when thread A begins to run it will enter the running state.Suppose Thread A make an I/O system call . In this situation control is transferred to the kernel which blocks the process .In this case thread A is still in running , even though the process is in blocked state. This is one of the example when process is blocked but thread is running.
For sure in TCB there must be a field for thread state.
yes there is field for state(e.g running , terminate , ready , block etc ) in tcb.
Is there a field of execution state in PCB and why?
Yes there is field of state in PCB too. But it is not calculated from tcb state because as I told both state can be different. Thread will generally call different procedures and thus a different execution history.
Hope you satisfy with my answer . Good luck :-)
---> Check this link for reference
---> check link for content of TCB and PCB

Starvation and Deadlock (Operating System)

I know deadlock and starvation definitions, but I'm still confused in these few points (Unable to arrive at which one is correct)
a) deadlock is a extreme case of starvation
b) deadlock and starvation are two unrelated concepts
c) starvation only leads to deadlock
Deadlock: is when all the processes do not get access to resources because every process is waiting for some another process and there is a cycle.
Starvation: is when a low priority process does not get access to the resources it needs because there is a high priority process accessing the resources. The entire system of processes hasn't come to halt in this case.
Since, only low priority process does not have access to resources in starvation, while in deadlock no process has access to the resources they need therefore deadlock is an extreme case of starvation with the criterion of extremeness being the total count of process unable to access the resource.
Deadlock and starvation are related as both are the cases of a process not having access to the resource.
Starvation does not lead to deadlock as a starving low priority process keeps waiting while other processes with high priority run to completion.
Rumor has it that when they shut down the IBM 7094 at MIT in 1973, they found a low-priority process that had been submitted in 1967 and had not yet been run.‡
‡Mentioned in the Operating System Concepts book by Abraham Silberschatz, Peter B. Galvin, Greg Gagne
Well, a is correct.
Starvation can lead to softlock or suboptimal performance (scheduling).
Since deadlock is a special case of starvation (all contenders are resource-starved) they are /not/ unrelated.
DeadLock : If two threads are waiting for each other and forever,such type of infinite waiting is called deadlock.Long waiting of a thread where waiting never ends is also called as deadlock.
Starvation : Long waiting of a thread where waiting ends at a certain point is called as deadlock.
For example low priority thread has to wait until completing all high priority threads,it may be long waiting but ends at certain point,is nothing but starvation.

What is priority inversion?

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.