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.
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 you call trig too soon! You want T2 to run after T3, but you trigger T2 before T3 does its thing. 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;
}
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().
I have a multihreaded program whose 2 threads communicate with each other via a message queue. The first thread (sender) periodically sends a message, while the second thread (receiver) processes the information.
The sender has code similar to this:
// Create queue
key_t key = ftok("/tmp", 'B');
int msqid = msgget(key, 0664 | IPC_CREAT);
// Create message and send
struct request_msg req_msg;
req_msg.mtype = 1;
snprintf(req_msg.mtext, MSG_LENGTH, "Send this information");
msgsnd(msqid, &req_msg, strlen(req_msg.mtext) + 1, 0);
On the receiving thread, I do this:
// Subscribe to queue
key_t key = ftok("/tmp", 'B');
int msqid = msgget(key, 0664);
struct request_msg req_msg;
while(running)
{
msgrcv(msqid, &req_msg, sizeof(req_msg.mtext), 0, 0);
// Do sth with the message
}
As you can see, the receiver sits within a while loop that is controlled by a global variable named "running". Error handlers do set the boolean to false, if an error is encountered within the process. This works in most cases, but if an error occurs before being able to send a message to the queue, the receiver will not exit the while loop because it waits for a message before continuing and thus, checking the running variable. That means it will hang there forever, as the sender will not send anything for the rest of the runtime.
I would like to avoid this, but I do not know how to let msgrcv know that it cannot expect any more messages. I was unable to learn how msgrcv behaves if I kill the queue, assuming this is the easiest version. Maybe timeouts or sending some kind of termination message (possibly using the mtype member of the message struct) are also possible.
Please, let me know what the most robust solution to this problem is. Thanks!
EDIT: based on suggestions I have reworked the code to make the signal handlers action atomic.
#include <stdbool.h> // bool data type
#include <stdio.h>
#include <signal.h>
#include <stdint.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#define ALARM_INTERVAL_SEC 1
#define ALARM_INTERVAL_USEC 0
struct message
{
uint64_t iteration;
char req_time[28];
};
static volatile bool running = true;
static volatile bool work = false;
static struct itimerval alarm_interval;
static struct timeval previous_time;
static uint64_t loop_count = 0;
static struct message msg;
pthread_mutex_t mutexmsg;
pthread_cond_t data_updated_cv;
static void
termination_handler(int signum)
{
running = false;
}
static void
alarm_handler(int signum)
{
work = true;
}
static void
write_msg(void)
{
// Reset the alarm interval
if(setitimer(ITIMER_REAL, &alarm_interval, NULL) < 0)
{
perror("setitimer");
raise(SIGTERM);
return;
}
struct timeval current_time;
gettimeofday(¤t_time, NULL);
printf("\nLoop count: %lu\n", loop_count);
printf("Loop time: %f us\n", (current_time.tv_sec - previous_time.tv_sec) * 1e6 +
(current_time.tv_usec - previous_time.tv_usec));
previous_time = current_time;
// format timeval struct
char tmbuf[64];
time_t nowtime = current_time.tv_sec;
struct tm *nowtm = localtime(&nowtime);
strftime(tmbuf, sizeof(tmbuf), "%Y-%m-%d %H:%M:%S", nowtm);
// write values
pthread_mutex_lock(&mutexmsg);
msg.iteration = loop_count;
snprintf(msg.req_time, sizeof(msg.req_time), "%s.%06ld", tmbuf, current_time.tv_usec);
pthread_cond_signal(&data_updated_cv);
pthread_mutex_unlock(&mutexmsg);
loop_count++;
}
static void*
process_msg(void *args)
{
while(1)
{
pthread_mutex_lock(&mutexmsg);
printf("Waiting for condition\n");
pthread_cond_wait(&data_updated_cv, &mutexmsg);
printf("Condition fulfilled\n");
if(!running)
{
break;
}
struct timeval process_time;
gettimeofday(&process_time, NULL);
char tmbuf[64];
char buf[64];
time_t nowtime = process_time.tv_sec;
struct tm *nowtm = localtime(&nowtime);
strftime(tmbuf, sizeof(tmbuf), "%Y-%m-%d %H:%M:%S", nowtm);
snprintf(buf, sizeof(buf), "%s.%06ld", tmbuf, process_time.tv_usec);
// something that takes longer than the interval time
// sleep(1);
printf("[%s] Req time: %s loop cnt: %lu\n", buf, msg.req_time, msg.iteration);
pthread_mutex_unlock(&mutexmsg);
}
pthread_exit(NULL);
}
int
main(int argc, char* argv[])
{
pthread_t thread_id;
pthread_attr_t attr;
// for portability, set thread explicitly as joinable
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
if(pthread_create(&thread_id, NULL, process_msg, NULL) != 0)
{
perror("pthread_create");
exit(1);
}
pthread_attr_destroy(&attr);
// signal handling setup
struct sigaction t;
t.sa_handler = termination_handler;
sigemptyset(&t.sa_mask);
t.sa_flags = 0;
sigaction(SIGINT, &t, NULL);
sigaction(SIGTERM, &t, NULL);
struct sigaction a;
a.sa_handler = alarm_handler;
sigemptyset(&a.sa_mask);
a.sa_flags = 0;
sigaction(SIGALRM, &a, NULL);
// Set the alarm interval
alarm_interval.it_interval.tv_sec = 0;
alarm_interval.it_interval.tv_usec = 0;
alarm_interval.it_value.tv_sec = ALARM_INTERVAL_SEC;
alarm_interval.it_value.tv_usec = ALARM_INTERVAL_USEC;
if(setitimer(ITIMER_REAL, &alarm_interval, NULL) < 0)
{
perror("setitimer");
exit(1);
}
gettimeofday(&previous_time, NULL);
while(1)
{
// suspending main thread until a signal is caught
pause();
if(!running)
{
// signal the worker thread to stop execution
pthread_mutex_lock(&mutexmsg);
pthread_cond_signal(&data_updated_cv);
pthread_mutex_unlock(&mutexmsg);
break;
}
if(work)
{
write_msg();
work = false;
}
}
// suspend thread until the worker thread joins back in
pthread_join(thread_id, NULL);
// reset the timer
alarm_interval.it_value.tv_sec = 0;
alarm_interval.it_value.tv_usec = 0;
if(setitimer(ITIMER_REAL, &alarm_interval, NULL) < 0)
{
perror("setitimer");
exit(1);
}
printf("EXIT\n");
pthread_exit(NULL);
}
You have not justified the use of a message queue other than as a synchronization primitive. You could be passing the message via a variable and an atomic flag to indicate message readiness. This answer then describes how to implement thread suspension and resuming using a condition variable. That’s how it’d be typically done between threads, although of course is not the only way.
I do not know how to let msgrcv know that it cannot expect any more messages
No need for that. Just send a message that tells the thread to finish! The running variable doesn’t belong: you are trying to communicate with the other thread, so do it the way you chose to: message it!
I have spent the last day to read a lot about threading and mutexes and tried to get my example program to work. It does, but unfortunately, it gets stuck when I try to shut it down via Ctrl+C. Reason being (again) that this time, that the worker thread waits for a signal from the main thread that won't send a signal anymore.
#Rachid K. and #Unslander Monica: if you want to take a look again, is this more state of the art code for doing this? Also, I think I have to use pthread_cond_timedwait instead of pthread_cond_wait to avoid the termination deadlock. Could you tell me how to handle that exactly?
Note that the program does simply periodically (interval 1 s) hand a timestamp and a loop counter to the processing thread, that prints out the data. The output also shows when the print got called.
Thanks again!
#include <stdbool.h> // bool data type
#include <stdio.h>
#include <signal.h>
#include <stdint.h>
#include <pthread.h>
#include <stdlib.h>
#define ALARM_INTERVAL_SEC 1
#define ALARM_INTERVAL_USEC 0
static bool running = true;
static struct itimerval alarm_interval;
static struct timeval previous_time;
static uint64_t loop_count = 0;
struct message
{
uint64_t iteration;
char req_time[28];
} msg;
pthread_mutex_t mutexmsg;
pthread_cond_t data_updated_cv;
static void
signal_handler(int signum)
{
if (signum == SIGINT || signum == SIGTERM)
{
running = false;
}
}
static void
write_msg(int signum)
{
if(!running)
{
return;
}
// Reset the alarm interval
if(setitimer(ITIMER_REAL, &alarm_interval, NULL) < 0)
{
perror("setitimer");
raise(SIGTERM);
return;
}
struct timeval current_time;
gettimeofday(¤t_time, NULL);
printf("\nLoop count: %lu\n", loop_count);
printf("Loop time: %f us\n", (current_time.tv_sec - previous_time.tv_sec) * 1e6 +
(current_time.tv_usec - previous_time.tv_usec));
previous_time = current_time;
// format timeval struct
char tmbuf[64];
time_t nowtime = current_time.tv_sec;
struct tm *nowtm = localtime(&nowtime);
strftime(tmbuf, sizeof(tmbuf), "%Y-%m-%d %H:%M:%S", nowtm);
// write values
pthread_mutex_lock(&mutexmsg);
msg.iteration = loop_count;
snprintf(msg.req_time, sizeof(msg.req_time), "%s.%06ld", tmbuf, current_time.tv_usec);
pthread_cond_signal(&data_updated_cv);
pthread_mutex_unlock(&mutexmsg);
loop_count++;
}
static void*
process_msg(void *args)
{
while(running)
{
pthread_mutex_lock(&mutexmsg);
printf("Waiting for condition\n");
pthread_cond_wait(&data_updated_cv, &mutexmsg);
printf("Condition fulfilled\n");
struct timeval process_time;
gettimeofday(&process_time, NULL);
char tmbuf[64];
char buf[64];
time_t nowtime = process_time.tv_sec;
struct tm *nowtm = localtime(&nowtime);
strftime(tmbuf, sizeof(tmbuf), "%Y-%m-%d %H:%M:%S", nowtm);
snprintf(buf, sizeof(buf), "%s.%06ld", tmbuf, process_time.tv_usec);
printf("[%s] Message req time: %s loop cnt: %lu\n", buf, msg.req_time, msg.iteration);
pthread_mutex_unlock(&mutexmsg);
}
pthread_exit(NULL);
}
int
main(int argc, char* argv[])
{
pthread_t thread_id;
pthread_attr_t attr;
// for portability, set thread explicitly as joinable
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
if(pthread_create(&thread_id, NULL, process_msg, NULL) != 0)
{
perror("pthread_create");
exit(1);
}
pthread_attr_destroy(&attr);
// signal handling setup
struct sigaction s;
s.sa_handler = signal_handler;
sigemptyset(&s.sa_mask);
s.sa_flags = 0;
sigaction(SIGINT, &s, NULL);
sigaction(SIGTERM, &s, NULL);
struct sigaction a;
a.sa_handler = write_msg;
sigemptyset(&a.sa_mask);
a.sa_flags = 0;
sigaction(SIGALRM, &a, NULL);
// Set the alarm interval
alarm_interval.it_interval.tv_sec = 0;
alarm_interval.it_interval.tv_usec = 0;
alarm_interval.it_value.tv_sec = ALARM_INTERVAL_SEC;
alarm_interval.it_value.tv_usec = ALARM_INTERVAL_USEC;
if(setitimer(ITIMER_REAL, &alarm_interval, NULL) < 0)
{
perror("setitimer");
exit(1);
}
gettimeofday(&previous_time, NULL);
// suspend thread until the worker thread joins back in
pthread_join(thread_id, NULL);
// reset the timer
alarm_interval.it_value.tv_sec = 0;
alarm_interval.it_value.tv_usec = 0;
if(setitimer(ITIMER_REAL, &alarm_interval, NULL) < 0)
{
perror("setitimer");
exit(1);
}
pthread_exit(NULL);
return 0;
}
On the receiving thread, I do this:
...
while(running)
{
msgrcv(msqid, &req_msg, sizeof(req_msg.mtext), 0, 0);
Hopefully, in reality you do more than that.
Because you're not checking any error status in the code you've posted. And that's flat-out wrong for a blocking function call that is likely specified to never be restarted on receipt of a signal (as is true on Linux and Solaris). Per Linux `signal(2):
The following interfaces are never restarted after being interrupted
by a signal handler, regardless of the use of SA_RESTART; they
always fail with the error EINTR when interrupted by a signal
handler:
...
System V IPC interfaces: msgrcv(2), msgsnd(2), semop(2), and
semtimedop(2).
and Solaris sigaction():
SA_RESTART
If set and the signal is caught, functions that are interrupted by the execution of this signal's handler are transparently restarted by the system, namely fcntl(2), ioctl(2), wait(3C), waitid(2), and the following functions on slow devices like terminals: getmsg() and getpmsg() (see getmsg(2)); putmsg() and putpmsg() (see putmsg(2)); pread(), read(), and readv() (see read(2)); pwrite(), write(), and writev() (see write(2)); recv(), recvfrom(), and recvmsg() (see recv(3SOCKET)); and send(), sendto(), and sendmsg() (see send(3SOCKET)). Otherwise, the function returns an EINTR error.
So your code need to look more like this in order to handle both errors and signal interrupts:
volatile sig_atomic_t running;
...
while(running)
{
errno = 0;
ssize_t result = msgrcv(msqid, &req_msg, sizeof(req_msg.mtext), 0, 0);
if ( result == ( ssize_t ) -1 )
{
// if the call failed or no longer running
// break the loop
if ( ( errno != EINTR ) || !running )
{
break;
}
// the call was interrupted by a signal
continue
}
...
}
And that opens up the opportunity to use a alarm() and a SIGALRM signal handler to set running to 0 for use as a timeout:
volatile sig_atomic_t running;
void handler( int sig );
{
running = 0;
}
...
struct sigaction sa;
memset( &sa, 0, sizeof( sa ) );
sa.sa_handler = handler;
sigaction( SIGALRM, &sa, NULL );
while(running)
{
// 10-sec timeout
alarm( 10 );
errno = 0;
ssize_t result = msgrcv( msqid, &req_msg, sizeof(req_msg.mtext), 0, 0 );
// save errno as alarm() can munge it
int saved_errno = errno;
// clear alarm if it hasn't fired yet
alarm( 0 );
if ( result == ( ssize_t ) -1 )
{
// if the call failed or no longer running
// break the loop
if ( ( saved_errno != EINTR ) || !running )
{
break;
}
// the call was interrupted by a signal
continue
}
...
}
That can almost certainly be improved upon - the logic is rather complex to catch all the corner cases and there's likely a simpler way to do it.
Answer to the new proposal in the question
The cyclic timer should be rearmed in the event loop of the main thread for better visibility (subjective proposal);
When the secondary thread goes out of its loop, it must release the
mutex otherwise the main thread will enter into a deadlock (waiting
for the mutex which was locked by the terminated secondary thread).
So, here is the last proposal with the above fixes/enhancements:
#include <stdbool.h> // bool data type
#include <stdio.h>
#include <signal.h>
#include <stdint.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#define ALARM_INTERVAL_SEC 1
#define ALARM_INTERVAL_USEC 0
struct message
{
uint64_t iteration;
char req_time[28];
};
static volatile bool running = true;
static volatile bool work = false;
static struct itimerval alarm_interval;
static struct timeval previous_time;
static uint64_t loop_count = 0;
static struct message msg;
pthread_mutex_t mutexmsg;
pthread_cond_t data_updated_cv;
static void
termination_handler(int signum)
{
running = false;
}
static void
alarm_handler(int signum)
{
work = true;
}
static void
write_msg(void)
{
struct timeval current_time;
gettimeofday(¤t_time, NULL);
printf("\nLoop count: %lu\n", loop_count);
printf("Loop time: %f us\n", (current_time.tv_sec - previous_time.tv_sec) * 1e6 +
(current_time.tv_usec - previous_time.tv_usec));
previous_time = current_time;
// format timeval struct
char tmbuf[64];
time_t nowtime = current_time.tv_sec;
struct tm *nowtm = localtime(&nowtime);
strftime(tmbuf, sizeof(tmbuf), "%Y-%m-%d %H:%M:%S", nowtm);
// write values
pthread_mutex_lock(&mutexmsg);
msg.iteration = loop_count;
snprintf(msg.req_time, sizeof(msg.req_time), "%s.%06ld", tmbuf, current_time.tv_usec);
pthread_cond_signal(&data_updated_cv);
pthread_mutex_unlock(&mutexmsg);
loop_count++;
}
static void*
process_msg(void *args)
{
while(1)
{
pthread_mutex_lock(&mutexmsg);
printf("Waiting for condition\n");
pthread_cond_wait(&data_updated_cv, &mutexmsg);
printf("Condition fulfilled\n");
if(!running)
{
pthread_mutex_unlock(&mutexmsg); // <----- To avoid deadlock
break;
}
struct timeval process_time;
gettimeofday(&process_time, NULL);
char tmbuf[64];
char buf[64];
time_t nowtime = process_time.tv_sec;
struct tm *nowtm = localtime(&nowtime);
strftime(tmbuf, sizeof(tmbuf), "%Y-%m-%d %H:%M:%S", nowtm);
snprintf(buf, sizeof(buf), "%s.%06ld", tmbuf, process_time.tv_usec);
// something that takes longer than the interval time
//sleep(2);
printf("[%s] Req time: %s loop cnt: %lu\n", buf, msg.req_time, msg.iteration);
pthread_mutex_unlock(&mutexmsg);
}
printf("Thread exiting...\n");
pthread_exit(NULL);
}
int
main(int argc, char* argv[])
{
pthread_t thread_id;
pthread_attr_t attr;
// for portability, set thread explicitly as joinable
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
if(pthread_create(&thread_id, NULL, process_msg, NULL) != 0)
{
perror("pthread_create");
exit(1);
}
pthread_attr_destroy(&attr);
// signal handling setup
struct sigaction t;
t.sa_handler = termination_handler;
sigemptyset(&t.sa_mask);
t.sa_flags = 0;
sigaction(SIGINT, &t, NULL);
sigaction(SIGTERM, &t, NULL);
struct sigaction a;
a.sa_handler = alarm_handler;
sigemptyset(&a.sa_mask);
a.sa_flags = 0;
sigaction(SIGALRM, &a, NULL);
// Set the alarm interval
alarm_interval.it_interval.tv_sec = 0;
alarm_interval.it_interval.tv_usec = 0;
alarm_interval.it_value.tv_sec = ALARM_INTERVAL_SEC;
alarm_interval.it_value.tv_usec = ALARM_INTERVAL_USEC;
if(setitimer(ITIMER_REAL, &alarm_interval, NULL) < 0)
{
perror("setitimer");
exit(1);
}
gettimeofday(&previous_time, NULL);
while(1)
{
// Reset the alarm interval <-------- Rearm the timer in the main loop
if(setitimer(ITIMER_REAL, &alarm_interval, NULL) < 0)
{
perror("setitimer");
raise(SIGTERM);
break;
}
// suspending main thread until a signal is caught
pause();
if(!running)
{
// signal the worker thread to stop execution
pthread_mutex_lock(&mutexmsg);
pthread_cond_signal(&data_updated_cv);
pthread_mutex_unlock(&mutexmsg);
break;
}
if(work)
{
write_msg();
work = false;
}
}
// suspend thread until the worker thread joins back in
pthread_join(thread_id, NULL);
// reset the timer
alarm_interval.it_value.tv_sec = 0;
alarm_interval.it_value.tv_usec = 0;
if(setitimer(ITIMER_REAL, &alarm_interval, NULL) < 0)
{
perror("setitimer");
exit(1);
}
printf("EXIT\n");
pthread_exit(NULL);
}
==================================================================
Answer to the original question
It is possible to use a conditional variable to wait for a signal from the senders. This makes the receiver wake up and check for messages in the message queue by passing IPC_NOWAIT in the flags parameter of msgrcv(). To end the communication, an "End of communication" message can be posted. It is also possible to use pthread_con_timedwait() to wake up periodically and check a "end of communication" or "end of receiver condition" (e.g. by checking your global "running" variable).
Receiver side:
// Mutex initialization
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
// Condition variable initialization
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
[...]
while (1) {
// Lock the mutex
pthread_mutex_lock(&mutex);
// Check for messages (non blocking thanks to IPC_NOWAIT)
rc = msgrcv(msqid, &req_msg, sizeof(req_msg.mtext), 0, IPC_NOWAIT);
if (rc == -1) {
if (errno == ENOMSG) {
// message queue empty
// Wait for message notification
pthread_cond_wait(&cond, &mutex); // <--- pthread_cond_timedwait() can be used to wake up and check for the end of communication or senders...
} else {
// Error
}
}
// Handle the message, end of communication (e.g. "running" variable)...
// Release the lock (so that the sender can post something in the queue)
pthread_mutex_unlock(&mutex);
}
Sender side:
// Prepare the message
[...]
// Take the lock
pthread_mutex_lock(&mutex);
// Send the message
msgsnd(msqid, &req_msg, strlen(req_msg.mtext) + 1, 0);
// Wake up the receiver
pthread_cond_signal(&cond);
// Release the lock
pthread_mutex_unlock(&mutex);
N.B.: SYSV message queues are obsolete. It is preferable to use the brand new Posix services.
I am learning mutex currently and the following is the test code. It works perfectly fine. It creates another instance after one is done. Yet, it also introduces overhead according to here.
How efficient is locking an unlocked mutex? What is the cost of a mutex?. How can I modify my code to improve the efficiency?
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
typedef struct _context_t {
uint64_t id;
char *name;
bool initialized;
} context_t;
context_t *ctx = NULL;
context_t *get_instance() {
pthread_mutex_lock(&lock);
if (ctx == NULL) {
ctx = (context_t *)malloc(sizeof(context_t));
assert(ctx != NULL);
ctx->initialized = false;
}
pthread_mutex_unlock(&lock);
return ctx;
}
int id = 0;
void *do_work(void *arg) {
context_t *ctx = get_instance();
if (!ctx->initialized) {
ctx->name = (char *)arg;
ctx->id = ++id;
ctx->initialized = true;
}
printf("name=%s\tid=%ld\n", ctx->name, ctx->id);
return NULL;
}
int main() {
int rc;
pthread_t p1, p2;
rc = pthread_create(&p1, NULL, do_work, "A");
assert(rc == 0);
rc = pthread_create(&p2, NULL, do_work, "B");
assert(rc == 0);
rc = pthread_join(p1, NULL);
assert(rc == 0);
rc = pthread_join(p2, NULL);
assert(rc == 0);
if (ctx) {
free(ctx);
}
return 0;
}
Instead of having two threads racing to create the context_t, you should create it once before the threads start, and pass its address to the threads explicitly.
Note that you can pass multiple arguments via pthread_create() by putting them in a struct and passing its address.
Then you won't need a mutex at all, because the threads will only read from ctx rather than potentially writing to it.
I am trying to simulate a server/client socket communication, and using threads for each client and then use mutex to lock and unlock those clients.
Mutex is working fine, even if I open another terminal and run the client script, it gets blocked till server unlocks it, but for some bad coding reason, this second client gets the same ID from the first client.
So, I want to create a new thread every time I run a new client. But I think that this "fork" is being the problem, for some reason...
int counter = 0;
int i = 0;
int j = 0;
void* operacoes (void*);
pthread_mutex_t mutexA = PTHREAD_MUTEX_INITIALIZER;
pthread_t thread_id [10];
int var;
int jo = 0;
int t = 0;
int main()
{
int sock_fd, sock_len, sock_novo, sock_novo_len,num;
struct sockaddr_un sock_ser, sock_cli;
char msg[100];
sock_fd = socket(AF_UNIX, SOCK_STREAM,0);
if(sock_fd<0)
{
printf("ERROR\n");
exit(0);
}
unlink("socket.unix.teste");
bzero((char*)&sock_ser, sizeof(sock_ser));
sock_ser.sun_family = AF_UNIX;
strcpy(sock_ser.sun_path,"socket.unix.teste");
sock_len = strlen(sock_ser.sun_path) +sizeof(sock_ser.sun_family);
if(bind(sock_fd,(struct sockaddr*)&sock_ser,sock_len)<0)
{
printf("ERROR\n");
exit(0);
}
listen(sock_fd,5);
for(;;)
{
sock_novo_len = sizeof(sock_cli);
sock_novo = accept(sock_fd, (struct sockaddr*)&sock_cli,&sock_novo_len);
if(fork() == 0)
{
close(sock_fd);
num = atoi(msg);
counter++;
for (i = jo; i<counter; i++)
{
pthread_create (&thread_id[i], NULL, operacoes, (void *)num);
jo++;
}
for (j = t; j<counter; j++)
{
pthread_join(thread_id[j], NULL);;
t++;
}
exit(0);
}
close(sock_novo);
}
return 0;
}
void* operacoes (void *arg)
{
if(arg == 1)
{
int id = pthread_self();
printf("Thread nummber: %d \n", id);
pthread_mutex_lock (&mutexA);
printf("Locked\n");
sleep(10);
pthread_mutex_unlock (&mutexA);
printf("Unlocked\n");
}
return 0;
}
On client side, I only send a single variable 'msg'.
How could I solve it? I tried to use those two variables 'jo' and 't', but every new client I create, it reads the whole code, and it gets back to 0 so I cant get next phthread_create's vector position.
Assuming I understand correctly what you want to achieve:
Create a unix domain socket and listen for incoming connections
Treat each connection in parallel
Have one global mutex being locked/unlocked during treatment of the connections
try this:
#include <pthread.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
pthread_mutex_t mutexA = PTHREAD_MUTEX_INITIALIZER;
struct oarg_t {
int con_socket;
};
void* operacoes (void *arg)
{
struct oarg_t* oarg = arg;
int id = pthread_self();
printf("Thread number: %d \n", id);
pthread_mutex_lock (&mutexA);
printf("Locked\n");
sleep(10);
pthread_mutex_unlock (&mutexA);
printf("Unlocked\n");
close(oarg->con_socket);
free(oarg);
return 0;
}
int main()
{
const char* domain_name = "socket_unix.teste";
int i = 0;
int j = 0;
int sock_fd;
sock_fd = socket(AF_UNIX, SOCK_STREAM,0);
if(sock_fd<0) goto error;
unlink(domain_name);
struct sockaddr_un sock_ser = {
.sun_family = AF_UNIX,
};
strncpy(sock_ser.sun_path, domain_name, sizeof(sock_ser.sun_path));
if(0 != bind(sock_fd, (struct sockaddr*) &sock_ser, sizeof(sock_ser)))
goto error;
listen(sock_fd,5);
size_t counter = 0;
while(1) {
struct sockaddr_un sock_cli = {0};
int sock_len = sizeof(sock_cli);
int sock_novo =
accept(sock_fd, (struct sockaddr*)&sock_cli, &sock_len);
if(0 > sock_novo) {
printf("Error accepting:% s\n", strerror(errno));
continue;
}
printf("Connection no %zu\n", counter++);
struct oarg_t* oarg = calloc(1, sizeof(struct oarg_t));
oarg->con_socket = sock_novo;
pthread_t id = 0;
pthread_create (&id, NULL, operacoes, oarg);
}
return EXIT_SUCCESS;
error:
printf("ERROR: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
I'm having a bit of trouble getting a basic two-thread arrangement working.
I am reading a chunk of bytes into memory from stdin in one "producer" thread, and processing those bytes in a second "consumer" thread, once those bytes are available. Once the bytes are consumed, the consumer thread goes back to being dormant and the producer thread gets running again.
I am using pthread_cond_wait() and pthread_cond_signal() to have the two threads communicate to each other that data are produced or consumed.
Here is the code for the two threads:
void * produce_bytes(void *t_data)
{
pthread_data_t *d = (pthread_data_t *)t_data;
do {
pthread_mutex_lock(&d->input_lock);
d->n_bytes = fread(d->in_buf, sizeof(unsigned char), BUF_LENGTH_VALUE, stdin);
if (d->n_bytes > 0) {
fprintf(stdout, "PRODUCER ...signaling consumer...\n");
pthread_cond_signal(&d->input_cond);
fprintf(stdout, "PRODUCER ...consumer signaled...\n");
}
pthread_mutex_unlock(&d->input_lock);
} while (d->n_bytes > 0);
return NULL;
}
void * consume_bytes(void *t_data)
{
pthread_data_t *d = (pthread_data_t *)t_data;
pthread_mutex_lock(&d->input_lock);
while (d->n_bytes == 0)
pthread_cond_wait(&d->input_cond, &d->input_lock);
fprintf(stdout, "CONSUMER ...consuming chunk...\n");
d->n_bytes = 0;
fprintf(stdout, "CONSUMER ...chunk consumed...\n");
pthread_mutex_unlock(&d->input_lock);
}
The pthread_data_t is a struct I use to keep track of state:
typedef struct {
pthread_mutex_t input_lock;
pthread_cond_t input_cond;
unsigned char in_buf[BUF_LENGTH_VALUE];
size_t n_bytes;
} pthread_data_t;
I configure variables in my main() function; here is the relevant excerpt:
pthread_t producer_thread = NULL;
pthread_t consumer_thread = NULL;
pthread_data_t *thread_data = NULL;
thread_data = malloc(sizeof(pthread_data_t));
thread_data->n_bytes = 0;
pthread_mutex_init(&(thread_data->input_lock), NULL);
pthread_cond_init(&(thread_data->input_cond), NULL);
pthread_create(&producer_thread, NULL, produce_bytes, (void *) thread_data);
pthread_create(&consumer_thread, NULL, consume_bytes, (void *) thread_data);
pthread_join(producer_thread, NULL);
pthread_join(consumer_thread, NULL);
When I run this, produce_bytes() signals consume_bytes() successfully on the first iteration, but on the second and subsequent iterations, a signal is sent to consume_bytes() and it never gets heard, so the consumer function never gets run again:
PRODUCER ...signaling consumer...
PRODUCER ...consumer signaled...
CONSUMER ...consuming chunk...
CONSUMER ...chunk consumed...
PRODUCER ...signaling consumer...
PRODUCER ...consumer signaled...
PRODUCER ...signaling consumer...
PRODUCER ...consumer signaled...
PRODUCER ...signaling consumer...
PRODUCER ...consumer signaled...
...
I am using the tutorial here as the basis for what I'm trying to do. What I am doing wrong?
There are a few issues with that code:
produce_bytes locks the mutex for the duration of the blocking call to fread. A general rule of thumb for responsive applications is to lock the mutex for as short periods as possible. You may like to read the input into a temporary buffer first, then lock the mutex and copy the data to the buffer shared between threads. Same applies to consume_bytes which holds the mutex while calling fprintf which can block.
produce_bytes in while(d->n_bytes > 0) does not hold the mutex, which is a race condition because consume_bytes assigns a new value to d->n_bytes. Assuming you would like to exit that loop when fread returns 0 (EOF), you need to copy the return value of fread into a local variable not shared between threads and use that as the condition in while(read_bytes > 0)
consume_bytes does not have any loop around it so that it returns after the first condition variable notification. You probably would like to wrap it into a while loop and exit only when EOF (0 bytes) have been read.
Here is a working example which addresses Maxim's point 2 and 3, but not 1 because that is necessary for responsiveness but not strictly for correctness.
Note that I have not implemented a means for the producer to signal EOF to the consumer, so the consumer will never exit.
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define BUF_LENGTH_VALUE 100
typedef struct {
pthread_mutex_t input_lock;
pthread_cond_t input_cond;
unsigned char in_buf[BUF_LENGTH_VALUE];
size_t n_bytes;
} pthread_data_t;
void * produce_bytes(void *t_data)
{
pthread_data_t *d = (pthread_data_t *)t_data;
size_t local_byte_count = 0;
do {
pthread_mutex_lock(&d->input_lock);
local_byte_count = fread(d->in_buf, sizeof(unsigned char),
BUF_LENGTH_VALUE, stdin);
d->n_bytes += local_byte_count;
if (d->n_bytes > 0) {
fprintf(stdout, "PRODUCER ...signaling consumer...\n");
pthread_cond_signal(&d->input_cond);
fprintf(stdout, "PRODUCER ...consumer signaled...\n");
}
pthread_mutex_unlock(&d->input_lock);
// This is added to slow down the producer so that we can observe
// multiple consumptions.
sleep(1);
} while (local_byte_count > 0);
return NULL;
}
void * consume_bytes(void *t_data)
{
pthread_data_t *d = (pthread_data_t *)t_data;
while (1) {
pthread_mutex_lock(&d->input_lock);
while (d->n_bytes == 0) {
fprintf(stdout, "CONSUMER entering wait \n");
pthread_cond_wait(&d->input_cond, &d->input_lock);
}
fprintf(stdout, "CONSUMER ...consuming chunk...\n");
d->n_bytes = 0;
fprintf(stdout, "CONSUMER ...chunk consumed...\n");
pthread_mutex_unlock(&d->input_lock);
fflush(stdout);
}
}
int main(){
pthread_t producer_thread = NULL;
pthread_t consumer_thread = NULL;
pthread_data_t *thread_data = NULL;
thread_data = malloc(sizeof(pthread_data_t));
thread_data->n_bytes = 0;
pthread_mutex_init(&(thread_data->input_lock), NULL);
pthread_cond_init(&(thread_data->input_cond), NULL);
pthread_create(&producer_thread, NULL, produce_bytes, (void *) thread_data);
pthread_create(&consumer_thread, NULL, consume_bytes, (void *) thread_data);
pthread_join(producer_thread, NULL);
pthread_join(consumer_thread, NULL);
}