• Andrew Morton's avatar
    [PATCH] fix posix-timers to have proper per-process scope · 0e568881
    Andrew Morton authored
    From: Roland McGrath <roland@redhat.com>
    
    The posix-timers implementation associates timers with the creating thread
    and destroys timers when their creator thread dies.  POSIX clearly
    specifies that these timers are per-process, and a timer should not be torn
    down when the thread that created it exits.  I hope there won't be any
    controversy on what the correct semantics are here, since POSIX is clear
    and the Linux feature is called "posix-timers".
    
    The attached program built with NPTL -lrt -lpthread demonstrates the bug.
    The program is correct by POSIX, but fails on Linux.  Note that a until
    just the other day, NPTL had a trivial bug that always disabled its use of
    kernel timer syscalls (check strace for lack of timer_create/SYS_259).  So
    unless you have built your own NPTL libs very recently, you probably won't
    see the kernel calls actually used by this program.
    
    Also attached is my patch to fix this.  It (you guessed it) moves the
    posix_timers field from task_struct to signal_struct.  Access is now
    governed by the siglock instead of the task lock.  exit_itimers is called
    from __exit_signal, i.e.  only on the death of the last thread in the
    group, rather than from do_exit for every thread.  Timers' it_process
    fields store the group leader's pointer, which won't die.  For the case of
    SIGEV_THREAD_ID, I hold a ref on the task_struct for it_process to stay
    robust in case the target thread dies; the ref is released and the dangling
    pointer cleared when the timer fires and the target thread is dead.  (This
    should only come up in a buggy user program, so noone cares exactly how the
    kernel handles that case.  But I think what I did is robust and sensical.)
    
    /* Test for bogus per-thread deletion of timers.  */
    
    #include <stdio.h>
    #include <error.h>
    #include <time.h>
    #include <signal.h>
    #include <stdint.h>
    #include <sys/time.h>
    #include <sys/resource.h>
    #include <unistd.h>
    #include <pthread.h>
    
    /* Creating timers in another thread should work too.  */
    static void *do_timer_create(void *arg)
    {
    	struct sigevent *const sigev = arg;
    	timer_t *const timerId = sigev->sigev_value.sival_ptr;
    	if (timer_create(CLOCK_REALTIME, sigev, timerId) < 0) {
    		perror("timer_create");
    		return NULL;
    	}
    	return timerId;
    }
    
    int main(void)
    {
    	int i, res;
    	timer_t timerId;
    	struct itimerspec itval;
    	struct sigevent sigev;
    
    	itval.it_interval.tv_sec = 2;
    	itval.it_interval.tv_nsec = 0;
    	itval.it_value.tv_sec = 2;
    	itval.it_value.tv_nsec = 0;
    
    	sigev.sigev_notify = SIGEV_SIGNAL;
    	sigev.sigev_signo = SIGALRM;
    	sigev.sigev_value.sival_ptr = (void *)&timerId;
    
    	for (i = 0; i < 100; i++) {
    		printf("cnt = %d\n", i);
    
    		pthread_t thr;
    		res = pthread_create(&thr, NULL, &do_timer_create, &sigev);
    		if (res) {
    			error(0, res, "pthread_create");
    			continue;
    		}
    		void *val;
    		res = pthread_join(thr, &val);
    		if (res) {
    			error(0, res, "pthread_join");
    			continue;
    		}
    		if (val == NULL)
    			continue;
    
    		res = timer_settime(timerId, 0, &itval, NULL);
    		if (res < 0)
    			perror("timer_settime");
    
    		res = timer_delete(timerId);
    		if (res < 0)
    			perror("timer_delete");
    	}
    
    	return 0;
    }
    0e568881
exit.c 28.8 KB