xref: /openbsd-src/regress/lib/libpthread/cancel/cancel.c (revision 8445c53715e7030056b779e8ab40efb7820981f2)
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