We have 3 tasks running at different priorities: A (120), B (110), C (100).
A takes a mutex semaphore with the Inversion Safe flag.
Task B does a semTake, which causes Task A's priority to be elevated to 110.
Later, task C does a semTake. Task A's priority is now 100.
At this point, A releases the semaphore and C grabs it.
We notice that A's priority did not go back down to its original priority of 120.
Shouldn't A's priority be restored right away?
Ideally, when the inherited priority level is
lowered, it will be done in a step-wise fashion. As each
dependency that caused the priority level to be bumped up is removed,
the inherited priority level should drop down to the priority level of
the highest remaining dependency.
For Example:
task A (100 bumped up to 80) has two mutexes (X & Y)
that tasks B (pri 90) and task C (pri 80) are respectively pending
for. When task A gives up mutex Y to task C, we might expect that its
priority will drop to 90. When it finally gives up mutex X to task B,
we would expect its priority level to drop back to 100.
Priority inheritance does not work that way in VxWorks.
How it works depends on the version of VxWorks you are using.
pre-VxWorks 6.0
The priority level remains "bumped up" until the task that has the
lock on the mutex semaphore gives up its last inversion safe mutex
semaphore.
Using the example from above, when task A gives up mutex Y
to task C, its priority remains at 80. After it gives up mutex X to
task B, then its priority will drop back to 100 (skipping 90).
Let's throw curve ball #1 into the mix. What if task A had a lock on mutex
Z while all this was going on, but no one was pending on Z? In that
case, the priority level will remain at 80 until Z is given up--then
it will drop back to 100.
Why do it this way?
It's simple, and for most cases, it is good
enough. However, it does mean that when "curve ball #1" comes into
play, the priority will remain higher for a longer period of time than
is necessary.
VxWorks 6.0+
The priority level now
remains elevated until the task that has the lock on the mutex
semaphore gives up its last inversion safe mutex that contributed to
raising the priority level.
This improvement avoids the problem of
"curve ball #1". It does have its own limitations. For example, if
task B and/or task C time(s) out while waiting for task A to give up
the semaphores, task A's priority level does not get recalculated
until it gives up the semaphore.
Related
I have 12 resources (r_1, r_2, ..., r_12), and 12 corresponding locks (l_1, l_2, ..., l_12) that my threads try to access. Each thread needs a specific sequence of resources to operate on. For example, thread 1 needs r_1, r_3, and r_5. Thread 2 needs r_1, r_7, r_8, r_10.
Now what I've basically done is ordered the resources from 1 to 12, make each thread lock its required resources in this order (ascending order), then when the thread is done, I unlock them in the reverse order (descending order) to maintain an order.
So my question is, am I preventing a deadlock in this case? Or can there happen a deadlock?
TL;DR: Yes, this system is totally immune to deadlock. At any point in time, the thread holding the highest-numbered lock must be able to make progress, since it cannot be waiting to acquire any locks held by other processes. More formally, your conditions ensure a total ordering on lock acquisition by all processes, which in turn ensures that circular wait can never occur. Circular wait is a necessary precondition for deadlock.
Detail: In order for deadlock to take place, all four of the following conditions must apply (see relevant Wikipedia):
Mutual exclusion - i.e. concurrent processes are accessing unsharable resources. Locks are unsharable by definition (they are also called mutexes for this reason).
Hold and wait - at least one process is attempting to access multiple resources, and it does so by holding some of them and then waiting for the others. This condition probably applies in your case, depending on the exact semantics of your program.
No preemption - it is not possible for processes to have their resources taken from them by other processes. Once again, this is a property of the locks we're using.
Circular wait - there is a cycle of processes, each waiting on a resource held by the next. This condition doesn't apply here. Consider a thread A, waiting on accessing a lock L_i. That lock must be held by a thread B which has already obtained all the locks it requires from indices 1 to i. As a result, B cannot be waiting on A. Similarly, any thread that B is waiting on in order to acquire its next lock L_j (where j > i by the order in which locks are acquired) cannot be waiting on any locks with indices 1 to j. By induction, there can be no cycles of dependency in this system.
In concurrent programming, it is typical for the first three cases to be set by the context in which you are developing (which concurrency primitives are being used etc.), whereas the last can occasionallyâ„¢ be avoided by cleverness.
I'm having some problems with FreeRTOS binary mutex. On my aplication there are multiple threads (tasks) with the same priority, two of these access to file I/O functions that are within mutex take and mutex release.
Depending on some timings one task is doing starvation to the other. Is that possible?
FreeRTOS take into account how many time is a task waiting for a resource?
Thanks
Are you using the mutex in tight loops in multiple tasks? If so then there is a logical reason why one task might hold the mutex for longer than you think. If tasks A and B have the same priority, A is holding the mutex and B is waiting for the mutex, then a context switch won't occur when A gives the mutex back because B has the same priority as A (it would occur if B had a higher priority, but it would be a breach of the scheduling algorithm and risk task thrashing if a task switch occurred to an equal priority task). There, if A is in a loop, gives the mutex back, then immediately takes it again, every time B attempts to take the mutex it will find A is still holding the mutex so, if B is also in a loop, it will block on the mutex again. This scenario is easy to fix - but suggest you read the chapter that describes this in the freely available book: http://www.freertos.org/Documentation/RTOS_book.html
Scheduling Scheme : Preemptive Priority Scheduling
Situation :
A process L (Low priority) acquires a spinlock on a resource (R). While still in the Critical Section, L gets preempted because of the arrival of another process - H (Higher Priority) into the ready queue. .
However, H also needs to access resource R and so tries to acquire a spin lock which results in H going to busy wait. Because spinlocks are used, H never actually goes into Wait and will always be in Running state or Ready state (in case an even higher priority process arrives ready queue), preventing L or any process with a priority lower than H from ever executing.
A) All processes with priority less than H can be considered to be under Starvation
B) All processes with priority less than H as well as the process H, can be considered to be in a deadlock. [But, then don't the processes have to be in Wait state for the system to be considered to be in a deadlock?]
C) All processes with priority less than H as well as the process H, can be considered to be in a livelock.[But, then only the state of H changes constantly, all the low priority process remain in just the Ready state. Don't the state of all processes need to change (as part of a spin lock) continuously if the system in livelock?]
D) H alone can be considered to be in livelock, all lower priority processes are just under starvation, not in livelock.
E) H will not progress, but cannot be considered to be in livelock. All lower priority processes are just under starvation, not in livelock.
Which of the above statements are correct? Can you explain?
This is not a livelock, because definition of livelock requires that "states of the processes involved in the livelock constantly change with regard to one another", and here states effectively do not change.
The first process can be considered to be in processor starvation, because if there were additional processor, it could run on it and eventually release the lock and let the second processor to run.
The situation also can be considered as a deadlock, with 2 resources in the resource graph, and 2 processes attempting to acquire that resources in opposite directions: first process owns the lock and needs the processor to proceed, while the second process owns the processor and needs the lock to proceed.
As known, priority inversion problem - when a thread with higher priority waits for a thread with lower-priority: https://en.wikipedia.org/wiki/Priority_inversion
It happen when we have 3 threads: L (low), M (medium), H (high-priority). L and H use the same mutex, but L acquire it early than H, and H blocked and goes to sleep. And then M occupies CPU-core because has higher priority than L, and L goes to sleep, but mutex still acquired. L & H are sleeping, but M is working.
There are some solutions of priority inversion:
Disabling all interrupts to protect critical sections
A priority ceiling
Priority inheritance
Random boosting - Ready tasks holding locks are randomly boosted in priority until they exit the critical section. This solution is used in Microsoft Windows.
Avoid blocking
Does Linux use some of the solutions of priority inversion, and which of its?
Linux uses Priority inheritance.
Priority inversion can be solved by using function int pthread_mutexattr_setprotocol(pthread_mutexattr_t *attr, int protocol);
Where protocol is:
PTHREAD_PRIO_NONE - nothing going on when acquires mutex ownership
PTHREAD_PRIO_INHERIT - Priority inheritance
PTHREAD_PRIO_PROTECT - uses a fixed level of the priority in accordance with prioceiling value which got by function pthread_mutexattr_getprioceiling()
When a thread owns one or more mutexes initialized with the
PTHREAD_PRIO_PROTECT protocol, it shall execute at the higher of its
priority or the highest of the priority ceilings of all the mutexes
owned by this thread and initialized with this attribute, regardless
of whether other threads are blocked on any of these mutexes or not.
In RR scheduling policy what will happen if a low priority thread locks a mutex and is removed by the scheduler because another high priority thread is waiting?
Will it also releases the lock held by low priority thread?
For example consider 3 threads running in a process with priorities 10,20 and 30 in RR scheduling policy.
Now at a given point of time low priority thread 1 locks mutex and is still doing execution mean while the high priority thread pops in and also waits on mutex held by thread 1. Now thread 2 comes in to picture which also needs the same mutex locked by thread 1.
As far as I know as per scheduling algorithm the threads sleeping or waiting for mutex,semaphore etc are removed and the other ones, even having low priority are allowed to execute. Is this correct? If so, in the above example ultimately high priority threads wait for the completion of low priority thread which doesn't make any sense.
Is this how the system works if at all threads are designed like I said above?
or
the thread priority should be set in such a way that high priority one's will not depend on low priority one's mutexe's ?
Also can anyone please explain me how scheduling works at process level? How do we set priority for a process?
Typically, scheduling and locks are unrelated in any other aspect than a "waiting thread is not scheduled until it's finished waiting". It would be rather daft to have a MUTEX that "stops other thread from accessing my data" but it only works if the other thread has the same or lower priority than the current thread.
The phenomena of "a low priority holds a lock that a high priority thread 'needs'" is called priority inversion, and it's a well known scenario in computer theory.
There are some schemes that "temporarily increase priority of a lock-holding thread until it releases the lock to the highest priority of the waiting threads" (or the priority of the first waiting thread if it's higher than the current thread, or some other variation on that theme). This is done to combat priority inversion - but it has other drawbacks too, so it's not implemented in all OS's/schedulers (after all, it affects other threads than the one that is waiting too).
Edit:
The point of a mutex (or other similar locks) is that it prevents two threads from accessing the same resources at once. For example, say we want to update five different variables with some pretty lengthy processing (complicated math, fetching data from a serial port or a network drive, or some such), but if we only do two of the variables, some other process using these would get an invalid result, then we clearly can't "let go" of the lock.
The high priority thread simply has to wait for all five variables to be updated and the low priority lock.
There is no simple workaround that the application can do to "fix" this problem - don't hold the locks more than necessary of course [and it may be that we can actually fix the problem described above, by performing the lengthy processing OUTSIDE of the lock, and only do the final "store it in the 5 variables" with the lock on. This would reduce the potential time that the high priority thread has to wait, but if the system is really busy, it won't really fix the problem.
There are whole PhD thesis written on this subject, and I'm not a specialist on "how to write a scheduler" - I have a fair idea how one works, just like I know how the engine in my car works - but if someone gave me a bunch of suitable basic shapes of steel and aluminium, and the required tools/workspace and told me to build an engine, I doubt it would work great...Same with a scheduler - I know what the parts are called, but not how to build one.
If a high priority thread needs to wait on a low priority thread (mutex semaphore etc), the low priority thread is temporarily elevated to the same priority as the high priority thread.
The high priority thread is not going to have the lock for which it is requesting until the low priority thread will unlock it.
To avoid this we can use semaphore where any other thread can initiate to unlock but in mutex it is not possible.