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//------------------------------------------------------------------------------
26void *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//------------------------------------------------------------------------------
42struct joiner_arg {
43 int sleep_before;
44 tbthread_t thread;
45 int join_status;
46 int sleep_after;
47};
48
49void *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//------------------------------------------------------------------------------
69int 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
260exit:
261 tbthread_finit();
262 return st;
263};
264