3

I'm trying to provoke Priority Inversion on a small C++ program for demonstration purposes but I can't: The low priority thread that holds the mutex is not preempted and keeps running on the critical section. This is what I'm doing:

// let's declare a global mutex
pthread_mutex_t my_mutex;
  ...

int main(int argc, char **argv) {
  ...
  pthread_t normal_thread;
  pthread_t prio_thread;

  pthread_mutexattr_t attr;
  pthread_mutexattr_init (&attr);
  pthread_mutexattr_setprotocol (&attr, PTHREAD_PRIO_NONE);  // ! None !
  pthread_mutex_init(&my_mutex, &attr);

  // create first normal thread (L):
  pthread_create(&normal_thread, NULL, the_locking_start_routine, NULL);

  // just to help the normal thread enter in the critical section
  sleep(2);

  // now will launch:
  // * (M) several CPU intensive SCHED_FIFO threads with priority < 99
  // * (H) one SCHED_FIFO thread that will try to lock the mutex, with priority < 99

  // build Real Time attributes for the Real Time threads:
  pthread_attr_t my_rt_att;
  pthread_attr_init(&my_rt_att);

  // it was missing in the original post and it was also wrong:
  // even setting the SchedPolicy you have to set "InheritSched"
  pthread_attr_setinheritsched(&my_rt_att, PTHREAD_EXPLICIT_SCHED)

  pthread_attr_setschedpolicy(&my_rt_att, SCHED_FIFO);
  struct sched_param params;

  params.sched_priority = 1;
  pthread_attr_setschedparam(&my_rt_att, &params);

  pthread_create(&prio_thread, &my_rt_att, the_CPU_intensive_start_routine, NULL) 

  params.sched_priority = 99;
  pthread_attr_setschedparam(&my_rt_att, &params);

  // create one RealTime thread like this:
  pthread_create(&prio_thread, &my_rt_att, the_locking_start_routine, NULL)  //coma was missing

  ...
}

void *the_locking_start_routine(void *arg) {
  ...
  pthread_mutex_lock(&my_mutex);
  // This thread is on the critical section
  // ... (skipped)
  pthread_mutex_unlock(&my_mutex);
  ...
}

... But it doesn't work, I can't have my desired Priority Inversion.

This is what happens:

As I understand, with a scheduller like Linux's CFS, a non-real time thread (SCHED_OTHER) will not run until there isn't any Real Time thread (SCHED_FIFO or SCHED_RR) in runnning state. But I have achieved this threads running simultaneously:

  • (L) One non-real time (SCHED_OTHER) thread locking the mutex and consuming CPU
  • (M) several Real Time threads (SCHED_FIFO , & priority > 0) CPU intensive and non-waiting to lock the mutex
  • (H) One Real Time thread (SCHED_FIFO , & highest priority ) waiting for the lock

There are more Real Time CPU intensive threads (M) running than the amount of CPUs of my system ... but the non-real time thread holding (L) the lock is still consuming CPU and finishes it's work and releases the mutex before the "M" threads finish consuming CPU.

Why isn't the low priority thread preempted, the application dead-locked and I can't get priority inversion?

I'm using g++ 4.5.2 on a Ubuntu Desktop 11.04 with kernel 2.6.38-13.

user1284631
  • 4,446
  • 36
  • 61
Angel
  • 940
  • 1
  • 9
  • 21
  • Please show all the code for the_start_routine. I can't see what it is doing between the lock and unlock. Also outside the Critical Section - presumably there is a loop somewhere that you have turned into dots? If it's inside the CS, why would it be preempted there - it's the only thread ready/running since all the others are stuck on the mutex. – Martin James Mar 19 '12 at 23:22
  • MOVED BELOW TO ANSWER (whether is or not) Most modern schedulers have anti-deadlock safeguards that will change the priority for a time slice or two to prevent priority inversion leading to deadlocks, when detected or as deemed appropriate. Whether linux does with whichever scheduler you are using with it, I don't know for sure. However, do be aware of this, if you aren't already. – dyasta Mar 20 '12 at 15:17
  • Hi @Angel, why did you set PTHREAD_PRIO_NONE instead of PTHREAD_PRIO_INHERIT ? – ransh Oct 03 '15 at 20:20
  • Hi, @ransh! I wanted to reproduce a Priority Inversion scenario first, and to do it the "L" thread must be a non-real-time thread, so I needed "PTHREAD_PRIO_NONE". You certainly know that PTHREAD_PRIO_INHERIT helps you to avoid Priority Inversion, achieving that "L" inherits "H"'s priority, but this is what you need if you want to solve the problem and I first wanted to reproduce it. My problem was not on the program, it was on the global limit on how much time realtime scheduling may use, like Kaz pointed. Cheers! /Angel – Angel Oct 05 '15 at 05:44

3 Answers3

5

Re: I'm trying to provoke Priority Inversion on a small C++ program for demonstration purposes but I can't: The low priority thread that holds the mutex is not preempted and keeps running ...

That is the beginning of a priority inversion scenario. A low-priority thread grabs an exclusive resource (e.g. mutex) on which high priority threads then block.

To properly show the consequences of priority inversion, you require, for example, three threads: a low (L), middle (M) and high (H) priority thread.

L locks a mutex, for which H contends. So L is running, H is not. This is bad already: important thread H is waiting for less important thread L to do something.

Now M becomes runnable and is compute-intensive. M doesn't care about the mutex; it is not related to H or L. But M has a higher priority than L and kicks L off the CPU.

So now M continues to execute, preventing L from running. That prevents L from reaching the line of code where it releases the mutex, and that prevents H from getting the mutex.

So a middle priority thread M is running instead of the highest priority thread H.

By blocking L, M is able to block H also: inversion.

See if you can code it up exactly like this.

Kaz
  • 55,781
  • 9
  • 100
  • 149
  • And you can see how various algorithms deal with this. For instance prirority inheritance: when H wants the mutex, the current mutex owner is given a temporary priority boost so that it is at least equal to H. This causes M not to be able to preempt L. – Kaz Mar 20 '12 at 00:22
  • I have updated the question, the source code is "approximate". The summary is : I can't make the Real Time (M) threads preempt the (L) non-realtime thread, they just add load ... and finally the mutex (L) is freed normaly and (H) finally can lock the mutex. Any help would be very appreciated. Best regards! – Angel Mar 20 '12 at 11:07
3
  1. Are you running the program as root?

  2. What are your values of these sysctl parameters? Here are mine from an Ubuntu box. The default is to give real time only 0.95 seconds out of a 1 second slice:

    kernel.sched_rt_period_us = 1000000
    kernel.sched_rt_runtime_us = 950000
    

    This prevents the real-time domain from taking all the CPU. If you want real real time, you have to disable these parameters.

See: http://www.kernel.org/doc/Documentation/scheduler/sched-rt-group.txt

If you set sched_rt_runtime_us to -1, you disable this safety mechanism.

Kaz
  • 55,781
  • 9
  • 100
  • 149
  • Really thank you, Jeremy Collake and Kaz. I was wondering if there could be happening something like this. I had been able to have a non-RT thread consumming some CPU cycles whilst there were running more RealTime threads than the number of cores I have. There could just be some mechanisms like this, but I didn't even knew where to begin looking for it. You rule!!! Thanks a lot!!! ;) – Angel Mar 21 '12 at 07:41
  • 2
    Hi again! In the original post there were also other error : I didn't called pthread_attr_setinheritsched to set PTHREAD_EXPLICIT_SCHED and it's essential for it to have effect. I tested it with "kernel.sched_rt_runtime_us = -1" on /etc/sysctl.conf and I had my desired Inversion of Priority and today I will sleep happier ;) Thanks again you all. /Angel – Angel Mar 22 '12 at 21:34
1

Most modern schedulers have anti-deadlock safeguards that will change the priority for a time slice or two to prevent priority inversion leading to deadlocks, when detected or as deemed appropriate. Whether linux does with whichever scheduler you are using with it, I don't know for sure. However, do be aware of this, if you aren't already.

dyasta
  • 2,056
  • 17
  • 23