1 /* $OpenBSD: cancel.c,v 1.9 2016/09/20 17:25:06 otto 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 <util.h> 8 #include <stdio.h> 9 #include <fcntl.h> 10 #include <stdlib.h> 11 #include "test.h" 12 13 static pthread_cond_t cond; 14 static pthread_mutex_t mutex; 15 static struct timespec expiretime; 16 17 static volatile int pv_state = 0; 18 19 static void 20 p(void) 21 { 22 CHECKr(pthread_mutex_lock(&mutex)); 23 if (pv_state <= 0) { 24 CHECKr(pthread_cond_timedwait(&cond, &mutex, &expiretime)); 25 } 26 pv_state--; 27 CHECKr(pthread_mutex_unlock(&mutex)); 28 } 29 30 static void 31 v(void) 32 { 33 int needsignal; 34 35 CHECKr(pthread_mutex_lock(&mutex)); 36 pv_state++; 37 needsignal = (pv_state == 1); 38 if (needsignal) 39 CHECKr(pthread_cond_signal(&cond)); 40 CHECKr(pthread_mutex_unlock(&mutex)); 41 } 42 43 static void 44 c1handler(void *arg) 45 { 46 CHECKe(close(*(int *)arg)); 47 v(); 48 } 49 50 static void * 51 child1fn(void *arg) 52 { 53 int fd, dummy; 54 char buf[1024]; 55 int len; 56 57 SET_NAME("c1"); 58 CHECKr(pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL)); 59 /* something that will block */ 60 CHECKe(openpty(&dummy, &fd, NULL, NULL, NULL)); 61 pthread_cleanup_push(c1handler, (void *)&fd); 62 v(); 63 while (1) { 64 CHECKe(len = read(fd, &buf, sizeof buf)); 65 printf("child 1 read %d bytes\n", len); 66 } 67 pthread_cleanup_pop(0); 68 PANIC("child 1"); 69 } 70 71 static int c2_in_test = 0; 72 73 static void 74 c2handler(void *arg) 75 { 76 ASSERT(c2_in_test); 77 v(); 78 } 79 80 static int message_seen = 0; 81 static void * 82 child2fn(void *arg) 83 { 84 SET_NAME("c2"); 85 86 CHECKr(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL)); 87 pthread_cleanup_push(c2handler, NULL); 88 v(); 89 90 while (1) { 91 struct timespec now; 92 struct timespec end; 93 94 /* 95 * XXX Be careful not to call any cancellation points 96 * until pthread_testcancel() 97 */ 98 99 CHECKe(clock_gettime(CLOCK_REALTIME, &end)); 100 end.tv_sec ++; 101 102 while (1) { 103 CHECKe(clock_gettime(CLOCK_REALTIME, &now)); 104 if (timespeccmp(&now, &end, >=)) 105 break; 106 pthread_yield(); 107 } 108 109 /* XXX write() contains a cancellation point */ 110 /* printf("child 2 testing for cancel\n"); */ 111 112 c2_in_test = 1; 113 pthread_testcancel(); 114 printf("you should see this message exactly once\n"); 115 message_seen++; 116 c2_in_test = 0; 117 ASSERT(message_seen == 1); 118 v(); 119 } 120 pthread_cleanup_pop(0); 121 PANIC("child 2"); 122 } 123 124 static int c3_cancel_survived; 125 126 static void 127 c3handler(void *arg) 128 { 129 printf("(fyi, cancellation of self %s instantaneous)\n", 130 (c3_cancel_survived ? "was not" : "was")); 131 v(); 132 } 133 134 static void * 135 child3fn(void *arg) 136 { 137 SET_NAME("c3"); 138 pthread_cleanup_push(c3handler, NULL); 139 140 /* Cancel myself */ 141 CHECKr(pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL)); 142 c3_cancel_survived = 0; 143 pthread_cancel(pthread_self()); 144 c3_cancel_survived = 1; 145 pthread_testcancel(); 146 pthread_cleanup_pop(0); 147 148 PANIC("child 3"); 149 } 150 151 static int c4_cancel_early; 152 153 static void 154 c4handler(void *arg) 155 { 156 printf("early = %d\n", c4_cancel_early); 157 ASSERT(c4_cancel_early == 0); 158 v(); 159 } 160 161 static void * 162 child4fn(void *arg) 163 { 164 SET_NAME("c4"); 165 pthread_cleanup_push(c4handler, NULL); 166 167 /* Cancel myself */ 168 CHECKr(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL)); 169 170 c4_cancel_early = 3; 171 pthread_cancel(pthread_self()); 172 173 c4_cancel_early = 2; 174 pthread_testcancel(); 175 176 c4_cancel_early = 1; 177 CHECKr(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL)); 178 179 c4_cancel_early = 0; 180 pthread_testcancel(); 181 182 pthread_cleanup_pop(0); 183 184 PANIC("child 4"); 185 } 186 187 int 188 main(int argc, char *argv[]) 189 { 190 pthread_t child1, child2, child3, child4; 191 192 /* Set up our control flow */ 193 CHECKr(pthread_mutex_init(&mutex, NULL)); 194 CHECKr(pthread_cond_init(&cond, NULL)); 195 CHECKe(clock_gettime(CLOCK_REALTIME, &expiretime)); 196 expiretime.tv_sec += 5; /* this test shouldn't run over 5 seconds */ 197 198 CHECKr(pthread_create(&child1, NULL, child1fn, NULL)); 199 CHECKr(pthread_create(&child2, NULL, child2fn, NULL)); 200 p(); 201 p(); 202 203 CHECKr(pthread_cancel(child1)); 204 p(); 205 206 /* Give thread 2 a chance to go through its deferred loop once */ 207 p(); 208 CHECKr(pthread_cancel(child2)); 209 p(); 210 211 /* Child 3 cancels itself */ 212 CHECKr(pthread_create(&child3, NULL, child3fn, NULL)); 213 p(); 214 215 /* Child 4 also cancels itself */ 216 CHECKr(pthread_create(&child4, NULL, child4fn, NULL)); 217 p(); 218 219 /* Make sure they're all gone */ 220 CHECKr(pthread_join(child4, NULL)); 221 CHECKr(pthread_join(child3, NULL)); 222 CHECKr(pthread_join(child2, NULL)); 223 CHECKr(pthread_join(child1, NULL)); 224 225 exit(0); 226 } 227