source: trunk/libc/src/kNIX/thread.c@ 2927

Last change on this file since 2927 was 2927, checked in by bird, 19 years ago

cleanup in progress (late night session)

File size: 15.0 KB
Line 
1/* $Id: $ */
2/** @file
3 *
4 * kUNIX - Thread Management.
5 *
6 * Copyright (c) 2006 knut st. osmundsen <bird-src-spam@anduin.net>
7 *
8 *
9 * This file is part of kLIBC.
10 *
11 * kLIBC is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU Lesser General Public License as published
13 * by the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * kLIBC is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Lesser General Public License for more details.
20 *
21 * You should have received a copy of the GNU Lesser General Public License
22 * along with kLIBC; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 *
25 */
26
27/*******************************************************************************
28* Header Files *
29*******************************************************************************/
30#define __LIBC_LOG_GROUP __LIBC_LOG_GRP_BACK_THREAD
31#include "kNIX.h"
32
33
34/*******************************************************************************
35* Global Variables *
36*******************************************************************************/
37/** Preallocated thread structure. */
38static __LIBC_THREAD gPreAllocThrd;
39/** Flags whether or not gPreAllocThrd is used. */
40static volatile int gfPreAllocThrd;
41
42/** Mutex for the thread database. */
43static _fmutex gmtxThrdDB;
44/** Thread database. (Protected gmtxThrdDB.) */
45static __LIBC_PTHREAD gpThrdDB;
46/** Number of threads in the thread database. (Protected gmtxThrdDB.) */
47static unsigned gcThrdDBEntries;
48/** Zombie thread database. (Protected gmtxThrdDB.) */
49static __LIBC_PTHREAD gpThrdDBZombies;
50/** Number of threads in the zombie thread database. (Protected gmtxThrdDB.) */
51static unsigned gcThrdDBZombies;
52
53/** Head of the thread termination callback list. */
54static __LIBC_PTHREADTERMCBREGREC gpTermHead;
55/** Tail of the thread termination callback list. */
56static __LIBC_PTHREADTERMCBREGREC gpTermTail;
57/** List protection semaphore (spinlock sort). */
58static _smutex gsmtxTerm;
59
60
61/**
62 * Initializes the thread manager.
63 *
64 * @returns 0 on success.
65 * @returns negated errno on failure.
66 */
67int __libc_back_threadInitManager(void)
68{
69 LIBCLOG_ENTER("\n");
70
71 /** @todo rewrite to use a read/write semaphore, not mutex. */
72 int rc = _fmutex_create2(&gmtxThrdDB, 0, "LIBC Thread DB Mutex");
73 if (rc)
74 LIBCLOG_ERROR_RETURN_INT(rc);
75
76 LIBCLOG_RETURN_INT(0);
77}
78
79
80/**
81 * Internal wrapper for the various OS interfaces for
82 * setting the thread pointer.
83 *
84 * @param pThrd The new thread pointer.
85 */
86static inline void threadSetPtr(__LIBC_PTHREAD pThrd)
87{
88#ifdef __OS2__
89 *__libc_gpTLS = pThrd;
90#elif defined(__NT__)
91 fibSetLibcThread(pThrd);
92#else
93# error "Port me!"
94#endif
95}
96
97
98/**
99 * Initialize a thread structure.
100 *
101 * @param pThrd Pointer to the thread structure which is to be initialized.
102 * @param pParentThrd Pointer to the thread structure for the parent thread.
103 * If NULL and thread id is 1 then inherit from parent process.
104 * If NULL and thread is not null or no record of parent then
105 * use defaults.
106 */
107static void threadInit(__LIBC_PTHREAD pThrd, const __LIBC_PTHREAD pParentThrd)
108{
109 bzero(pThrd, sizeof(*pThrd));
110 pThrd->cRefs = 1;
111 __libc_Back_threadInit(pThrd, pParentThrd);
112}
113
114
115void __libc_threadUse(__LIBC_PTHREAD pThrd)
116{
117 LIBCLOG_ENTER("pThrd=%p\n", (void *)pThrd);
118 int rc;
119
120 /*
121 * Check that pThrd isn't already in use and set gpTLS to point at pThrd.
122 * (*gpTLS might already be pointing to a temporary structure.)
123 */
124 assert(!pThrd->pNext && !pThrd->tid);
125 threadSetPtr(pThrd);
126
127 /*
128 * Set the thread id of this thread.
129 */
130 pThrd->tid = _gettid();
131
132 /*
133 * Insert this thread into the thread database.
134 * Note that there might be a dead thread by the same id in the database.
135 * This have to be removed of course.
136 */
137 /** @todo rewrite to use a read/write semaphore, not mutex. */
138 if (gmtxThrdDB.fs == _FMS_UNINIT)
139 _fmutex_create2(&gmtxThrdDB, 0, "LIBC Thread DB Mutex");
140
141 rc = _fmutex_request(&gmtxThrdDB, 0);
142 if (!rc)
143 {
144 /* Remove any zombie threads. */
145 __LIBC_PTHREAD pPrev = NULL;
146 __LIBC_PTHREAD p = gpThrdDB;
147 while (p)
148 {
149 if (p->tid == pThrd->tid)
150 {
151 /* remove it and place it in the zombie list */
152 __LIBC_PTHREAD pNext = p->pNext;
153 if (pPrev)
154 pPrev->pNext = pNext;
155 else
156 gpThrdDB = pNext;
157 gcThrdDBEntries--;
158
159 p->pNext = gpThrdDBZombies;
160 gpThrdDBZombies = p;
161 gcThrdDBZombies++;
162 p = pNext;
163 /* paranoid as always, we continue scanning the entire list. */
164 }
165 else
166 {
167 /* next */
168 pPrev = p;
169 p = p->pNext;
170 }
171 }
172
173 /* Insert it */
174 pThrd->pNext = gpThrdDB;
175 gpThrdDB = pThrd;
176 gcThrdDBEntries++;
177
178 _fmutex_release(&gmtxThrdDB);
179 LIBCLOG_RETURN_VOID();
180 }
181
182 LIBCLOG_ERROR_RETURN_VOID();
183}
184
185
186__LIBC_PTHREAD __libc_threadCurrentSlow(void)
187{
188 LIBCLOG_ENTER("\n");
189 if (!__libc_threadCurrentNoAuto())
190 {
191 /*
192 * Setup a temporary thread block on the stack so _hmalloc()
193 * can't end up calling us recursivly if something goes wrong.
194 */
195 __LIBC_THREAD Thrd;
196 threadInit(&Thrd, NULL);
197 threadSetPtr(&Thrd);
198
199 __LIBC_PTHREAD pThrd;
200 if (!__lxchg(&gfPreAllocThrd, 1))
201 pThrd = &gPreAllocThrd;
202 else
203 {
204 pThrd = _hmalloc(sizeof(__LIBC_THREAD));
205 assert(pThrd); /* deep, deep, deep, shit. abort the process in a controlled manner... */
206 }
207 *pThrd = Thrd;
208
209 __libc_threadUse(pThrd);
210 LIBCLOG_MSG("Created thread block %p\n", (void*)pThrd);
211 }
212
213 LIBCLOG_RETURN_P(__libc_threadCurrentNoAuto());
214}
215
216
217__LIBC_PTHREAD __libc_threadAlloc(void)
218{
219 LIBCLOG_ENTER("\n");
220
221 /*
222 * No need to use the pre allocated here since the current thread will
223 * most likely be using that one!
224 */
225 __LIBC_PTHREAD pThrd = _hmalloc(sizeof(__LIBC_THREAD));
226 if (pThrd)
227 {
228 threadInit(pThrd, __libc_threadCurrentNoAuto());
229 LIBCLOG_RETURN_P(pThrd);
230 }
231 LIBCLOG_ERROR_RETURN_P(pThrd);
232}
233
234
235void __libc_threadDereference(__LIBC_PTHREAD pThrd)
236{
237 LIBCLOG_ENTER("pThrd=%p (tid=%d)\n", (void *)pThrd, pThrd->tid);
238
239 /*
240 * Take owner ship of the DB semaphore.
241 */
242 _fmutex_request(&gmtxThrdDB, 0);
243 if (pThrd->cRefs)
244 pThrd->cRefs--;
245 if (pThrd->cRefs)
246 {
247#ifdef DEBUG_LOGGING
248 unsigned cRefs = pThrd->cRefs;
249#endif
250 _fmutex_release(&gmtxThrdDB);
251 LIBCLOG_RETURN_MSG_VOID("ret void. cRefs=%d\n", cRefs);
252 }
253 LIBCLOG_MSG("unlinking and disposing the thread.\n");
254
255 /*
256 * Thread is dead, unlink it.
257 */
258 if (pThrd->tid)
259 {
260 if (pThrd == gpThrdDB)
261 {
262 gpThrdDB = pThrd->pNext;
263 gcThrdDBEntries--;
264 }
265 else
266 {
267 __LIBC_PTHREAD p;
268 for (p = gpThrdDB; p->pNext; p = p->pNext)
269 if (p->pNext == pThrd)
270 {
271 p->pNext = pThrd->pNext;
272 gcThrdDBEntries--;
273 p = NULL;
274 break;
275 }
276
277 /*
278 * Not found? search zombie DB.
279 */
280 if (p)
281 {
282 if (gpThrdDBZombies == pThrd)
283 {
284 gpThrdDBZombies = pThrd->pNext;
285 gcThrdDBZombies--;
286 p = NULL;
287 }
288 else
289 {
290 for (p = gpThrdDBZombies; p->pNext; p = p->pNext)
291 if (p->pNext == pThrd)
292 {
293 p->pNext = pThrd->pNext;
294 gcThrdDBZombies--;
295 p = NULL;
296 break;
297 }
298 }
299 }
300
301 /*
302 * Did we succeed in finding it? If not don't dispose of the the thread!
303 */
304 if (p)
305 {
306 assert(!p);
307 pThrd = NULL;
308 }
309 }
310 }
311
312 _fmutex_release(&gmtxThrdDB);
313
314
315 /*
316 * Dispose of the thread
317 */
318 if (pThrd)
319 {
320 /*
321 * Clean up members.
322 */
323 __libc_Back_threadCleanup(pThrd);
324
325 /*
326 * Clear the TLS if it's for the current thread.
327 */
328 if (__libc_threadCurrentNoAuto() == pThrd)
329 threadSetPtr(NULL);
330
331 /*
332 * Release storage.
333 */
334 if (pThrd != &gPreAllocThrd)
335 free(pThrd);
336 else
337 __lxchg(&gfPreAllocThrd, 0);
338 LIBCLOG_RETURN_MSG_VOID("ret (gcThrdDBEntires=%d gcThrdDBZombies=%d)\n", gcThrdDBEntries, gcThrdDBZombies);
339 }
340 LIBCLOG_ERROR_RETURN_MSG_VOID("ret (gcThrdDBEntires=%d gcThrdDBZombies=%d)\n", gcThrdDBEntries, gcThrdDBZombies);
341}
342
343
344__LIBC_PTHREAD __libc_threadLookup(unsigned tid)
345{
346 LIBCLOG_ENTER("tid=%d\n", tid);
347 int rc;
348 __LIBC_PTHREAD pThrd;
349
350 /* can't search something which isn't there. */
351 if (gmtxThrdDB.fs == _FMS_UNINIT)
352 LIBCLOG_ERROR_RETURN_P(NULL);
353
354 rc = _fmutex_request(&gmtxThrdDB, 0);
355 if (rc)
356 LIBCLOG_ERROR_RETURN(NULL, "ret NULL - fmutex f**ked. rc=%d\n", rc);
357
358 for (pThrd = gpThrdDB; pThrd; pThrd = pThrd->pNext)
359 if (pThrd->tid == tid)
360 {
361 pThrd->cRefs++;
362 break;
363 }
364
365 _fmutex_release(&gmtxThrdDB);
366 if (pThrd)
367 LIBCLOG_RETURN_P(pThrd);
368 LIBCLOG_ERROR_RETURN_P(pThrd);
369}
370
371
372__LIBC_PTHREAD __libc_threadLookup2(int (pfnCallback)(__LIBC_PTHREAD pCur, __LIBC_PTHREAD pBest, void *pvParam), void *pvParam)
373{
374 LIBCLOG_ENTER("pfnCallback=%p pvParam=%p\n", (void *)pfnCallback, pvParam);
375 int rc;
376 __LIBC_PTHREAD pThrd;
377 __LIBC_PTHREAD pBest = NULL;
378
379 /* can't search something which isn't there. */
380 if (gmtxThrdDB.fs == _FMS_UNINIT)
381 LIBCLOG_ERROR_RETURN_P(NULL);
382
383 rc = _fmutex_request(&gmtxThrdDB, 0);
384 if (rc)
385 LIBCLOG_ERROR_RETURN(NULL, "ret NULL - fmutex f**ked. rc=%d\n", rc);
386
387 for (pThrd = gpThrdDB; pThrd; pThrd = pThrd->pNext)
388 {
389 rc = pfnCallback(pThrd, pBest, pvParam);
390 if (rc == 0)
391 continue;
392 else if (rc == 1)
393 {
394 pBest = pThrd;
395 continue;
396 }
397 else if (rc == 2)
398 {
399 pBest = pThrd;
400 break;
401 }
402 else if (rc == -1)
403 break;
404 else
405 {
406 LIBC_ASSERTM_FAILED("Callback returned %d, allowed values are 2, 1, 0 and -1!\n", rc);
407 break;
408 }
409 }
410
411 if (pBest)
412 pBest->cRefs++;
413
414 _fmutex_release(&gmtxThrdDB);
415
416 if (pBest)
417 LIBCLOG_RETURN_MSG(pBest, "ret %p (tid=%d)\n", (void *)pBest, pBest->tid);
418 LIBCLOG_ERROR_RETURN(pBest, "ret NULL\n");
419}
420
421
422
423int __libc_threadEnum(int (pfnCallback)(__LIBC_PTHREAD pCur, void *pvParam), void *pvParam)
424{
425 LIBCLOG_ENTER("pfnCallback=%p pvParam=%p\n", (void *)pfnCallback, pvParam);
426 int rc;
427 __LIBC_PTHREAD pThrd;
428
429 /* can't search something which isn't there. */
430 if (gmtxThrdDB.fs == _FMS_UNINIT)
431 LIBCLOG_ERROR_RETURN_P(NULL);
432
433 rc = _fmutex_request(&gmtxThrdDB, 0);
434 if (rc)
435 LIBCLOG_ERROR_RETURN(NULL, "ret NULL - fmutex f**ked. rc=%d\n", rc);
436
437 for (pThrd = gpThrdDB; pThrd; pThrd = pThrd->pNext)
438 {
439 rc = pfnCallback(pThrd, pvParam);
440 if (rc == 0)
441 continue;
442 else if (rc == -1)
443 break;
444 else
445 {
446 LIBC_ASSERTM_FAILED("Callback returned %d, allowed values are 0 and -1!\n", rc);
447 break;
448 }
449 }
450
451 _fmutex_release(&gmtxThrdDB);
452
453 LIBCLOG_RETURN_INT(rc);
454}
455
456int __libc_ThreadRegisterTermCallback(__LIBC_PTHREADTERMCBREGREC pRegRec)
457{
458 LIBCLOG_ENTER("pRegRec=%p:{.pNext=%p, .fFlags=%u, .pfnCallback=%p}\n", (void *)pRegRec,
459 pRegRec ? (void *)pRegRec->pNext : NULL,
460 pRegRec ? pRegRec->fFlags : 0,
461 pRegRec ? (void *)pRegRec->pfnCallback : NULL);
462
463 /*
464 * Validate input.
465 */
466 if (pRegRec->pNext)
467 {
468 errno = EINVAL;
469 LIBCLOG_ERROR_RETURN(-1, "ret -1 - pNext must be NULL not %p\n", (void * )pRegRec->pNext);
470 }
471 if (!pRegRec->pfnCallback)
472 {
473 errno = EINVAL;
474 LIBCLOG_ERROR_RETURN(-1, "ret -1 - pfnCallback not be NULL\n");
475 }
476 if (pRegRec->fFlags)
477 {
478 errno = EINVAL;
479 LIBCLOG_ERROR_RETURN(-1, "ret -1 - fFlags must be ZERO not %u\n", pRegRec->fFlags);
480 }
481
482 /*
483 * Insert into the LIFO.
484 */
485 _smutex_request(&gsmtxTerm);
486 if ( !pRegRec->pNext
487 && pRegRec != gpTermTail)
488 {
489 pRegRec->pNext = gpTermTail;
490 gpTermTail = pRegRec;
491 if (!gpTermHead)
492 gpTermHead = pRegRec;
493 _smutex_release(&gsmtxTerm);
494 LIBCLOG_RETURN_INT(0);
495 }
496 else
497 {
498 _smutex_release(&gsmtxTerm);
499 errno = EEXIST;
500 LIBCLOG_ERROR_RETURN(-1, "ret -1 - Double registration of %p\n", (void *)pRegRec);
501 }
502}
503
504
505void __libc_threadTermination(unsigned fFlags)
506{
507 LIBCLOG_ENTER("fFlags=%u\n", fFlags);
508 __LIBC_PTHREADTERMCBREGREC pCur;
509 __LIBC_PTHREADTERMCBREGREC pTail;
510
511 /*
512 * We'll need to do this in a safe manner.
513 *
514 * ASSUME no removal of records.
515 * Thus we just pick the head and tail record and walk them.
516 */
517 _smutex_request(&gsmtxTerm);
518 pTail = gpTermTail;
519 pCur = gpTermHead;
520 _smutex_release(&gsmtxTerm);
521
522 while (pCur)
523 {
524 /* call it */
525 LIBCLOG_MSG("calling %p with %p\n", (void *)pCur->pfnCallback, (void *)pCur);
526 pCur->pfnCallback(pCur, fFlags);
527
528 /* next */
529 if (pCur == pTail)
530 break;
531 pCur = pCur->pNext;
532 }
533
534 LIBCLOG_RETURN_VOID();
535}
536
Note: See TracBrowser for help on using the repository browser.