This is the mail archive of the
libc-help@sourceware.org
mailing list for the glibc project.
SCHED_FIFO and pthread_cond_broadcast
- From: Tadeus Prastowo <0x66726565 at gmail dot com>
- To: libc-help at sourceware dot org
- Date: Thu, 16 Jan 2020 23:59:44 +0100
- Subject: SCHED_FIFO and pthread_cond_broadcast
Hello,
AFAIK, POSIX specifies that pthread_cond_broadcast should wake up
waiting threads in their scheduling orders. So, in the attached
program, main is expected to always return 1 because:
1. When main performs pthread_cond_broadcast(&b) in line 52, the
condition variable b already has threads task_1 and task_2 waiting.
2. Both task_1 and task_2 are scheduled using SCHED_FIFO such that
task_2 has a priority higher than task_1.
3. Since pthread_cond_broadcast wakes up waiting threads in their
scheduling orders, task_2 runs first and sets the value to be returned
to 2 and then task_1 runs and sets the value to be returned to 1.
4. The main function returns the last value written, which is 1.
However, I observe that the main function can return the value 2,
indicating that task_1 can run before task_2 despite POSIX
specification by the following steps:
$ gcc -O2 -o z z.c -pthread
$ sudo chown root z
$ sudo chmod u+s z
$ for ((i = 0; i < 1000000; ++i)); do ./z; if [ $? -eq 2 ]; then echo
It is two at $i; break; fi; done
When I follow the steps in my Ubuntu 16.04.6 (the output of uname -a
is: Linux Eus 4.15.0-75-generic #85~16.04.1-Ubuntu SMP Wed Jan 15
12:30:12 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux), which uses Glibc
2.23 (obtained by executing /lib/x86_64-linux-gnu/libc-2.23.so -v) and
GCC 9.2.1, I get an output like the following, indicating that task_1
can run before task_2:
It is two at 22718
Could someone help me figure out why pthread_cond_broadcast does not
respect the POSIX specification, please?
Thank you.
--
Best regards,
Tadeus
#include <pthread.h>
#include <sched.h>
struct data {
const int my_value;
pthread_mutex_t *m;
pthread_cond_t *b;
pthread_cond_t *c;
int *value;
};
void *fn(void *arg) {
struct data *data = arg;
pthread_mutex_lock(data->m);
pthread_cond_signal(data->c);
pthread_cond_wait(data->b, data->m);
*data->value = data->my_value;
pthread_mutex_unlock(data->m);
return NULL;
}
int main() {
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t b = PTHREAD_COND_INITIALIZER,
c = PTHREAD_COND_INITIALIZER;
int value = 0;
pthread_t task_1, task_2;
pthread_attr_t attr_1, attr_2;
struct sched_param prm_1, prm_2;
struct data data_1 = {1, &m, &b, &c, &value},
data_2 = {2, &m, &b, &c, &value};
pthread_attr_init(&attr_1);
pthread_attr_setinheritsched(&attr_1, PTHREAD_EXPLICIT_SCHED);
pthread_attr_setschedpolicy(&attr_1, SCHED_FIFO);
prm_1.sched_priority = sched_get_priority_min(SCHED_FIFO);
pthread_attr_setschedparam(&attr_1, &prm_1);
pthread_attr_init(&attr_2);
pthread_attr_setinheritsched(&attr_2, PTHREAD_EXPLICIT_SCHED);
pthread_attr_setschedpolicy(&attr_2, SCHED_FIFO);
prm_2.sched_priority = sched_get_priority_max(SCHED_FIFO);
pthread_attr_setschedparam(&attr_2, &prm_2);
pthread_mutex_lock(&m);
pthread_create(&task_1, &attr_1, fn, &data_1);
pthread_cond_wait(&c, &m);
pthread_create(&task_2, &attr_2, fn, &data_2);
pthread_cond_wait(&c, &m);
pthread_mutex_unlock(&m);
pthread_cond_broadcast(&b);
pthread_join(task_1, NULL);
pthread_join(task_2, NULL);
return value;
}