If a high priority thread and a low priority thread are blocked in pthread_cond_wait(), the low priority thread will not be awakened,… instead the high priority thread checks it’s condition twice.
The following code when run on linux properly wakes up thread b, but when run with the freertos posix porting layer, the two calls to SemaphoreGive are both consumed by the high priority thread a.
thread b is starved and never re-evaluates it’s condition.
#include <stdio.h>
#include <pthread.h>
#include <sched.h>
pthread_cond_t c;
pthread_mutex_t mtx;
volatile int expression_a = 0;
volatile int expression_b = 0;
void *medium_priority_thread_a(void*param)
{
pthread_mutex_lock(&mtx);
while (!expression_a)
{
printf("thread a waiting for expression_a\n");
pthread_cond_wait(&c, &mtx);
}
pthread_mutex_unlock(&mtx);
return NULL;
}
void *low_priority_thread_b(void*param)
{
pthread_mutex_lock(&mtx);
while (!expression_b)
{
printf("thread b waiting for expression_b\n");
pthread_cond_wait(&c, &mtx);
}
pthread_mutex_unlock(&mtx);
printf("thread b proceeding\n");
return NULL;
}
void *high_priority_thread_c(void*param)
{
// wait for thread a and thread b to be stuck on the condition.
MSCC_OS_USLEEP(100000);
printf("thread c allowing thread b to proceed\n");
expression_b = 1;
pthread_cond_broadcast(&c);
return NULL;
}
void main(int argc,char**argv)
{
pthread_cond_init(&c, NULL);
pthread_mutex_init(&mtx, NULL);
pthread_t thread_id_a;
pthread_t thread_id_b;
pthread_t thread_id_c;
pthread_attr_t thread_attr;
struct sched_param param;
// start thread a (medium priority)
pthread_attr_init (&thread_attr);
pthread_attr_getschedparam (&thread_attr, ¶m);
param.sched_priority = 4;
pthread_attr_setschedparam (&thread_attr, ¶m);
pthread_create(&thread_id_a, &thread_attr, medium_priority_thread_a, NULL);
// start thread b (low priority)
param.sched_priority = 2;
pthread_attr_setschedparam (&thread_attr, ¶m);
pthread_create(&thread_id_b, &thread_attr, low_priority_thread_b, NULL);
// start thread c (high priority)
param.sched_priority = 5;
pthread_attr_setschedparam (&thread_attr, ¶m);
pthread_create(&thread_id_c, &thread_attr, high_priority_thread_c, NULL);
pthread_join (thread_id_c, NULL);
printf("thread c finished.\n");
pthread_join (thread_id_b, NULL);
printf("thread b finished.\n");
return 0;
}
the output of running this in freertos is:
thread a waiting for expression_a
thread b waiting for expression_b
thread c allowing thread b to proceed
thread c finished.
thread a waiting for expression_a
thread a waiting for expression_a
whereas if the priorities are inverted and run on linux, the output is:
thread a waiting for expression_a
thread b waiting for expression_b
thread c allowing thread b to proceed
thread a waiting for expression_a
thread c finished.
thread b proceeding
thread b finished.
As a simple fix for us, we changed pthread_cond_wait/broadcast to use a list of threads that should be notified,… and instead use xTaskNotifyGive & ulTaskNotifyTake.