xref: /rk3399_rockchip-uboot/examples/standalone/sched.c (revision 1bc1538613d66cef3cbce680fc8d7c3561a0fbd0)
1*1bc15386SPeter Tyser /*
2*1bc15386SPeter Tyser  * This program is free software; you can redistribute it and/or
3*1bc15386SPeter Tyser  * modify it under the terms of the GNU General Public License as
4*1bc15386SPeter Tyser  * published by the Free Software Foundation; either version 2 of
5*1bc15386SPeter Tyser  * the License, or (at your option) any later version.
6*1bc15386SPeter Tyser  *
7*1bc15386SPeter Tyser  * This program is distributed in the hope that it will be useful,
8*1bc15386SPeter Tyser  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9*1bc15386SPeter Tyser  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10*1bc15386SPeter Tyser  * GNU General Public License for more details.
11*1bc15386SPeter Tyser  *
12*1bc15386SPeter Tyser  * You should have received a copy of the GNU General Public License
13*1bc15386SPeter Tyser  * along with this program; if not, write to the Free Software
14*1bc15386SPeter Tyser  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
15*1bc15386SPeter Tyser  * MA 02111-1307 USA
16*1bc15386SPeter Tyser  */
17*1bc15386SPeter Tyser 
18*1bc15386SPeter Tyser #include <common.h>
19*1bc15386SPeter Tyser #include <exports.h>
20*1bc15386SPeter Tyser 
21*1bc15386SPeter Tyser /*
22*1bc15386SPeter Tyser  * Author: Arun Dharankar <ADharankar@ATTBI.Com>
23*1bc15386SPeter Tyser  *
24*1bc15386SPeter Tyser  * A very simple thread/schedular model:
25*1bc15386SPeter Tyser  *   - only one master thread, and no parent child relation maintained
26*1bc15386SPeter Tyser  *   - parent thread cannot be stopped or deleted
27*1bc15386SPeter Tyser  *   - no permissions or credentials
28*1bc15386SPeter Tyser  *   - no elaborate safety checks
29*1bc15386SPeter Tyser  *   - cooperative multi threading
30*1bc15386SPeter Tyser  *   - Simple round-robin scheduleing with no priorities
31*1bc15386SPeter Tyser  *   - no metering/statistics collection
32*1bc15386SPeter Tyser  *
33*1bc15386SPeter Tyser  * Basic idea of implementing this is to allow more than one tests to
34*1bc15386SPeter Tyser  * execute "simultaneously".
35*1bc15386SPeter Tyser  *
36*1bc15386SPeter Tyser  * This may be modified such thread_yield may be called in syscalls, and
37*1bc15386SPeter Tyser  * timer interrupts.
38*1bc15386SPeter Tyser  */
39*1bc15386SPeter Tyser 
40*1bc15386SPeter Tyser 
41*1bc15386SPeter Tyser #define MAX_THREADS 8
42*1bc15386SPeter Tyser 
43*1bc15386SPeter Tyser #define CTX_SIZE 512
44*1bc15386SPeter Tyser #define STK_SIZE 8*1024
45*1bc15386SPeter Tyser 
46*1bc15386SPeter Tyser #define STATE_EMPTY 0
47*1bc15386SPeter Tyser #define STATE_RUNNABLE 1
48*1bc15386SPeter Tyser #define STATE_STOPPED 2
49*1bc15386SPeter Tyser #define STATE_TERMINATED 2
50*1bc15386SPeter Tyser 
51*1bc15386SPeter Tyser #define MASTER_THREAD 0
52*1bc15386SPeter Tyser 
53*1bc15386SPeter Tyser #define RC_FAILURE	(-1)
54*1bc15386SPeter Tyser #define	RC_SUCCESS	(0)
55*1bc15386SPeter Tyser 
56*1bc15386SPeter Tyser typedef	vu_char *jmp_ctx;
57*1bc15386SPeter Tyser unsigned long setctxsp (vu_char *sp);
58*1bc15386SPeter Tyser int ppc_setjmp(jmp_ctx env);
59*1bc15386SPeter Tyser void ppc_longjmp(jmp_ctx env, int val);
60*1bc15386SPeter Tyser #define setjmp	ppc_setjmp
61*1bc15386SPeter Tyser #define longjmp	ppc_longjmp
62*1bc15386SPeter Tyser 
63*1bc15386SPeter Tyser struct lthread {
64*1bc15386SPeter Tyser 	int state;
65*1bc15386SPeter Tyser 	int retval;
66*1bc15386SPeter Tyser 	char stack[STK_SIZE];
67*1bc15386SPeter Tyser 	uchar context[CTX_SIZE];
68*1bc15386SPeter Tyser 	int (*func) (void *);
69*1bc15386SPeter Tyser 	void *arg;
70*1bc15386SPeter Tyser };
71*1bc15386SPeter Tyser static volatile struct lthread lthreads[MAX_THREADS];
72*1bc15386SPeter Tyser static volatile int current_tid = MASTER_THREAD;
73*1bc15386SPeter Tyser 
74*1bc15386SPeter Tyser 
75*1bc15386SPeter Tyser static uchar dbg = 0;
76*1bc15386SPeter Tyser 
77*1bc15386SPeter Tyser #define PDEBUG(fmt, args...)	 {					\
78*1bc15386SPeter Tyser 	if(dbg != 0) {							\
79*1bc15386SPeter Tyser 		printf("[%s %d %s]: ",__FILE__,__LINE__,__FUNCTION__);\
80*1bc15386SPeter Tyser 		printf(fmt, ##args);				\
81*1bc15386SPeter Tyser 		printf("\n");					\
82*1bc15386SPeter Tyser 	}								\
83*1bc15386SPeter Tyser }
84*1bc15386SPeter Tyser 
85*1bc15386SPeter Tyser static int testthread (void *);
86*1bc15386SPeter Tyser static void sched_init (void);
87*1bc15386SPeter Tyser static int thread_create (int (*func) (void *), void *arg);
88*1bc15386SPeter Tyser static int thread_start (int id);
89*1bc15386SPeter Tyser static void thread_yield (void);
90*1bc15386SPeter Tyser static int thread_delete (int id);
91*1bc15386SPeter Tyser static int thread_join (int *ret);
92*1bc15386SPeter Tyser 
93*1bc15386SPeter Tyser #if 0							/* not used yet */
94*1bc15386SPeter Tyser static int thread_stop (int id);
95*1bc15386SPeter Tyser #endif							/* not used yet */
96*1bc15386SPeter Tyser 
97*1bc15386SPeter Tyser /* An example of schedular test */
98*1bc15386SPeter Tyser 
99*1bc15386SPeter Tyser #define NUMTHREADS 7
100*1bc15386SPeter Tyser int sched (int ac, char *av[])
101*1bc15386SPeter Tyser {
102*1bc15386SPeter Tyser 	int i, j;
103*1bc15386SPeter Tyser 	int tid[NUMTHREADS];
104*1bc15386SPeter Tyser 	int names[NUMTHREADS];
105*1bc15386SPeter Tyser 
106*1bc15386SPeter Tyser 	app_startup(av);
107*1bc15386SPeter Tyser 
108*1bc15386SPeter Tyser 	sched_init ();
109*1bc15386SPeter Tyser 
110*1bc15386SPeter Tyser 	for (i = 0; i < NUMTHREADS; i++) {
111*1bc15386SPeter Tyser 		names[i] = i;
112*1bc15386SPeter Tyser 		j = thread_create (testthread, (void *) &names[i]);
113*1bc15386SPeter Tyser 		if (j == RC_FAILURE)
114*1bc15386SPeter Tyser 			printf ("schedtest: Failed to create thread %d\n", i);
115*1bc15386SPeter Tyser 		if (j > 0) {
116*1bc15386SPeter Tyser 			printf ("schedtest: Created thread with id %d, name %d\n",
117*1bc15386SPeter Tyser 						j, i);
118*1bc15386SPeter Tyser 			tid[i] = j;
119*1bc15386SPeter Tyser 		}
120*1bc15386SPeter Tyser 	}
121*1bc15386SPeter Tyser 	printf ("schedtest: Threads created\n");
122*1bc15386SPeter Tyser 
123*1bc15386SPeter Tyser 	printf ("sched_test: function=0x%08x\n", (unsigned)testthread);
124*1bc15386SPeter Tyser 	for (i = 0; i < NUMTHREADS; i++) {
125*1bc15386SPeter Tyser 		printf ("schedtest: Setting thread %d runnable\n", tid[i]);
126*1bc15386SPeter Tyser 		thread_start (tid[i]);
127*1bc15386SPeter Tyser 		thread_yield ();
128*1bc15386SPeter Tyser 	}
129*1bc15386SPeter Tyser 	printf ("schedtest: Started %d threads\n", NUMTHREADS);
130*1bc15386SPeter Tyser 
131*1bc15386SPeter Tyser 	while (1) {
132*1bc15386SPeter Tyser 		printf ("schedtest: Waiting for threads to complete\n");
133*1bc15386SPeter Tyser 		if (tstc () && getc () == 0x3) {
134*1bc15386SPeter Tyser 			printf ("schedtest: Aborting threads...\n");
135*1bc15386SPeter Tyser 			for (i = 0; i < NUMTHREADS; i++) {
136*1bc15386SPeter Tyser 				printf ("schedtest: Deleting thread %d\n", tid[i]);
137*1bc15386SPeter Tyser 				thread_delete (tid[i]);
138*1bc15386SPeter Tyser 			}
139*1bc15386SPeter Tyser 			return RC_SUCCESS;
140*1bc15386SPeter Tyser 		}
141*1bc15386SPeter Tyser 		j = -1;
142*1bc15386SPeter Tyser 		i = thread_join (&j);
143*1bc15386SPeter Tyser 		if (i == RC_FAILURE) {
144*1bc15386SPeter Tyser 			printf ("schedtest: No threads pending, "
145*1bc15386SPeter Tyser 						"exiting schedular test\n");
146*1bc15386SPeter Tyser 			return RC_SUCCESS;
147*1bc15386SPeter Tyser 		}
148*1bc15386SPeter Tyser 		printf ("schedtest: thread is %d returned %d\n", i, j);
149*1bc15386SPeter Tyser 		thread_yield ();
150*1bc15386SPeter Tyser 	}
151*1bc15386SPeter Tyser 
152*1bc15386SPeter Tyser 	return RC_SUCCESS;
153*1bc15386SPeter Tyser }
154*1bc15386SPeter Tyser 
155*1bc15386SPeter Tyser static int testthread (void *name)
156*1bc15386SPeter Tyser {
157*1bc15386SPeter Tyser 	int i;
158*1bc15386SPeter Tyser 
159*1bc15386SPeter Tyser 	printf ("testthread: Begin executing thread, myname %d, &i=0x%08x\n",
160*1bc15386SPeter Tyser 		*(int *) name, (unsigned)&i);
161*1bc15386SPeter Tyser 
162*1bc15386SPeter Tyser 	printf ("Thread %02d, i=%d\n", *(int *) name, i);
163*1bc15386SPeter Tyser 
164*1bc15386SPeter Tyser 	for (i = 0; i < 0xffff * (*(int *) name + 1); i++) {
165*1bc15386SPeter Tyser 		if (tstc () && getc () == 0x3) {
166*1bc15386SPeter Tyser 			printf ("testthread: myname %d terminating.\n",
167*1bc15386SPeter Tyser 						*(int *) name);
168*1bc15386SPeter Tyser 			return *(int *) name + 1;
169*1bc15386SPeter Tyser 		}
170*1bc15386SPeter Tyser 
171*1bc15386SPeter Tyser 		if (i % 100 == 0)
172*1bc15386SPeter Tyser 			thread_yield ();
173*1bc15386SPeter Tyser 	}
174*1bc15386SPeter Tyser 
175*1bc15386SPeter Tyser 	printf ("testthread: returning %d, i=0x%x\n",
176*1bc15386SPeter Tyser 				*(int *) name + 1, i);
177*1bc15386SPeter Tyser 
178*1bc15386SPeter Tyser 	return *(int *) name + 1;
179*1bc15386SPeter Tyser }
180*1bc15386SPeter Tyser 
181*1bc15386SPeter Tyser 
182*1bc15386SPeter Tyser static void sched_init (void)
183*1bc15386SPeter Tyser {
184*1bc15386SPeter Tyser 	int i;
185*1bc15386SPeter Tyser 
186*1bc15386SPeter Tyser 	for (i = MASTER_THREAD + 1; i < MAX_THREADS; i++)
187*1bc15386SPeter Tyser 		lthreads[i].state = STATE_EMPTY;
188*1bc15386SPeter Tyser 
189*1bc15386SPeter Tyser 	current_tid = MASTER_THREAD;
190*1bc15386SPeter Tyser 	lthreads[current_tid].state = STATE_RUNNABLE;
191*1bc15386SPeter Tyser 	PDEBUG ("sched_init: master context = 0x%08x",
192*1bc15386SPeter Tyser 		(unsigned)lthreads[current_tid].context);
193*1bc15386SPeter Tyser 	return;
194*1bc15386SPeter Tyser }
195*1bc15386SPeter Tyser 
196*1bc15386SPeter Tyser static void thread_yield (void)
197*1bc15386SPeter Tyser {
198*1bc15386SPeter Tyser 	static int i;
199*1bc15386SPeter Tyser 
200*1bc15386SPeter Tyser 	PDEBUG ("thread_yield: current tid=%d", current_tid);
201*1bc15386SPeter Tyser 
202*1bc15386SPeter Tyser #define SWITCH(new)							\
203*1bc15386SPeter Tyser 	if(lthreads[new].state == STATE_RUNNABLE) {			\
204*1bc15386SPeter Tyser 		PDEBUG("thread_yield: %d match, ctx=0x%08x",		\
205*1bc15386SPeter Tyser 			new,						\
206*1bc15386SPeter Tyser 			(unsigned)lthreads[current_tid].context);	\
207*1bc15386SPeter Tyser 		if(setjmp(lthreads[current_tid].context) == 0) {	\
208*1bc15386SPeter Tyser 			current_tid = new;				\
209*1bc15386SPeter Tyser 			PDEBUG("thread_yield: tid %d returns 0",	\
210*1bc15386SPeter Tyser 				new);					\
211*1bc15386SPeter Tyser 			longjmp(lthreads[new].context, 1);		\
212*1bc15386SPeter Tyser 		} else {						\
213*1bc15386SPeter Tyser 			PDEBUG("thread_yield: tid %d returns 1",	\
214*1bc15386SPeter Tyser 				new);					\
215*1bc15386SPeter Tyser 			return;						\
216*1bc15386SPeter Tyser 		}							\
217*1bc15386SPeter Tyser 	}
218*1bc15386SPeter Tyser 
219*1bc15386SPeter Tyser 	for (i = current_tid + 1; i < MAX_THREADS; i++) {
220*1bc15386SPeter Tyser 		SWITCH (i);
221*1bc15386SPeter Tyser 	}
222*1bc15386SPeter Tyser 
223*1bc15386SPeter Tyser 	if (current_tid != 0) {
224*1bc15386SPeter Tyser 		for (i = 0; i <= current_tid; i++) {
225*1bc15386SPeter Tyser 			SWITCH (i);
226*1bc15386SPeter Tyser 		}
227*1bc15386SPeter Tyser 	}
228*1bc15386SPeter Tyser 
229*1bc15386SPeter Tyser 	PDEBUG ("thread_yield: returning from thread_yield");
230*1bc15386SPeter Tyser 	return;
231*1bc15386SPeter Tyser }
232*1bc15386SPeter Tyser 
233*1bc15386SPeter Tyser static int thread_create (int (*func) (void *), void *arg)
234*1bc15386SPeter Tyser {
235*1bc15386SPeter Tyser 	int i;
236*1bc15386SPeter Tyser 
237*1bc15386SPeter Tyser 	for (i = MASTER_THREAD + 1; i < MAX_THREADS; i++) {
238*1bc15386SPeter Tyser 		if (lthreads[i].state == STATE_EMPTY) {
239*1bc15386SPeter Tyser 			lthreads[i].state = STATE_STOPPED;
240*1bc15386SPeter Tyser 			lthreads[i].func = func;
241*1bc15386SPeter Tyser 			lthreads[i].arg = arg;
242*1bc15386SPeter Tyser 			PDEBUG ("thread_create: returns new tid %d", i);
243*1bc15386SPeter Tyser 			return i;
244*1bc15386SPeter Tyser 		}
245*1bc15386SPeter Tyser 	}
246*1bc15386SPeter Tyser 
247*1bc15386SPeter Tyser 	PDEBUG ("thread_create: returns failure");
248*1bc15386SPeter Tyser 	return RC_FAILURE;
249*1bc15386SPeter Tyser }
250*1bc15386SPeter Tyser 
251*1bc15386SPeter Tyser static int thread_delete (int id)
252*1bc15386SPeter Tyser {
253*1bc15386SPeter Tyser 	if (id <= MASTER_THREAD || id > MAX_THREADS)
254*1bc15386SPeter Tyser 		return RC_FAILURE;
255*1bc15386SPeter Tyser 
256*1bc15386SPeter Tyser 	if (current_tid == id)
257*1bc15386SPeter Tyser 		return RC_FAILURE;
258*1bc15386SPeter Tyser 
259*1bc15386SPeter Tyser 	lthreads[id].state = STATE_EMPTY;
260*1bc15386SPeter Tyser 	return RC_SUCCESS;
261*1bc15386SPeter Tyser }
262*1bc15386SPeter Tyser 
263*1bc15386SPeter Tyser static void thread_launcher (void)
264*1bc15386SPeter Tyser {
265*1bc15386SPeter Tyser 	PDEBUG ("thread_launcher: invoking func=0x%08x",
266*1bc15386SPeter Tyser 		   (unsigned)lthreads[current_tid].func);
267*1bc15386SPeter Tyser 
268*1bc15386SPeter Tyser 	lthreads[current_tid].retval =
269*1bc15386SPeter Tyser 			lthreads[current_tid].func (lthreads[current_tid].arg);
270*1bc15386SPeter Tyser 
271*1bc15386SPeter Tyser 	PDEBUG ("thread_launcher: tid %d terminated", current_tid);
272*1bc15386SPeter Tyser 
273*1bc15386SPeter Tyser 	lthreads[current_tid].state = STATE_TERMINATED;
274*1bc15386SPeter Tyser 	thread_yield ();
275*1bc15386SPeter Tyser 	printf ("thread_launcher: should NEVER get here!\n");
276*1bc15386SPeter Tyser 
277*1bc15386SPeter Tyser 	return;
278*1bc15386SPeter Tyser }
279*1bc15386SPeter Tyser 
280*1bc15386SPeter Tyser static int thread_start (int id)
281*1bc15386SPeter Tyser {
282*1bc15386SPeter Tyser 	PDEBUG ("thread_start: id=%d", id);
283*1bc15386SPeter Tyser 	if (id <= MASTER_THREAD || id > MAX_THREADS) {
284*1bc15386SPeter Tyser 		return RC_FAILURE;
285*1bc15386SPeter Tyser 	}
286*1bc15386SPeter Tyser 
287*1bc15386SPeter Tyser 	if (lthreads[id].state != STATE_STOPPED)
288*1bc15386SPeter Tyser 		return RC_FAILURE;
289*1bc15386SPeter Tyser 
290*1bc15386SPeter Tyser 	if (setjmp (lthreads[current_tid].context) == 0) {
291*1bc15386SPeter Tyser 		lthreads[id].state = STATE_RUNNABLE;
292*1bc15386SPeter Tyser 		current_tid = id;
293*1bc15386SPeter Tyser 		PDEBUG ("thread_start: to be stack=0%08x",
294*1bc15386SPeter Tyser 			(unsigned)lthreads[id].stack);
295*1bc15386SPeter Tyser 		setctxsp ((vu_char *)&lthreads[id].stack[STK_SIZE]);
296*1bc15386SPeter Tyser 		thread_launcher ();
297*1bc15386SPeter Tyser 	}
298*1bc15386SPeter Tyser 
299*1bc15386SPeter Tyser 	PDEBUG ("thread_start: Thread id=%d started, parent returns", id);
300*1bc15386SPeter Tyser 
301*1bc15386SPeter Tyser 	return RC_SUCCESS;
302*1bc15386SPeter Tyser }
303*1bc15386SPeter Tyser 
304*1bc15386SPeter Tyser #if 0	/* not used so far */
305*1bc15386SPeter Tyser static int thread_stop (int id)
306*1bc15386SPeter Tyser {
307*1bc15386SPeter Tyser 	if (id <= MASTER_THREAD || id >= MAX_THREADS)
308*1bc15386SPeter Tyser 		return RC_FAILURE;
309*1bc15386SPeter Tyser 
310*1bc15386SPeter Tyser 	if (current_tid == id)
311*1bc15386SPeter Tyser 		return RC_FAILURE;
312*1bc15386SPeter Tyser 
313*1bc15386SPeter Tyser 	lthreads[id].state = STATE_STOPPED;
314*1bc15386SPeter Tyser 	return RC_SUCCESS;
315*1bc15386SPeter Tyser }
316*1bc15386SPeter Tyser #endif	/* not used so far */
317*1bc15386SPeter Tyser 
318*1bc15386SPeter Tyser static int thread_join (int *ret)
319*1bc15386SPeter Tyser {
320*1bc15386SPeter Tyser 	int i, j = 0;
321*1bc15386SPeter Tyser 
322*1bc15386SPeter Tyser 	PDEBUG ("thread_join: *ret = %d", *ret);
323*1bc15386SPeter Tyser 
324*1bc15386SPeter Tyser 	if (!(*ret == -1 || *ret > MASTER_THREAD || *ret < MAX_THREADS)) {
325*1bc15386SPeter Tyser 		PDEBUG ("thread_join: invalid tid %d", *ret);
326*1bc15386SPeter Tyser 		return RC_FAILURE;
327*1bc15386SPeter Tyser 	}
328*1bc15386SPeter Tyser 
329*1bc15386SPeter Tyser 	if (*ret == -1) {
330*1bc15386SPeter Tyser 		PDEBUG ("Checking for tid = -1");
331*1bc15386SPeter Tyser 		while (1) {
332*1bc15386SPeter Tyser 			/* PDEBUG("thread_join: start while-loopn"); */
333*1bc15386SPeter Tyser 			j = 0;
334*1bc15386SPeter Tyser 			for (i = MASTER_THREAD + 1; i < MAX_THREADS; i++) {
335*1bc15386SPeter Tyser 				if (lthreads[i].state == STATE_TERMINATED) {
336*1bc15386SPeter Tyser 					*ret = lthreads[i].retval;
337*1bc15386SPeter Tyser 					lthreads[i].state = STATE_EMPTY;
338*1bc15386SPeter Tyser 					/* PDEBUG("thread_join: returning retval %d of tid %d",
339*1bc15386SPeter Tyser 					   ret, i); */
340*1bc15386SPeter Tyser 					return RC_SUCCESS;
341*1bc15386SPeter Tyser 				}
342*1bc15386SPeter Tyser 
343*1bc15386SPeter Tyser 				if (lthreads[i].state != STATE_EMPTY) {
344*1bc15386SPeter Tyser 					PDEBUG ("thread_join: %d used slots tid %d state=%d",
345*1bc15386SPeter Tyser 						   j, i, lthreads[i].state);
346*1bc15386SPeter Tyser 					j++;
347*1bc15386SPeter Tyser 				}
348*1bc15386SPeter Tyser 			}
349*1bc15386SPeter Tyser 			if (j == 0) {
350*1bc15386SPeter Tyser 				PDEBUG ("thread_join: all slots empty!");
351*1bc15386SPeter Tyser 				return RC_FAILURE;
352*1bc15386SPeter Tyser 			}
353*1bc15386SPeter Tyser 			/*  PDEBUG("thread_join: yielding"); */
354*1bc15386SPeter Tyser 			thread_yield ();
355*1bc15386SPeter Tyser 			/*  PDEBUG("thread_join: back from yield"); */
356*1bc15386SPeter Tyser 		}
357*1bc15386SPeter Tyser 	}
358*1bc15386SPeter Tyser 
359*1bc15386SPeter Tyser 	if (lthreads[*ret].state == STATE_TERMINATED) {
360*1bc15386SPeter Tyser 		i = *ret;
361*1bc15386SPeter Tyser 		*ret = lthreads[*ret].retval;
362*1bc15386SPeter Tyser 		lthreads[*ret].state = STATE_EMPTY;
363*1bc15386SPeter Tyser 		PDEBUG ("thread_join: returing %d for tid %d", *ret, i);
364*1bc15386SPeter Tyser 		return RC_SUCCESS;
365*1bc15386SPeter Tyser 	}
366*1bc15386SPeter Tyser 
367*1bc15386SPeter Tyser 	PDEBUG ("thread_join: thread %d is not terminated!", *ret);
368*1bc15386SPeter Tyser 	return RC_FAILURE;
369*1bc15386SPeter Tyser }
370