I found unexpected behavior when i tried to lock mutex with 'trylock' when the previous owner is dead
without unlock it.
The first process that uses 'trylock' gets EOWNERDEAD state as expected, so uses the 'unlock' function to relase the mutex:
lock_status = pthread_mutex_trylock(mutex);
if (lock_status == EOWNERDEAD)
{
pthread_mutex_unlock(mutex);
printf("P1 mutex status: EOWNERDEAD\n");
}
else if (lock_status == ENOTRECOVERABLE)
{printf("P1 mutex status: ENOTRECOVERABLE\n");}
else if (lock_status == EBUSY)
{printf("P1 mutex status: EBUSY\n");}
else {printf("P1 mutex status: %d\n", lock_status);}
The second executes the same code obtaining the ENOTRECOVERABLE state, as expected.
But when the third executes the same operations gets the EBUSY state and this is unexpected.
The state should be ENOTRECOVERABLE as reported here
I tried to use the 'lock' function instead 'trylock' and the correct state was returned.
Is that a bug? in my purpouse i have to check the mutex status before doing any operation, so i cannot use the 'lock' function or a deadlock may occur.
I'm thinking to destroy the mutex when the ENOTRECOVERABLE state has returned:
else if (lock_status == ENOTRECOVERABLE)
{
pthread_mutex_destroy(mutex);
printf("P1 mutex status: ENOTRECOVERABLE\n");
}
But maybe there is a better way than this drastic solution, could it be? Full code:
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
main(void)
{
int lock_status, mutex_fdesc;
pid_t process_id;
pthread_mutex_t* mutex;
pthread_mutexattr_t m_att;
//Process 1 die with locked mutex
process_id = fork();
if (process_id < 0) {exit(0);}
if (process_id == 0)
{
usleep(100000);
mutex_fdesc = shm_open("/mutex", O_RDWR, S_IRWXU | S_IRWXG);
mutex = (pthread_mutex_t*)mmap(NULL, sizeof(pthread_mutex_t),
PROT_READ | PROT_WRITE, MAP_SHARED, mutex_fdesc, 0);
close(mutex_fdesc);
pthread_mutex_lock(mutex);
exit(0);
}
///Process 2 try to lock mutex and gets EOWNERDEAD then make an unlock
process_id = fork();
if (process_id < 0) {exit(0);}
if (process_id == 0)
{
usleep(200000);
mutex_fdesc = shm_open("/mutex", O_RDWR, S_IRWXU | S_IRWXG);
mutex = (pthread_mutex_t*)mmap(NULL, sizeof(pthread_mutex_t),
PROT_READ | PROT_WRITE, MAP_SHARED, mutex_fdesc, 0);
close(mutex_fdesc);
lock_status = pthread_mutex_trylock(mutex);
if (lock_status == EOWNERDEAD)
{
pthread_mutex_unlock(mutex);
printf("P2 mutex status: EOWNERDEAD\n");}
else if (lock_status == ENOTRECOVERABLE)
{printf("P2 mutex status: ENOTRECOVERABLE\n");}
else if (lock_status == EBUSY)
{printf("P2 mutex status: EBUSY\n");}
else {printf("P2 mutex status: %d\n", lock_status);}
exit(0);
}
///Process 2 try to lock mutex and gets ENOTRECOVERABLE then do nothing
process_id = fork();
if (process_id < 0) {exit(0);}
if (process_id == 0)
{
usleep(400000);
mutex_fdesc = shm_open("/mutex", O_RDWR, S_IRWXU | S_IRWXG);
mutex = (pthread_mutex_t*)mmap(NULL, sizeof(pthread_mutex_t),
PROT_READ | PROT_WRITE, MAP_SHARED, mutex_fdesc, 0);
close(mutex_fdesc);
lock_status = pthread_mutex_trylock(mutex);
if (lock_status == EOWNERDEAD)
{
pthread_mutex_unlock(mutex);
printf("P3 mutex status: EOWNERDEAD\n");}
else if (lock_status == ENOTRECOVERABLE)
{printf("P3 mutex status: ENOTRECOVERABLE\n");}
else if (lock_status == EBUSY)
{printf("P3 mutex status: EBUSY\n");}
else {printf("P3 mutex status: %d\n", lock_status);}
exit(0);
}
mutex_fdesc = shm_open("/mutex", O_RDWR | O_CREAT | O_EXCL, S_IRWXU | S_IRWXG);
ftruncate(mutex_fdesc, sizeof(pthread_mutex_t));
mutex = (pthread_mutex_t*)mmap(NULL, sizeof(pthread_mutex_t),
PROT_READ | PROT_WRITE, MAP_SHARED, mutex_fdesc, 0);
close(mutex_fdesc);
pthread_mutexattr_init(&m_att);
pthread_mutexattr_setpshared(&m_att, PTHREAD_PROCESS_SHARED);
pthread_mutexattr_setrobust(&m_att, PTHREAD_MUTEX_ROBUST);
pthread_mutex_init(mutex, &m_att);
pthread_mutexattr_destroy(&m_att);
///Parent process try to lock the mutex and gets EBUSY
usleep(800000);
lock_status = pthread_mutex_trylock(mutex);
if (lock_status == EOWNERDEAD)
{printf("Pparent mutex status: EOWNERDEAD\n");}
else if (lock_status == ENOTRECOVERABLE)
{printf("Pparent mutex status: ENOTRECOVERABLE\n");}
else if (lock_status == EBUSY)
{printf("Pparent mutex status: EBUSY\n");}
else {printf("Pparent mutex status: %d\n", lock_status);}
pthread_mutex_destroy(mutex);
munmap((void*)mutex, sizeof(pthread_mutex_t));
shm_unlink("/mutex");
wait(NULL);
wait(NULL);
wait(NULL);
exit(0);
}