1 /* $OpenBSD: cancel.c,v 1.6 2003/07/31 21:48:04 deraadt Exp $ */ 2 /* David Leonard <d@openbsd.org>, 1999. Public Domain. */ 3 4 #include <pthread.h> 5 #include <pthread_np.h> 6 #include <unistd.h> 7 #include <stdio.h> 8 #include <fcntl.h> 9 #include <stdlib.h> 10 #include "test.h" 11 12 static pthread_cond_t cond; 13 static pthread_mutex_t mutex; 14 static struct timespec expiretime; 15 16 static volatile int pv_state = 0; 17 18 static void 19 p(void) 20 { 21 CHECKr(pthread_mutex_lock(&mutex)); 22 if (pv_state <= 0) { 23 CHECKr(pthread_cond_timedwait(&cond, &mutex, &expiretime)); 24 } 25 pv_state--; 26 CHECKr(pthread_mutex_unlock(&mutex)); 27 } 28 29 static void 30 v(void) 31 { 32 int needsignal; 33 34 CHECKr(pthread_mutex_lock(&mutex)); 35 pv_state++; 36 needsignal = (pv_state == 1); 37 if (needsignal) 38 CHECKr(pthread_cond_signal(&cond)); 39 CHECKr(pthread_mutex_unlock(&mutex)); 40 } 41 42 static void 43 c1handler(void *arg) 44 { 45 CHECKe(close(*(int *)arg)); 46 v(); 47 } 48 49 static void * 50 child1fn(void *arg) 51 { 52 int fd; 53 char buf[1024]; 54 int len; 55 56 SET_NAME("c1"); 57 CHECKr(pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL)); 58 /* something that will block */ 59 CHECKe(fd = open("/dev/tty", O_RDONLY)); 60 pthread_cleanup_push(c1handler, (void *)&fd); 61 v(); 62 while (1) { 63 CHECKe(len = read(fd, &buf, sizeof buf)); 64 printf("child 1 read %d bytes\n", len); 65 } 66 PANIC("child 1"); 67 } 68 69 static int c2_in_test = 0; 70 71 static void 72 c2handler(void *arg) 73 { 74 ASSERT(c2_in_test); 75 v(); 76 } 77 78 static int message_seen = 0; 79 static void * 80 child2fn(void *arg) 81 { 82 SET_NAME("c2"); 83 84 CHECKr(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL)); 85 pthread_cleanup_push(c2handler, NULL); 86 v(); 87 88 while (1) { 89 struct timespec now; 90 struct timespec end; 91 92 /* 93 * XXX Be careful not to call any cancellation points 94 * until pthread_testcancel() 95 */ 96 97 CHECKe(clock_gettime(CLOCK_REALTIME, &end)); 98 end.tv_sec ++; 99 100 while (1) { 101 CHECKe(clock_gettime(CLOCK_REALTIME, &now)); 102 if (timespeccmp(&now, &end, >=)) 103 break; 104 pthread_yield(); 105 } 106 107 /* XXX write() contains a cancellation point */ 108 /* printf("child 2 testing for cancel\n"); */ 109 110 c2_in_test = 1; 111 pthread_testcancel(); 112 printf("you should see this message exactly once\n"); 113 message_seen++; 114 c2_in_test = 0; 115 ASSERT(message_seen == 1); 116 v(); 117 } 118 PANIC("child 2"); 119 } 120 121 static int c3_cancel_survived; 122 123 static void 124 c3handler(void *arg) 125 { 126 printf("(fyi, cancellation of self %s instantaneous)\n", 127 (c3_cancel_survived ? "was not" : "was")); 128 v(); 129 } 130 131 static void * 132 child3fn(void *arg) 133 { 134 SET_NAME("c3"); 135 pthread_cleanup_push(c3handler, NULL); 136 137 /* Cancel myself */ 138 CHECKr(pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL)); 139 c3_cancel_survived = 0; 140 pthread_cancel(pthread_self()); 141 c3_cancel_survived = 1; 142 pthread_testcancel(); 143 144 PANIC("child 3"); 145 } 146 147 int 148 main(int argc, char *argv[]) 149 { 150 pthread_t child1, child2, child3; 151 152 /* Set up our control flow */ 153 CHECKr(pthread_mutex_init(&mutex, NULL)); 154 CHECKr(pthread_cond_init(&cond, NULL)); 155 CHECKe(clock_gettime(CLOCK_REALTIME, &expiretime)); 156 expiretime.tv_sec += 5; /* this test shouldn't run over 5 seconds */ 157 158 CHECKr(pthread_create(&child1, NULL, child1fn, NULL)); 159 CHECKr(pthread_create(&child2, NULL, child2fn, NULL)); 160 p(); 161 p(); 162 163 CHECKr(pthread_cancel(child1)); 164 p(); 165 166 /* Give thread 2 a chance to go through its deferred loop once */ 167 p(); 168 CHECKr(pthread_cancel(child2)); 169 p(); 170 171 /* Child 3 cancels itself */ 172 CHECKr(pthread_create(&child3, NULL, child3fn, NULL)); 173 p(); 174 175 /* Make sure they're all gone */ 176 CHECKr(pthread_join(child3, NULL)); 177 CHECKr(pthread_join(child2, NULL)); 178 CHECKr(pthread_join(child1, NULL)); 179 180 exit(0); 181 } 182