How to Terminate a Sleeping Thread in Pthread

how to terminate a sleeping thread in pthread?

As an alternative to sleep, you could use pthread_cond_timedwait with a 1000 ms timeout. Then when you want to exit, signal the condition variable.

This is similar to how you might do this in C#/Java using wait and notify.

How to wake the sleeping threads and exit the main thread?

The simplest thing to do would be to enqueue dummy tasks that indicate "all done" to the workers, since you know the number of workers in advance:

for(int i=0;i<total_threads_to_create;i++){
// a task of -1 means "no more work"
add_task_to_my_queue(-1);
}

Alternatively, you can have a "breakable" queue. This queue wakes up waiting consumers with a compound predicate: either non-empty or finished. Perl's Thread::Queue objects can be ended, for example, and Python's queues can track completed tasks.

Another option would be to keep track of the number of tasks completed yourself, with its own condition variable and mutex or a "countdown latch" or whatever, and not care about the worker threads. They'll be vaporized when the program exits.

Interrupting select, sleep and similar blocking functions inside a thread (pthreads). Signals in threads

Here is what I came up with. I don't know if there is another solution. I'll wait for suggestions.

To signal that work needs to be done SIGUSR1 is sent to the program. It sends SIGALERT to the threads to make them interrupt blocking code like sleep, select, etc. As a side effect blocking in main thread also gets interrupted, even though the signals are sent to the other threads only.

It can't be done with just one signal - like handle sigalert and then signal alert the two other threads using pthread_kill, because for it enters an endless loop.

#include <stdio.h>
#include <stdlib.h> // EXIT_FAILURE
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <errno.h> // for errno
#include <pthread.h>

#ifndef __cplusplus
#define true 1
#define false 0
#define nullptr NULL
typedef int bool;
#endif

// I copied this. I don't know why it's in a loop
#define handle_error_en(en, msg) \
do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0)

pthread_t t1;
pthread_t t2;
pthread_t st;
pthread_t mainThreadId; // main thread

void *workerThread(void * param);
void unblockSignals();
void alertSignaHlandler(int sigNum);
void installSignalHandler(int sigNum, __sighandler_t handler);

void sigUsr1Handler(int sigNum) {
pthread_kill(t1, SIGALRM);
// pthread_kill(t2, SIGALRM); // let's say we only want t1 to interrupt
}

// on SIGTERM we want all threads to interrupt sleep so they can quit faster
void sigTermHandler(int sigNum) {
// setRunning(false); // also implement a global running vairable with a mutex, that other threads check to quit
pthread_kill(t1, SIGALRM);
pthread_kill(t2, SIGALRM);
// pthread_kill(mainThreadId, SIGALRM); // main thread will be interrupted by the signal anyway.
}

void alertSignaHlandler(int sigNum) {}

int main() {
void * ret1;
void * ret2;

mainThreadId = pthread_self();

printf("main starting with pid %d\n", getpid());

installSignalHandler(SIGALRM, alertSignaHlandler); // we need this to prevent program termination on alarm
installSignalHandler(SIGUSR1, sigUsr1Handler); // send this signal to interrupt other threads
installSignalHandler(SIGTERM, sigTermHandler);
installSignalHandler(SIGINT, sigTermHandler);

pthread_create(&t1, NULL, &workerThread, (void *)1);
pthread_create(&t2, NULL, &workerThread, (void *)2);

// simulate work in main thread. Only interrupt this on SIGTERM or SIGINT (ctrl-c)
time_t ts = time(NULL);
sleep(20);
ts = time(NULL) - ts;
printf("main(): sleep took %lds\n", ts);

pthread_join(t1, &ret1);
pthread_join(t2, &ret2);

printf("main thread reached end\n");

return 0;
}

void *workerThread(void * param){
long id = (long) param;

printf("workerThread %d starting...\n", id);

// in the real world this will be in a loop. somethign like this
// while(running) {
// // do something every minute
// doSomething();
// sleep(60); // this sleep we want to interrupt, because we don't want to wait 60 seconds to exit
// }

time_t ts = time(NULL);
sleep(60); // this can also be select, read or other timed blocking call.
ts = time(NULL) - ts;
printf("workerThread(%ld): sleep took %lds\n", id, ts);

return NULL;
}

void installSignalHandler(int sigNum, __sighandler_t handler){
struct sigaction newAction, oldAction;

memset(&newAction, '\0', sizeof(newAction));
newAction.sa_flags = 0;
//newAction.sa_flags = SA_RESETHAND;
newAction.sa_handler = handler;
sigaction(sigNum, &newAction, &oldAction);
}

How to kill a running thread?

You can use pthread_cancel() to kill a thread:

int pthread_cancel(pthread_t thread);

Note that the thread might not get a chance to do necessary cleanups, for example, release a lock, free memory and so on..So you should first use pthread_cleanup_push() to add cleanup functions that will be called when the thread is cancelled. From man pthread_cleanup_push(3):

These functions manipulate the calling thread's stack of
thread-cancellation clean-up handlers. A clean-up handler is a
function that is automatically executed when a thread is cancelled
(or
in various other circumstances described below); it might, for
example, unlock a mutex so that it becomes available to other threads
in the process.

Regarding the question of whether a thread will be cancelled if blocking or not, it's not guaranteed, note that the manual also mentions this:

A thread's cancellation type, determined by pthread_setcanceltype(3),
may be either asynchronous or deferred (the default for new
threads)
. Asynchronous cancelability means that the thread
can be canceled at any time (usually immediately, but the system does
not guarantee this
). Deferred cancelability means that cancellation
will be delayed until the thread next calls a function that is a
cancellation point.

So this means that the only guaranteed behaviour is the the thread will be cancelled at a certain point after the call to pthread_cancel().

Note:
If you cannot change the thread code to add the cleanup handlers, then your only other choice is to kill the thread with pthread_kill(), however this is a very bad way to do it for the aforementioned reasons.

pthread - Sleep prevents other threads from execution

    if (pthread_create (&tid, attr, watek, tdata) != 0) {
perror("pthread_create");
exit(1);
}
if (pthread_join (tid, 0) != 0) {
perror("join\n");
exit(1);
}

You're creating the first thread and then waiting to pthread_join with it before creating the next one. The next loop iteration, therefore, (creating the next thread) won't happen until the first thread terminates.

If you want all the threads to start right away, just create and start them in the first loop - deal with waiting for them to complete later.

RESOLVED sleep methods hang the program threads in C pthreads (race condition, it had nothing to do with sleep)

You creates deadlock because you have race condition between threads.

Moving pthread_mutex_t poltrona_m, dorme_m, get_sedia_m, occupa_sedia_m;
at global scope, adding these verbose functions:

const char * whoIam()
{
if (pthread_self() == barbiere_thread)
return "barbiere";

for (int i = 0; i != NRCLIENTI; ++i) {
if (pthread_self() == cliente_thread[i]) {
static char str[NRCLIENTI][64];

sprintf(str[i], "cliente %d", i+1); /* done several times but whatever */
return str[i];
}
}

return "unknown-thread";
}

const char * whoIsMutex(pthread_mutex_t * m)
{
if (m == &poltrona_m)
return "poltrona";
if (m == &dorme_m)
return "dorme";
if (m == &get_sedia_m)
return "sedia";
if (m == &occupa_sedia_m)
return "occupa_sedia";
return "unknown-mutex";
}

void my_pthread_mutex_unlock(pthread_mutex_t * m)
{
printf("%s unlock %s\n", whoIam(), whoIsMutex(m));
pthread_mutex_unlock(m);
}

void my_pthread_mutex_lock(pthread_mutex_t * m)
{
printf("%s want to lock %s\n", whoIam(), whoIsMutex(m));
pthread_mutex_lock(m);
printf("%s locked %s\n", whoIam(), whoIsMutex(m));
}

and using my_pthread_mutex_lock and my_pthread_mutex_unlock out of init_attr the execution can be :

[MAIN] thread "barber" creato

[MAIN] thread "client 1" creato
barbiere want to lock sedia
barbiere locked sedia
barbiere want to lock dorme
barbiere locked dorme
cliente 1 want to lock poltrona
cliente 1 locked poltrona
cliente 1 unlock dorme <<< does nothing because does not have it
----[CLIENTE] Cliente "1" si taglia i capelli!
[BARBIERE] il barbiere dorme...
barbiere want to lock poltrona <<< wait while get by client 1
[MAIN] thread "client 2" creato
[MAIN] thread "client 3" creato
[MAIN] thread "client 4" creato
cliente 2 want to lock sedia
[MAIN] thread "client 5" creato
cliente 4 want to lock sedia
[MAIN] thread "client 6" creato
cliente 3 want to lock sedia
[MAIN] thread "client 7" creato
cliente 6 want to lock sedia
cliente 5 want to lock sedia
[MAIN] thread "client 8" creato
cliente 7 want to lock sedia
[MAIN] thread "client 9" creato
cliente 8 want to lock sedia
[MAIN] thread "client 10" creato
cliente 9 want to lock sedia
cliente 10 want to lock sedia
cliente 1 unlock poltrona <<< finally unlock poltrona
barbiere locked poltrona <<< wake up
barbiere unlock poltrona
barbiere want to lock sedia <<< dead because get by itself
[MAIN]Joined client "1"

Adding my_sleep(1000); before sedia_taglio = occupata; in cliente helps to not have the race condition :

bruno@bruno-XPS-8300:/tmp$ gcc -Wall t.c -lpthread
bruno@bruno-XPS-8300:/tmp$ ./a.out
[MAIN] thread "barber" creato

[MAIN] thread "client 1" creato
[BARBIERE] il barbiere dorme...
[MAIN] thread "client 2" creato
[MAIN] thread "client 3" creato
[MAIN] thread "client 4" creato
[MAIN] thread "client 5" creato
[MAIN] thread "client 6" creato
[MAIN] thread "client 7" creato
[MAIN] thread "client 8" creato
[MAIN] thread "client 9" creato
[MAIN] thread "client 10" creato
----[CLIENTE] Cliente "1" si taglia i capelli!
[MAIN]Joined client "1"
----[CLIENTE] Cliente "3" si taglia i capelli!
----[CLIENTE] Cliente "2" si taglia i capelli!
[MAIN]Joined client "2"
[MAIN]Joined client "3"
----[CLIENTE] Cliente "4" si taglia i capelli!
[MAIN]Joined client "4"
----[CLIENTE] Cliente "5" si taglia i capelli!
[MAIN]Joined client "5"
----[CLIENTE] Cliente "6" si taglia i capelli!
[MAIN]Joined client "6"
----[CLIENTE] Cliente "7" si taglia i capelli!
[MAIN]Joined client "7"
----[CLIENTE] Cliente "8" si taglia i capelli!
[MAIN]Joined client "8"
----[CLIENTE] Cliente "9" si taglia i capelli!
[MAIN]Joined client "9"
----[CLIENTE] Cliente "10" si taglia i capelli!
[MAIN]Joined client "10"

Out of that, about the way to sleep, the function sleep blocks all the process, contrarily to usleep and nanosleep.


Here a proposal without race condition, nor making assumption on the mutex implementation (no undefined behavior).

There is only one mutex to protect datas consistency and for the conditions, I also use it to protect printing.

There are seats to wait, and one used to cut hairs. To manage clients in order of arrival they get a ticket having a number, when there is no free seat at all a client cannot get a ticket and will retry later. The number of the ticket of the client to have hairs cut is given by CurrentTicket (as a screen client read) and the signal NextClient is produced (as a beep) to allow clients to check if their turn comes. The number of the ticket in the distributor is managed by LastTicket. The signal HelloBarber is sent by a client when he reaches the barber and may be wake up him. The barber sleeps when there is no client.

I use a random time limited to 5 seconds for the delay used by a client to (re)try to get a place, and the barber cut hairs of a client in a random time limited to 3 seconds.

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <time.h>

#define NCLIENT 10
#define NSEAT 5 /* seats to wait */

unsigned int Seed;
int Nseat = NSEAT + 1; /* add 1 to have seat to cut hairs */
int CurrentTicket = 0;
int LastTicket = 0;

pthread_mutex_t Mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t NextClient = PTHREAD_COND_INITIALIZER;
pthread_cond_t HelloBarber = PTHREAD_COND_INITIALIZER;

/* return -1 is no seat, else ticket number */
int getTicket()
{
int result;

pthread_mutex_lock(&Mutex);
if (Nseat == 0)
result = -1;
else {
Nseat -= 1;
result = ++LastTicket;
}
pthread_mutex_unlock(&Mutex);

return result;
}

void randomSleep(int max)
{
int millis = rand_r(&Seed) % (max * 1000);
struct timespec req, rem;

if (millis > 999) {
req.tv_sec = (int) (millis / 1000);
req.tv_nsec = (millis - ((long) req.tv_sec * 1000)) * 1000000;
} else {
req.tv_sec = 0;
req.tv_nsec = millis * 1000000;
}

nanosleep(&req, &rem);
}

void * client(void * arg)
{
int id = *((int *) arg);
int ticket;
int justArrived = 1;

while (randomSleep(5), ((ticket = getTicket()) == -1)) {
pthread_mutex_lock(&Mutex);
printf("----[CLIENT] Client \"%d\" no place, will try again later\n", id);
pthread_mutex_unlock(&Mutex);
}

pthread_mutex_lock(&Mutex);
printf("----[CLIENT] Client \"%d\" got ticket number %d\n", id, ticket);

while (ticket != CurrentTicket) {
printf("----[CLIENT] Client \"%d\" not my turn\n", id);
if (justArrived) {
justArrived = 0;
printf("----[CLIENT] Client \"%d\" seat to wait\n", id);
}
pthread_cond_wait(&NextClient, &Mutex);
}
printf("----[CLIENT] Client \"%d\" my turn\n", id);
if (justArrived)
printf("----[CLIENT] Client \"%d\" do not need to seat to wait\n", id);

pthread_cond_signal(&HelloBarber);

pthread_cond_wait(&NextClient, &Mutex);
printf("----[CLIENT] Client \"%d\" done for me\n", id);
pthread_mutex_unlock(&Mutex);

return NULL;
}

void * barber(void * dummy)
{
pthread_mutex_lock(&Mutex);

puts("[BARBER] ready");
CurrentTicket = 1;
pthread_cond_signal(&NextClient);

for (;;) {
printf("[BARBER] screen indicates ticket %d\n", CurrentTicket);

int sleep = (Nseat == NSEAT + 1);

if (sleep)
puts("[BARBER] no client, time to sleep");

pthread_cond_wait(&HelloBarber, &Mutex); /* in all cases to be sure next client had time to see his turn */

if (sleep)
puts("[BARBER] woken up by a client");
puts("[BARBER] cutting hairs");

pthread_mutex_unlock(&Mutex);
randomSleep(3); /* time to cut hairs of current client */
pthread_mutex_lock(&Mutex);

puts("[BARBER] haircut done");
Nseat += 1;
CurrentTicket += 1;
pthread_cond_broadcast(&NextClient);
}
}

int main()
{
pthread_t barber_thread, client_thread[NCLIENT];
int i, id[NCLIENT];

Seed = time(NULL);

if (pthread_create(&barber_thread, NULL, barber, NULL ) < 0){
fprintf(stderr, "[MAIN] Error creating \"barber\" thread!\n");
exit(EXIT_FAILURE);
}

pthread_mutex_lock(&Mutex);
if (CurrentTicket == 0) {
/* wait barber ready */
pthread_cond_wait(&NextClient, &Mutex);
}
pthread_mutex_unlock(&Mutex);

for (i = 0; i < NCLIENT; ++i) {
id[i] = i+1;
if (pthread_create(&client_thread[i], NULL, client, &id[i]) < 0) {
fprintf(stderr, "[MAIN] Error creating \"client %d\" thread!\n", id[i]);
exit(EXIT_FAILURE);
}
}

for (i = 0; i < NCLIENT; i++) {
pthread_join(client_thread[i], NULL);

pthread_mutex_lock(&Mutex);
printf("[MAIN] Joined client \"%d\"\n", i+1);
pthread_mutex_unlock(&Mutex);
}

return 0;
}

Compilation and example of execution :

pi@raspberrypi:~ $ gcc -Wall barber.c  -lpthread
pi@raspberrypi:~ $ ./a.out
[BARBER] ready
[BARBER] screen indicates ticket 1
[BARBER] no client, time to sleep
----[CLIENT] Client "7" got ticket number 1
----[CLIENT] Client "7" my turn
----[CLIENT] Client "7" do not need to seat to wait
[BARBER] woken up by a client
[BARBER] cutting hairs
----[CLIENT] Client "6" got ticket number 2
----[CLIENT] Client "6" not my turn
----[CLIENT] Client "6" seat to wait
----[CLIENT] Client "5" got ticket number 3
----[CLIENT] Client "5" not my turn
----[CLIENT] Client "5" seat to wait
----[CLIENT] Client "1" got ticket number 4
----[CLIENT] Client "1" not my turn
----[CLIENT] Client "1" seat to wait
----[CLIENT] Client "3" got ticket number 5
----[CLIENT] Client "3" not my turn
----[CLIENT] Client "3" seat to wait
[BARBER] haircut done
[BARBER] screen indicates ticket 2
----[CLIENT] Client "7" done for me
----[CLIENT] Client "3" not my turn
----[CLIENT] Client "1" not my turn
----[CLIENT] Client "6" my turn
----[CLIENT] Client "5" not my turn
[BARBER] cutting hairs
----[CLIENT] Client "9" got ticket number 6
----[CLIENT] Client "9" not my turn
----[CLIENT] Client "9" seat to wait
----[CLIENT] Client "8" got ticket number 7
----[CLIENT] Client "8" not my turn
----[CLIENT] Client "8" seat to wait
----[CLIENT] Client "4" no place, will try again later
----[CLIENT] Client "2" no place, will try again later
----[CLIENT] Client "10" no place, will try again later
[BARBER] haircut done
[BARBER] screen indicates ticket 3
----[CLIENT] Client "8" not my turn
----[CLIENT] Client "5" my turn
----[CLIENT] Client "9" not my turn
----[CLIENT] Client "1" not my turn
----[CLIENT] Client "6" done for me
[BARBER] cutting hairs
----[CLIENT] Client "3" not my turn
[BARBER] haircut done
[BARBER] screen indicates ticket 4
----[CLIENT] Client "9" not my turn
----[CLIENT] Client "1" my turn
----[CLIENT] Client "3" not my turn
----[CLIENT] Client "5" done for me
----[CLIENT] Client "8" not my turn
[BARBER] cutting hairs
[BARBER] haircut done
[BARBER] screen indicates ticket 5
----[CLIENT] Client "8" not my turn
----[CLIENT] Client "1" done for me
[MAIN] Joined client "1"
----[CLIENT] Client "9" not my turn
----[CLIENT] Client "3" my turn
[BARBER] cutting hairs
----[CLIENT] Client "10" got ticket number 8
----[CLIENT] Client "10" not my turn
----[CLIENT] Client "10" seat to wait
----[CLIENT] Client "2" got ticket number 9
----[CLIENT] Client "2" not my turn
----[CLIENT] Client "2" seat to wait
----[CLIENT] Client "4" got ticket number 10
----[CLIENT] Client "4" not my turn
----[CLIENT] Client "4" seat to wait
[BARBER] haircut done
[BARBER] screen indicates ticket 6
----[CLIENT] Client "2" not my turn
----[CLIENT] Client "3" done for me
----[CLIENT] Client "4" not my turn
----[CLIENT] Client "8" not my turn
----[CLIENT] Client "9" my turn
----[CLIENT] Client "10" not my turn
[BARBER] cutting hairs
[BARBER] haircut done
[BARBER] screen indicates ticket 7
----[CLIENT] Client "2" not my turn
----[CLIENT] Client "9" done for me
----[CLIENT] Client "4" not my turn
----[CLIENT] Client "8" my turn
[BARBER] cutting hairs
----[CLIENT] Client "10" not my turn
[BARBER] haircut done
[BARBER] screen indicates ticket 8
----[CLIENT] Client "10" my turn
[BARBER] cutting hairs
----[CLIENT] Client "2" not my turn
----[CLIENT] Client "8" done for me
----[CLIENT] Client "4" not my turn
[BARBER] haircut done
[BARBER] screen indicates ticket 9
----[CLIENT] Client "10" done for me
----[CLIENT] Client "2" my turn
----[CLIENT] Client "4" not my turn
[BARBER] cutting hairs
[BARBER] haircut done
[BARBER] screen indicates ticket 10
----[CLIENT] Client "2" done for me
----[CLIENT] Client "4" my turn
[BARBER] cutting hairs
[MAIN] Joined client "2"
[MAIN] Joined client "3"
[BARBER] haircut done
[BARBER] screen indicates ticket 11
[BARBER] no client, time to sleep
----[CLIENT] Client "4" done for me
[MAIN] Joined client "4"
[MAIN] Joined client "5"
[MAIN] Joined client "6"
[MAIN] Joined client "7"
[MAIN] Joined client "8"
[MAIN] Joined client "9"
[MAIN] Joined client "10"
pi@raspberrypi:~ $

How do I wake up a sleeping pthread?

What you are looking for is pthread_cond_t object, pthread_cond_timedwait and pthread_cond_wait functions. You could create conditional variable isThereAnyTaskToDo and wait on it in event thread. When new event is added, you just wake event thread with pthread_cond_signal().



Related Topics



Leave a reply



Submit