1 /* $OpenBSD: sem_timedwait.c,v 1.4 2019/12/20 23:39:01 cheloha Exp $ */ 2 /* 3 * Martin Pieuchot <mpi@openbsd.org>, 2011. Public Domain. 4 */ 5 6 #include <sys/types.h> 7 #include <sys/sysctl.h> 8 9 #include <err.h> 10 #include <errno.h> 11 #include <unistd.h> 12 #include <semaphore.h> 13 #include <signal.h> 14 #include <pthread.h> 15 #include "test.h" 16 17 18 void *waiter(void *arg); 19 20 void 21 handler(int sig) 22 { 23 static char message[] = "got sig\n"; 24 25 write(STDERR_FILENO, message, sizeof(message) - 1); 26 } 27 28 sem_t sem; 29 volatile int posted = 0, eintr_ok = 0; 30 31 int 32 main(int argc, char **argv) 33 { 34 pthread_t th; 35 struct clockinfo info; 36 struct sigaction sa; 37 struct timespec delay, ts, ts2; 38 size_t infosize = sizeof(info); 39 int mib[] = { CTL_KERN, KERN_CLOCKRATE }; 40 41 CHECKr(clock_gettime(CLOCK_REALTIME, &ts)); 42 ts.tv_sec += 3; 43 CHECKn(sem_timedwait(&sem, &ts)); 44 ASSERT(errno == EINVAL); 45 46 CHECKr(sem_init(&sem, 0, 0)); 47 48 CHECKr(pthread_create(&th, NULL, waiter, &sem)); 49 50 sleep(1); 51 52 printf("expect: sem_destroy on semaphore with waiters!\n"); 53 CHECKn(sem_destroy(&sem)); 54 ASSERT(errno == EBUSY); 55 56 posted = 1; 57 CHECKr(sem_post(&sem)); 58 CHECKr(pthread_join(th, NULL)); 59 60 /* test that sem_timedwait() resumes after handling a signal */ 61 memset(&sa, 0, sizeof sa); 62 sa.sa_handler = &handler; 63 sigemptyset(&sa.sa_mask); 64 sa.sa_flags = SA_RESTART; 65 if (sigaction(SIGUSR1, &sa, NULL)) 66 err(1, "sigaction"); 67 posted = 0; 68 CHECKr(pthread_create(&th, NULL, waiter, &sem)); 69 sleep(1); 70 fprintf(stderr, "sending sig\n"); 71 eintr_ok = 1; 72 pthread_kill(th, SIGUSR1); 73 sleep(1); 74 fprintf(stderr, "posting\n"); 75 posted = 1; 76 eintr_ok = 0; 77 CHECKr(sem_post(&sem)); 78 CHECKr(pthread_join(th, NULL)); 79 80 CHECKr(clock_gettime(CLOCK_REALTIME, &ts)); 81 ts.tv_sec += 2; 82 CHECKn(sem_timedwait(&sem, &ts)); 83 ASSERT(errno == ETIMEDOUT); 84 CHECKr(clock_gettime(CLOCK_REALTIME, &ts2)); 85 86 fprintf(stderr, "timeout: expected %lld.%09ld actual %lld.%09ld\n", 87 ts.tv_sec, ts.tv_nsec, ts2.tv_sec, ts2.tv_nsec); 88 89 /* Check that we don't return early. */ 90 ASSERT(timespeccmp(&ts, &ts2, <=)); 91 92 /* 93 * Check that we don't return unusually late. Something might be 94 * off if the wait returns more than two ticks after our timeout. 95 */ 96 CHECKr(sysctl(mib, 2, &info, &infosize, NULL, 0)); 97 delay.tv_sec = 0; 98 delay.tv_nsec = info.tick * 1000; /* usecs -> nsecs */ 99 timespecadd(&delay, &delay, &delay); /* up to two ticks of delay */ 100 timespecadd(&ts, &delay, &ts); 101 ASSERT(timespeccmp(&ts2, &ts, <=)); 102 103 CHECKe(sem_destroy(&sem)); 104 105 SUCCEED; 106 } 107 108 void * 109 waiter(void *arg) 110 { 111 sem_t *semp = arg; 112 struct timespec ts; 113 int value; 114 int r; 115 116 CHECKr(clock_gettime(CLOCK_REALTIME, &ts)); 117 ts.tv_sec += 3; 118 r = sem_timedwait(semp, &ts); 119 CHECKr(sem_getvalue(semp, &value)); 120 if (r == 0) { 121 ASSERT(value == 0); 122 ASSERT(posted != 0); 123 } else { 124 ASSERT(r == -1); 125 ASSERT(errno == EINTR); 126 ASSERT(eintr_ok); 127 if (posted) 128 ASSERT(value == 1); 129 else 130 ASSERT(value == 0); 131 } 132 133 return (NULL); 134 } 135