Question:
I am writing a program that uses shared memory and semaphores for ipc. There is one main server process that creates the shared memory and semaphores. Any number of client processes can attach to the shared memory and read and write to it when allowed. The semaphores provide the blocking mechanism to control reads and writes. Everything works fine except when a I try to terminate a client. The semaphore block to access the shared memory is in a thread and on process termination I have no way to release the semaphore block so the thread exits correctly. How would I go about this? This is for Linux.
To be specific, there is one shm and two sems. The first sem blocks writing, and the second blocks reading. When a client has something to write, it waits for the write sem to be 0, then sets it to 1, writes, then sets the read sem to 0 which releases the waiting server to read what the client wrote. once read the server sets the write sem back to 0 and the next client in line gets to write. It hangs on a semop call which releases when read sem is 0. This semop call is in a thread and I need to figure out how to exit that thread correctly before letting the main thread terminate.
Here is an example of what i want to do but isn't working (the sleep is pretending to be the hanging semop call):
#include <stdlib.h> #include <errno.h> #include <pthread.h> #include <signal.h> #include <stdio.h> #include <unistd.h> void termination_handler (int signum) { printf( "Got Signal\n" ); } void *threadfunc( void *parm ) { struct sigaction action; action.sa_handler = termination_handler; sigemptyset( &action.sa_mask ); action.sa_flags = 0; sigaction( SIGUSR1, &action, NULL ); printf("Thread executing\n"); sleep( 100 ); // pretending to be the semaphore pthread_exit( NULL ); } int main() { int status; pthread_t threadid; int thread_stat; status = pthread_create( &threadid, NULL, threadfunc, NULL ); if ( status < 0) { perror("pthread_create failed"); exit(1); } sleep( 5 ); status = pthread_kill( threadid, SIGUSR1 ); if ( status < 0 ) perror("pthread_kill failed"); status = pthread_join( threadid, (void *)&thread_stat ); if ( status < 0 ) perror("pthread_join failed"); exit( 0 ); }
Solution:1
He said, this is for Linux.
It would be useful if you could say exactly how are you doing it. I assume you are blocking in sem_wait or sem_timedwait. If your thread is blocking there and you want to interrupt it, you can use pthread_kill.
pthread_kill(blocking_thread_id, SIGUSR1);
Of course, you need to setup the proper signal handler (man sigaction) to catch SIGUSR1 and you need to check the return code of sem_wait() for EINTR, in which case you can do whatever you want to do knowing that you were interrupted and did not get the lock.
In the case you are using processes you would use simply kill() and not pthread_kill() providing the process id. (sorry, initially I misread and thought you were using threads)
Solution:2
I have two and a half answers for you. :)
First, your example code works for me (on Linux): the pthread_kill successfully
EINTRupts the worker thread's sleep as expected after about five seconds, as revealed with a few printfs and remembering the return value of sleep. AFAICT, if you want to signal-interrupt a specific thread, you've done it.
Second, try
SEM_UNDO. This flag may be set in the sem_flg member passed in the sembuf argument semop, and it will, as the name suggests, undo semaphore adjustments upon process termination. IIUC, when you kill a client, that client leaves a semaphore inappropriately locked.
SEM_UNDO was made for just this circumstance.
Finally and respectfully, have you perhaps inverted the logic of semaphores here? As I read your question, a semval of zero indicates "resource free" and a semval of one is "resource locked" (quote: "...[a client] waits for the write sem to be 0, then sets it to 1, writes..."). However, if two or more writing clients are waiting for a SysV sem to drop to zero, they will all be released together when that occurs. That's rather an unpleasant race condition, which might at the least result in unanticipated semaphore decrements and increments.
Solution:3
Depending on your environment perhaps you could only try to take the semaphore with a timeout. After each timeout check if a close thread has been requested and simply give up and shutdown.
Solution:4
It may not be the best idea to use blocking mutexes/semaphores if operations on protected area may last too long for your purposes.
You can solve the issue by putting read and write requests to a queue (linked list, for example) and let the first in the queue to operate on protected area and remove it from the list once it enters the area.
In case of read-only op you may access other reads enter the protected area as well as long as the first operation is read only. When the first operation is write, protected area must be empty before allowing it to access.
List modifications must be protected by mutex (or something alike) but that is near constant time and you probably can pay that.
When threads are sitting in a queue, each has its private condition variable which you can use to wake up any of them. Condition variable must be protected by mutex too. You may store condition variable, mutex etc into a structure and put the into an array or list and store thread id with each so that it will be easy to find the thread you want to wake up.
Once thread wakes up, it first checks what was the reason it had to wake up. If exit flag is set, then the thread know to exit.
Note:If u also have question or solution just comment us below or mail us on toontricks1994@gmail.com
EmoticonEmoticon