views:

902

answers:

4

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 );
}
+1  A: 

What platform? Windows/Mac/Unix? Unix has sigterm (or kill -9) but windows doesn't.

Kelly French
+3  A: 

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)

Moy
As a small remark, *ianion* is using SysV rather than POSIX semaphores, that is, `semop()` rather than `sem_wait()` and friends.
pilcrow
A: 

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.

Spence
+1  A: 

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.

pilcrow
I don't think that when semaphore drops to zero all blocked semops get released in my implementation. I have each semop call perform two operations, the first performs a wait for zero and the second sets it back to one. It was my understanding that if I have 5 of these operations queued all waiting for zero, once the semaphore drops to zero its a FIFO on all the waiting semop calls, therefore the first of the five performs the two operations and by the time you reach the second in queue the semaphore isn't zero anymore so it waits again. this seems to be working fine for me.
Ah, I didn't grok that a single `semop()` performed "await-zero" and "post". (Do you have full example code somewhere?) Yes, that'll work, though I confess I find this usage a bit awkward. Thanks.
pilcrow