1 | //------------------------------------------------------------------------------ |
2 | // Copyright (c) 2016 by Lukasz Janyst <lukasz@jany.st> |
3 | //------------------------------------------------------------------------------ |
4 | // This file is part of thread-bites. |
5 | // |
6 | // thread-bites is free software: you can redistribute it and/or modify |
7 | // it under the terms of the GNU General Public License as published by |
8 | // the Free Software Foundation, either version 3 of the License, or |
9 | // (at your option) any later version. |
10 | // |
11 | // thread-bites is distributed in the hope that it will be useful, |
12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | // GNU General Public License for more details. |
15 | // |
16 | // You should have received a copy of the GNU General Public License |
17 | // along with thread-bites. If not, see <http://www.gnu.org/licenses/>. |
18 | //------------------------------------------------------------------------------ |
19 | |
20 | #include <tb.h> |
21 | #include <string.h> |
22 | |
23 | //------------------------------------------------------------------------------ |
24 | // Thread function |
25 | //------------------------------------------------------------------------------ |
26 | void *thread_func(void *arg) |
27 | { |
28 | tbthread_t self = tbthread_self(); |
29 | int num = *(int*)arg; |
30 | int i; |
31 | |
32 | for(i = 0; i < 5; ++i) { |
33 | tbprint("[thread 0x%llx] Hello from thread #%d\n" , self, num); |
34 | tbsleep(1); |
35 | } |
36 | return arg; |
37 | } |
38 | |
39 | //------------------------------------------------------------------------------ |
40 | // Thread joiner |
41 | //------------------------------------------------------------------------------ |
42 | struct joiner_arg { |
43 | int sleep_before; |
44 | tbthread_t thread; |
45 | int join_status; |
46 | int sleep_after; |
47 | }; |
48 | |
49 | void *thread_joiner_func(void *arg) |
50 | { |
51 | tbthread_t self = tbthread_self(); |
52 | struct joiner_arg *a = (struct joiner_arg *)arg; |
53 | |
54 | tbprint("[thread 0x%llx] Sleeping %d seconds\n" , self, a->sleep_before); |
55 | tbsleep(a->sleep_before); |
56 | int ret = tbthread_join(a->thread, 0); |
57 | if(ret == a->join_status) |
58 | tbprint("[thread 0x%llx] Joining performed as expected\n" , self); |
59 | else |
60 | tbprint("[thread 0x%llx] Unexpected result of joining: %d\n" , self, ret); |
61 | tbprint("[thread 0x%llx] Sleeping %d seconds\n" , self, a->sleep_after); |
62 | tbsleep(a->sleep_after); |
63 | return 0; |
64 | } |
65 | |
66 | //------------------------------------------------------------------------------ |
67 | // Start the show |
68 | //------------------------------------------------------------------------------ |
69 | int main(int argc, char **argv) |
70 | { |
71 | tbthread_init(); |
72 | |
73 | tbthread_t thread[5]; |
74 | int targ[5]; |
75 | tbthread_attr_t attr; |
76 | void *ret; |
77 | int st = 0; |
78 | tbthread_attr_init(&attr); |
79 | |
80 | //---------------------------------------------------------------------------- |
81 | // Test the joiner waiting for threads to finish |
82 | //---------------------------------------------------------------------------- |
83 | tbprint("[thread main] Testing the joiner waiting for threads\n" ); |
84 | for(int i = 0; i < 5; ++i) { |
85 | targ[i] = i; |
86 | st = tbthread_create(&thread[i], &attr, thread_func, &targ[i]); |
87 | if(st != 0) { |
88 | tbprint("Failed to spawn thread %d: %s\n" , i, tbstrerror(-st)); |
89 | goto exit; |
90 | } |
91 | } |
92 | |
93 | tbprint("[thread main] Threads spawned successfully\n" ); |
94 | |
95 | for(int i = 0; i < 5; ++i) { |
96 | void *ret; |
97 | st = tbthread_join(thread[i], &ret); |
98 | if(st != 0) { |
99 | tbprint("Failed to join thread %d: %s\n" , i, tbstrerror(-st)); |
100 | goto exit; |
101 | } |
102 | tbprint("[thread main] Thread %d joined, retval; %d\n" , i, *(int*)ret); |
103 | } |
104 | |
105 | //---------------------------------------------------------------------------- |
106 | // Let the threads finish, wait, and then join |
107 | //---------------------------------------------------------------------------- |
108 | tbprint("---\n" ); |
109 | tbprint("[thread main] Testing the threads exiting before joining\n" ); |
110 | for(int i = 0; i < 5; ++i) { |
111 | targ[i] = i; |
112 | st = tbthread_create(&thread[i], &attr, thread_func, &targ[i]); |
113 | if(st != 0) { |
114 | tbprint("Failed to spawn thread %d: %s\n" , i, tbstrerror(-st)); |
115 | goto exit; |
116 | } |
117 | } |
118 | |
119 | tbprint("[thread main] Threads spawned successfully\n" ); |
120 | tbprint("[thread main] Sleeping 10 seconds\n" ); |
121 | tbsleep(10); |
122 | |
123 | for(int i = 0; i < 5; ++i) { |
124 | void *ret; |
125 | st = tbthread_join(thread[i], &ret); |
126 | if(st != 0) { |
127 | tbprint("Failed to join thread %d: %s\n" , i, tbstrerror(-st)); |
128 | goto exit; |
129 | } |
130 | tbprint("Thread %d joined, retval; %d\n" , i, *(int*)ret); |
131 | } |
132 | |
133 | //---------------------------------------------------------------------------- |
134 | // Join detached threads that exist |
135 | //---------------------------------------------------------------------------- |
136 | tbprint("---\n" ); |
137 | tbprint("[thread main] Join detached threads that still exist\n" ); |
138 | for(int i = 0; i < 5; ++i) { |
139 | targ[i] = i; |
140 | st = tbthread_create(&thread[i], &attr, thread_func, &targ[i]); |
141 | if(st != 0) { |
142 | tbprint("Failed to spawn thread %d: %s\n" , i, tbstrerror(-st)); |
143 | goto exit; |
144 | } |
145 | tbthread_detach(thread[i]); |
146 | } |
147 | |
148 | tbprint("[thread main] Threads spawned successfully\n" ); |
149 | |
150 | for(int i = 0; i < 5; ++i) { |
151 | void *ret; |
152 | st = tbthread_join(thread[i], &ret); |
153 | if(st != -EINVAL) { |
154 | tbprint("Thread %d (0x%llx) does not seem to be detached\n" , i); |
155 | goto exit; |
156 | } |
157 | } |
158 | tbprint("[thread main] Sleeping 10 seconds\n" ); |
159 | tbsleep(10); |
160 | |
161 | //---------------------------------------------------------------------------- |
162 | // Join detached threads that exited |
163 | //---------------------------------------------------------------------------- |
164 | tbprint("---\n" ); |
165 | tbprint("[thread main] Join detached threads that exited\n" ); |
166 | for(int i = 0; i < 5; ++i) { |
167 | targ[i] = i; |
168 | st = tbthread_create(&thread[i], &attr, thread_func, &targ[i]); |
169 | if(st != 0) { |
170 | tbprint("Failed to spawn thread %d: %s\n" , i, tbstrerror(-st)); |
171 | goto exit; |
172 | } |
173 | tbthread_detach(thread[i]); |
174 | } |
175 | |
176 | tbprint("[thread main] Threads spawned successfully\n" ); |
177 | tbprint("[thread main] Sleeping 10 seconds\n" ); |
178 | tbsleep(10); |
179 | |
180 | for(int i = 0; i < 5; ++i) { |
181 | void *ret; |
182 | st = tbthread_join(thread[i], &ret); |
183 | if(st != -ESRCH) { |
184 | tbprint("Thread %d (0x%llx) exists but shouldn't\n" , i); |
185 | goto exit; |
186 | } |
187 | } |
188 | |
189 | //---------------------------------------------------------------------------- |
190 | // Join self |
191 | //---------------------------------------------------------------------------- |
192 | if(tbthread_join(tbthread_self(), 0) != -EDEADLK) |
193 | tbprint("Joining self shouldn't have succeeded\n" ); |
194 | |
195 | //---------------------------------------------------------------------------- |
196 | // Mutual joining |
197 | //---------------------------------------------------------------------------- |
198 | tbprint("---\n" ); |
199 | tbprint("[thread main] Test mutual joining\n" ); |
200 | struct joiner_arg jarg[3]; |
201 | memset(jarg, 0, sizeof(jarg)); |
202 | jarg[0].sleep_before = 4; |
203 | jarg[1].sleep_before = 2; |
204 | jarg[0].join_status = -EDEADLK; |
205 | jarg[1].join_status = 0; |
206 | |
207 | for(int i = 0; i < 2; ++i) { |
208 | st = tbthread_create(&thread[i], &attr, thread_joiner_func, &jarg[i]); |
209 | if(st != 0) { |
210 | tbprint("Failed to spawn thread %d: %s\n" , i, tbstrerror(-st)); |
211 | goto exit; |
212 | } |
213 | } |
214 | jarg[0].thread = thread[1]; |
215 | jarg[1].thread = thread[0]; |
216 | |
217 | tbprint("[thread main] Threads spawned successfully\n" ); |
218 | |
219 | st = tbthread_join(thread[1], 0); |
220 | if(st != 0) { |
221 | tbprint("Unable to join thread 0x%llx\n" , thread[1]); |
222 | goto exit; |
223 | } |
224 | tbprint("Thread joined\n" ); |
225 | |
226 | //---------------------------------------------------------------------------- |
227 | // Double join |
228 | //---------------------------------------------------------------------------- |
229 | tbprint("---\n" ); |
230 | tbprint("[thread main] Test double join\n" ); |
231 | memset(jarg, 0, sizeof(jarg)); |
232 | jarg[0].sleep_before = 5; |
233 | jarg[1].sleep_before = 2; |
234 | jarg[2].sleep_before = 3; |
235 | jarg[0].join_status = -ESRCH; |
236 | jarg[1].join_status = 0; |
237 | jarg[2].join_status = -EINVAL; |
238 | |
239 | for(int i = 0; i < 3; ++i) { |
240 | st = tbthread_create(&thread[i], &attr, thread_joiner_func, &jarg[i]); |
241 | if(st != 0) { |
242 | tbprint("Failed to spawn thread %d: %s\n" , i, tbstrerror(-st)); |
243 | goto exit; |
244 | } |
245 | } |
246 | jarg[1].thread = thread[0]; |
247 | jarg[2].thread = thread[0]; |
248 | |
249 | tbprint("[thread main] Threads spawned successfully\n" ); |
250 | |
251 | for(int i = 1; i < 3; ++i) { |
252 | st = tbthread_join(thread[i], 0); |
253 | if(st != 0) { |
254 | tbprint("Failed to join thread %d: %s\n" , i, tbstrerror(-st)); |
255 | goto exit; |
256 | } |
257 | tbprint("Thread %d joined\n" , i); |
258 | } |
259 | |
260 | exit: |
261 | tbthread_finit(); |
262 | return st; |
263 | }; |
264 | |