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