I have a C program where I run 3 branches of code: 2 that i started through pthread_create and the normal one.
I am wondering how to correctly protect it if my second thread fails to be created somehow.
Here is my code:
# include <pthread.h>
# include <stdio.h>
# include <stdlib.h>
# include <semaphore.h>
# include <errno.h>
typedef struct s_philo{
sem_t *first;
sem_t *second;
sem_t *stop_A;
sem_t *stop_B;
pthread_t A_thread;
pthread_t B_thread;
} t_philo;
void sem_close_safe(sem_t *sem)
{
if (sem_close(sem) == -1)
printf("Failed to close semaphore\n");
}
int free_philo(t_philo *philo)
{
if (philo->first)
sem_close_safe(philo->first);
if (philo->second)
sem_close_safe(philo->second);
if (philo->stop_A)
sem_close_safe(philo->stop_A);
if (philo->stop_B)
sem_close_safe(philo->stop_B);
free(philo);
return (1);
}
void *check_philo(t_philo *philo)
{
void *check;
check = philo;
if (!philo->first || !philo->second || !philo->stop_A || !philo->stop_B)
check = NULL;
return (check);
}
sem_t *sem_open_new_safe(const char *name, unsigned int value)
{
sem_t *sem;
sem = sem_open(name, O_CREAT | O_EXCL, 0644, value);
if (errno == EEXIST)
{
if (sem_unlink(name) == -1)
return (NULL);
sem = sem_open(name, O_CREAT | O_EXCL, 0644, value);
}
if (sem == SEM_FAILED)
return (NULL);
if (sem_unlink(name) == -1)
{
sem_close_safe(sem);
return (NULL);
}
return (sem);
}
void *A(void *p)
{
t_philo *philo;
philo = (t_philo *) p;
sem_wait(philo->stop_A);
sem_post(philo->stop_A);
return (NULL);
}
void *B(void *p)
{
t_philo *philo;
philo = (t_philo *) p;
sem_wait(philo->stop_B);
sem_post(philo->stop_B);
return (NULL);
}
int main(void)
{
t_philo *philo;
int i;
philo = malloc(sizeof(*philo));
philo->first = sem_open_new_safe("/first", 1);
philo->second = sem_open_new_safe("/second", 1);
philo->stop_A = sem_open_new_safe("/stop_A", 0);
philo->stop_B = sem_open_new_safe("/stop_B", 0);
if (!check_philo(philo))
return (free_philo(philo));
if (pthread_create(&philo->A_thread, NULL, &A, (void *)philo))
return (free_philo(philo));
if (pthread_create(&philo->B_thread, NULL, &B, (void *)philo))
return (free_philo(philo));
i = 0;
while (i++ < 100)
{
if (sem_wait(philo->first) == -1)
sem_post(philo->stop_B);
if (sem_wait(philo->second) == -1)
sem_post(philo->stop_A);
printf("%d\n", i);
sem_post(philo->second);
sem_post(philo->first);
}
sem_post(philo->stop_B);
sem_post(philo->stop_A);
pthread_join(philo->A_thread, NULL);
pthread_join(philo->B_thread, NULL);
free_philo(philo);
return (0);
}
Both of my A and B threads wait for semaphores on their first lines of code so they will never return on their own if I do not post these semaphores.
Should I pthread_join thread A ? Should I manually post some semaphores to force thread A to continue its execution and return ? Or maybe I should use pthread_detach ? I am a bit lost.
Edit: I have been asked to post more code to make it executable, but I have a lot of lines of code and it would just drown the above one. What I am looking for (if it exists) is not a guided code-specific answer, but more of a best practice to gracefully handle pthread_create errors.
Edit 2: I added the least code I could to make it runnable
The general case looks something like this pseudocode:
if (!setup_A()) {
exit(FAILURE);
}
if (!setup_B()) {
teardown_A();
exit(FAILURE);
}
if (!setup_C()) {
teardown_B();
teardown_A();
exit(FAILURE);
}
do_successful_processing();
teardown_C();
teardown_B();
teardown_A();
and you're effectively asking how to write teardown_B()
.
The general solution (assuming you can't just switch to C++ to use proper destructors and RAII) does not exist. The teardown is just as specific to the details of A, B, C and your application as the setup is.
I am wondering how to correctly protect it if my second thread fails to be created somehow.
The proximate answer is to tell the thread to quit (in some application-specific way), and then to join it.
The actual semantics of requesting a shutdown are specific to your code, since you wrote the function that thread is executing. Here, it should be sufficient to sem_post
the semaphone thread A
is waiting on.
NB. DO NOT use pthread_kill
to shut down threads, if you can possibly avoid it. It's much better to write clean shutdown handling explicitly.