I'm trying to create 3 thread task with different priorities. The task will only wait or signal other thread tasks, and save the execution in char array to see that they executed as supposed.
Here is the code
#include <stdio.h>
#include <unistd.h>
#include <stdbool.h>
#include <time.h>
#include <pthread.h>
static void *pT1();
static void *pT2();
static void *pT3();
static void trigT3();
static void trigT2();
pthread_cond_t gCondVar1;
pthread_cond_t gCondVar2;
pthread_cond_t gCondVar3;
pthread_cond_t gFinish;
pthread_mutex_t gLock1;
pthread_mutex_t gLock2;
pthread_mutex_t gLock3;
pthread_mutex_t gLockF;
static char savedExe[4];
static int gPos = 0;
static bool gSignalT2 = false;
static bool gSignalT3 = false;
int main(int argc, char const *argv[])
{
struct sched_param param1;
struct sched_param param2;
struct sched_param param3;
pthread_attr_t attr1;
pthread_attr_t attr2;
pthread_attr_t attr3;
pthread_t tid1;
pthread_t tid2;
pthread_t tid3;
pthread_attr_init(&attr1);
pthread_attr_init(&attr2);
pthread_attr_init(&attr3);
pthread_attr_setschedpolicy(&attr1, SCHED_FIFO);
pthread_attr_setschedpolicy(&attr2, SCHED_FIFO);
pthread_attr_setschedpolicy(&attr3, SCHED_FIFO);
param1.sched_priority = 10;
param2.sched_priority = 20;
param3.sched_priority = 30;
pthread_attr_setschedparam(&attr1, ¶m1);
pthread_attr_setschedparam(&attr2, ¶m2);
pthread_attr_setschedparam(&attr3, ¶m3);
pthread_create(&tid1, &attr1, pT1, NULL);
pthread_create(&tid2, &attr2, pT2, NULL);
pthread_create(&tid3, &attr3, pT3, NULL);
for (int i = 0; i < sizeof(savedExe); i) {
printf("%c, ", savedExe[i]);
}
printf("\b\b\n");
pthread_cond_signal(&gCondVar1);
/*.
.
.
.*/
pthread_cond_wait(&gFinish, &gLockF);
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
pthread_join(tid3, NULL);
for (int i = 0; i < sizeof(savedExe); i) {
printf("%c, ", savedExe[i]);
}
printf("\b\b\n");
return 0;
}
static void trigT3(){
pthread_mutex_lock(&gLock3);
pthread_cond_signal(&gCondVar3);
gSignalT3 = true;
pthread_mutex_unlock(&gLock3);
}
static void trigT2(){
pthread_mutex_lock(&gLock2);
pthread_cond_signal(&gCondVar2);
gSignalT2 = true;
pthread_mutex_unlock(&gLock2);
}
static void *pT1(){
pthread_mutex_lock(&gLock1);
pthread_cond_wait(&gCondVar1, &gLock1);
trigT3();
gPos ;
savedExe[gPos] = '1';
pthread_mutex_unlock(&gLock1);
pthread_cond_signal(&gFinish);
return NULL;
}
static void *pT3(){
//printf("T3\n");
pthread_mutex_lock(&gLock3);
if(!gSignalT3){
pthread_cond_wait(&gCondVar3, &gLock3);
gSignalT3 = false;
}
trigT2();
gPos ;
savedExe[gPos] = '3';
pthread_mutex_unlock(&gLock3);
return NULL;
}
static void *pT2(){
pthread_mutex_lock(&gLock2);
if(!gSignalT2){
pthread_cond_wait(&gCondVar2, &gLock2);
gSignalT3 = false;
}
gPos ;
savedExe[gPos] = '2';
pthread_mutex_unlock(&gLock2);
return NULL;
}
In the main, I create the 3 different tasks and assign them with different priorities. pT1 has the lowest priority = 10, pT2 = 20 and pT3 = 30. pT1 starts the execution first and since its a lower priority and it triggers pT3, pT3 will start executing. pT3 will trigger pT2 but since pT2 is lower priority, pT3 will finish executing and then it will trigger pT2.
The output of this should be 3, 2, 1. But what I get is 1, 2, 3, as if it does not take care to any priority at all and just executes the tasks in the given order.
CodePudding user response:
Priority affects how much CPU time a thread gets when multiple threads want to use the CPU at once. It's not relevant here.
The problem is that call trig
too soon! You want T2 to run after T3, but you trigger T2 before T3 does its thing running. Both end up trying to modify savedExe
and gPos
at the same time, which is bad. Both problems are avoided by properly ordering the statements.
static void *pT3() {
wait_for_trigger(&gLock3, &gCondVar3, &gSignalT3);
savedExe[gPos ] = '3';
trigger(&gLock2, &gCondVar2, &gSignalT2);
return NULL;
}
Demo on Compiler Explorer
Note that you don't need so many mutexes and conv vars. You could use a single mutex and condvar for all signals.
static void wait_for_trigger(bool *flag) {
pthread_mutex_lock(&mutex);
while (!*flag)
pthread_cond_wait(&cond, &mutex);
pthread_mutex_unlock(&mutex);
}
static void trigger(bool *flag) {
pthread_mutex_lock(&mutex);
*flag = true;
pthread_mutex_unlock(&mutex);
pthread_cond_broadcast(&cond);
}
Demo on Compiler Explorer
You could even use a single variable for the different priority levels!
static void wait_for_level(int a_prio_lvl) {
pthread_mutex_lock(&mutex);
while (prio_lvl > a_prio_lvl)
pthread_cond_wait(&cond, &mutex);
pthread_mutex_unlock(&mutex);
}
static void switch_to_level(int a_prio_lvl) {
pthread_mutex_lock(&mutex);
prio_lvl = a_prio_lvl;
pthread_mutex_unlock(&mutex);
pthread_cond_broadcast(&cond);
}
Demo on Compiler Explorer
You have numerous other problems covered below. These are all fixed in this demo.
Improper prototype
static void *pT1();
should be
static void *pT1(void);
Empty parens in a declaration doesn't mean no arguments. You need to use void
to mean that.
Improper arguments
static void *pT1() { ... }
should be
static void *pT1(void *arg) { ... }
pthread_create
calls the function with a void *
argument.
You can use (void)arg
to silence the unused argument warning.
Uninitialized mutexes
pthread_mutex_t gLock1;
should be
pthread_mutex_t gLock1 = PTHREAD_MUTEX_INITIALIZER;
Mutex need to be initialized before they are used!
Uninitialized cond vars
pthread_cond_t gCondVar1;
should be
pthread_cond_t gCondVar1 = PTHREAD_COND_INITIALIZER;
Cond vars need to be initialized before they are used!
Incorrect expectations from pthread_cond_wait
if (!gSignalT3){
pthread_cond_wait(&gCondVar3, &gLock3);
}
should be
while (!gSignalT3){
pthread_cond_wait(&gCondVar3, &gLock3);
}
pthread_cond_wait
can return at any time. To be clear, pthread_cond_wait
can return even if pthread_cond_signal
hasn't been used. So it must always be called in a loop that checks some external condition.
Passing unlocked mutex to pthread_cond_wait
pthread_cond_wait(&gFinish, &gLockF);
This suffers from four problems we've already covered:
- Lack of initializing of
gFinish
- Lack of initialization of
gLockF
- Lack of a required loop
- Lack of an external condition
But that's not it.
pthread_cond_wait
requires a locked mutex, but you are passing an unlocked mutex. It will return immediately with error EINVAL
.
Useless code
This is again about
pthread_cond_wait(&gFinish, &gLockF);
It's completely unneeded. pthread_join
already waits for the thread to finish. So this serves no purpose.
Full final code:
#include <stdio.h>
#include <stdbool.h>
#include <pthread.h>
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
static int prio_lvl = 3;
static int gPos = 0;
static char savedExe[3];
static void wait_for_level(int a_prio_lvl) {
pthread_mutex_lock(&mutex);
while (prio_lvl > a_prio_lvl)
pthread_cond_wait(&cond, &mutex);
pthread_mutex_unlock(&mutex);
}
static void switch_to_level(int a_prio_lvl) {
pthread_mutex_lock(&mutex);
prio_lvl = a_prio_lvl;
pthread_mutex_unlock(&mutex);
pthread_cond_broadcast(&cond);
}
static void *pT1(void *arg) {
(void)arg;
wait_for_level(1);
savedExe[gPos ] = '1';
return NULL;
}
static void *pT2(void *arg) {
(void)arg;
wait_for_level(2);
savedExe[gPos ] = '2';
switch_to_level(1);
return NULL;
}
static void *pT3(void *arg) {
(void)arg;
wait_for_level(3);
savedExe[gPos ] = '3';
switch_to_level(2);
return NULL;
}
int main(void) {
pthread_t tid1;
pthread_t tid2;
pthread_t tid3;
pthread_create(&tid1, NULL, pT1, NULL);
pthread_create(&tid2, NULL, pT2, NULL);
pthread_create(&tid3, NULL, pT3, NULL);
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
pthread_join(tid3, NULL);
{
const char *format = "%c";
for (size_t i = 0; i < sizeof(savedExe); i) {
printf(format, savedExe[i]);
format = ", %c";
}
printf("\n");
}
return 0;
}
CodePudding user response:
Ok, as i understood your requirements. You want to make priority based thread initiation and storing result in what so ever form.
You can achive this by making a simple priority queue of struct having the priority value and the ptherad variable to hold thread information.
only catch is that you need to make a custom priority queue in c over a structure of roughly of this kind
typedef struct prt_qu{
uint8_t prty;
pthread th;
} ptr_qu;
Upon execution you just need to enqueue all tasks in priority queue and make the main thread call these threads one by one. You can make main wait for current thread wait by just using pthread_join()
.