#include #include #include #include #include #include #include #include #include #include #include #include #include #include "common/common.h" #include /* * Copyright (c) 2011 Luka Marčetić * * Redistribution and use in source and binary forms, with or without * modification, are permitted. * * There's ABSOLUTELY NO WARRANTY, express or implied. */ /** ** \file ** Tests that EINTR isn't returned by interrupted pthread_* functions for which ** this is a specified behavior ** tests: pthread_create, ** depends: open,pipe,close, mmap, sigaction, kill,waitpid,nanosleep, fprintf, ** setjmp, sched_yield **/ #define NANOSLEEP_MAX 100 unsigned char *exit_child; //a barrier for child processes //signal handler that just returns static void handler(int sig) { ++sig; // -Wunused-parameter return; } //waits for *exit_child to be set, then returns static void child_wait(void) { while(!*exit_child) sched_yield(); return; } static void child_wait_vp(void* foo) { child_wait(); ++foo; // -Wunused-parameter return; } /** ** Executes a function whose pointer was passed to it, before exitting ** \param fun a void pointer to the function to execute, or NULL for none **/ static void* thread(void *fun) { if(fun != NULL) { pthread_cleanup_push(child_wait_vp, NULL); } return NULL; } int main() { char *s; pid_t pid; pthread_t tid; pthread_key_t tkey; struct timespec ns = {.tv_nsec=0}; unsigned int i, nr_fun, failed; const int fd = open("/dev/zero", O_RDWR); int sig, ret[2]={0,0}, stat, pfd[2]; const char *function[] = { "pthread_create", "pthread_cancel", "pthread_once", "pthread_setspecific", "pthread_key_delete", "pthread_join", "pthread_atfork", "pthread_sigmask", "pthread_equal", "pthread_setschedprio", "pthread_setconcurrency", "pthread_create", "pthread_setconcurrency", "pthread_detach", "pthread_sigmask", "pthread_setspecific", "pthread_key_create", "pthread_rwlock_unlock", "pthread_kill", }; struct { unsigned int kill:1, wait:1; } err, no_err = {.wait=0}; struct sigaction act = {.sa_handler=handler, .sa_flags=SA_NODEFER}; exit_child = mmap(NULL,1,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0); failed = 0; for (i=0; i < (nr_fun=sizeof(function)/sizeof(*function)); ++i) { memset(&err, 0, sizeof(err)); pipe(pfd); *exit_child = 0; if(!(pid = fork())) { sigaction(SIGABRT, &act, NULL); sig = 0; switch (i) { case 1: pthread_create(&tid, NULL, thread, child_wait); break; case 3: case 4: pthread_key_create(&tkey, child_wait_vp); break; } close(pfd[0]); close(pfd[1]); //ready switch (i) { case 0: while(sig != EINTR && !*exit_child) sig = pthread_create(&tid, NULL, thread, NULL); break; case 1: while(sig != EINTR && !*exit_child) sig = pthread_cancel(tid); break; case 2: sig = pthread_once((pthread_once_t []){ PTHREAD_ONCE_INIT }, child_wait); break; case 3: while(sig != EINTR && !*exit_child) sig = pthread_setspecific(tkey, NULL); /*no break;*/ case 4: pthread_key_delete(tkey); break; default: fprintf(stdout, "BUG: test i=%d missing!\n", i); break; } return (sig == EINTR); } if (!pid) { fprintf(stdout, "BUG: child i=%d escaped!\n", i); exit(0); //exit/contain child }else if (pid == -1) { fprintf(stderr, "A call to fork() nr %d/%d yeilded -1 (errno=%s)!\n", i+1, nr_fun, (s = e_name(errno)) ); free(s); close (pfd[0]); close (pfd[1]); failed += 1; }else{ close (pfd[1]); read(pfd[0], NULL, 1); //wait for the child to become ready close (pfd[0]); //Send signals in intervals of microseconds: ns.tv_nsec = NANOSLEEP_MAX; while( (ret[0] = kill(pid, SIGABRT)) == 0 && (ret[1] = waitpid(pid, &stat, WNOHANG)) == 0 && --ns.tv_nsec ) nanosleep(&ns, NULL); *exit_child=1; //let the child loose from its loop (if any) //Process errors: s = e_name(errno); err.kill = (ret[0] != 0); err.wait = (ret[1] != 0 && ret[1] != pid); if (!(!memcmp(&err, &no_err, sizeof(err)))) { ++failed; if (err.kill) fprintf(stderr, "A call to kill() returned %d, errno=%s\n", ret[0], s ); if (err.wait) fprintf(stderr, "A call to waitpid() returned %d, errno=%s\n", ret[1], s ); }else if (WIFEXITED(stat)) { *ret = WEXITSTATUS(stat); //repurpose ret[0] if (*ret == 1) { ++failed; fprintf(stderr, "%s() returned EINTR\n", function[i]); } else if (*ret != 0) fprintf(stdout, "BUG: child nr %d ended with status %d!\n", i, *ret ); }else{ fprintf(stderr, "%s()'s process crashed!\n", function[i]); if (WIFSIGNALED(stat)) fprintf(stderr,"\tTerminating signal: %d\n",WTERMSIG(stat)); } free(s); } } return failed; }