source: trunk/src/helpers/threads.c@ 117

Last change on this file since 117 was 116, checked in by umoeller, 24 years ago

More updates.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 21.2 KB
Line 
1
2/*
3 *@@sourcefile threads.c:
4 * contains helper functions for creating, destroying, and
5 * synchronizing threads, including PM threads with a
6 * message queue which is created automatically.
7 *
8 * See thrCreate() for how to start such a thread.
9 *
10 * Usage: All OS/2 programs.
11 *
12 * Function prefixes (new with V0.81):
13 * -- thr* Thread helper functions
14 *
15 * This file is new with V0.81 and contains all the thread
16 * functions that used to be in helpers.c.
17 *
18 * Note: Version numbering in this file relates to XWorkplace version
19 * numbering.
20 *
21 *@@header "helpers\threads.h"
22 */
23
24/*
25 * Copyright (C) 1997-2000 Ulrich M”ller.
26 * This file is part of the "XWorkplace helpers" source package.
27 * This is free software; you can redistribute it and/or modify
28 * it under the terms of the GNU General Public License as published
29 * by the Free Software Foundation, in version 2 as it comes in the
30 * "COPYING" file of the XWorkplace main distribution.
31 * This program is distributed in the hope that it will be useful,
32 * but WITHOUT ANY WARRANTY; without even the implied warranty of
33 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
34 * GNU General Public License for more details.
35 */
36
37#define OS2EMX_PLAIN_CHAR
38 // this is needed for "os2emx.h"; if this is defined,
39 // emx will define PSZ as _signed_ char, otherwise
40 // as unsigned char
41
42#define INCL_DOSPROCESS
43#define INCL_DOSSEMAPHORES
44#define INCL_DOSERRORS
45#include <os2.h>
46
47#include <string.h>
48#include <stdlib.h>
49
50#include "setup.h" // code generation and debugging options
51
52#include "helpers\linklist.h"
53#include "helpers\threads.h"
54
55#pragma hdrstop
56
57/*
58 *@@category: Helpers\Control program helpers\Thread management
59 * see threads.c.
60 */
61
62/* ******************************************************************
63 *
64 * Global variables
65 *
66 ********************************************************************/
67
68LINKLIST G_llThreadInfos;
69 // linked list of all THREADINFOS ever created...
70 // no auto-free
71HMTX G_hmtxThreadInfos = NULLHANDLE;
72
73/* ******************************************************************
74 *
75 * Functions
76 *
77 ********************************************************************/
78
79/*
80 *@@ LockThreadInfos:
81 *
82 *@@added V0.9.9 (2001-03-07) [umoeller]
83 */
84
85BOOL LockThreadInfos(VOID)
86{
87 APIRET arc = NO_ERROR;
88
89 if (G_hmtxThreadInfos == NULLHANDLE)
90 {
91 // first call:
92 arc = DosCreateMutexSem(NULL, // unnamed
93 &G_hmtxThreadInfos,
94 0, // unshared
95 TRUE); // request!
96 lstInit(&G_llThreadInfos, FALSE);
97 }
98 else
99 arc = DosRequestMutexSem(G_hmtxThreadInfos,
100 SEM_INDEFINITE_WAIT);
101
102 return (arc == NO_ERROR);
103}
104
105/*
106 *@@ UnlockThreadInfos:
107 *
108 *@@added V0.9.9 (2001-03-07) [umoeller]
109 */
110
111VOID UnlockThreadInfos(VOID)
112{
113 DosReleaseMutexSem(G_hmtxThreadInfos);
114}
115
116/*
117 *@@ thr_fntGeneric:
118 * generic thread function used by thrCreate.
119 * This in turn calls the actual thread function
120 * specified with thrCreate.
121 *
122 *@@added V0.9.2 (2000-03-05) [umoeller]
123 *@@changed V0.9.7 (2000-12-18) [lafaix]: THRF_TRANSIENT support added
124 */
125
126VOID _Optlink thr_fntGeneric(PVOID ptiMyself)
127{
128 PTHREADINFO pti = (PTHREADINFO)ptiMyself;
129
130 if (pti)
131 {
132 if (pti->flFlags & THRF_WAIT)
133 // "Wait" flag set: thrCreate is then
134 // waiting on the wait event sem posted...
135 // note: we do not post if THRF_WAIT_EXPLICIT!
136 // thread must then post itself
137 DosPostEventSem(pti->hevRunning);
138
139 if (pti->flFlags & THRF_PMMSGQUEUE)
140 {
141 // create msg queue
142 if ((pti->hab = WinInitialize(0)))
143 {
144 if ((pti->hmq = WinCreateMsgQueue(pti->hab, 4000)))
145 {
146 // run thread func
147 ((PTHREADFUNC)pti->pThreadFunc)(pti);
148 WinDestroyMsgQueue(pti->hmq);
149 }
150 WinTerminate(pti->hab);
151 }
152 }
153 else
154 // no msgqueue:
155 // run thread func without msg queue
156 ((PTHREADFUNC)pti->pThreadFunc)(pti);
157
158 if (pti->flFlags & (THRF_WAIT | THRF_WAIT_EXPLICIT)) // V0.9.9 (2001-03-14) [umoeller]
159 // "Wait" flag set: delete semaphore
160 DosCloseEventSem(pti->hevRunning);
161
162 // V0.9.9 (2001-03-07) [umoeller]
163 // remove thread from global list
164 if (LockThreadInfos())
165 {
166 lstRemoveItem(&G_llThreadInfos, pti);
167 UnlockThreadInfos();
168 }
169
170 // set exit flags
171 // V0.9.7 (2000-12-20) [umoeller]
172 pti->fExitComplete = TRUE;
173 pti->tid = NULLHANDLE;
174
175 if (pti->ptidRunning)
176 // clear "running" flag
177 *(pti->ptidRunning) = 0;
178
179 // (2000-12-18) [lafaix] clean up pti if thread is transient.
180 if (pti->flFlags & THRF_TRANSIENT)
181 free(pti);
182 }
183
184 // thread func exits
185}
186
187/*
188 *@@ thrCreate:
189 * this function fills a THREADINFO structure and starts
190 * a new thread using _beginthread.
191 *
192 * You must pass the thread function in pfn, which will
193 * then be executed. The thread will be passed a pointer
194 * to the THREADINFO structure as its thread parameter.
195 * The ulData field in that structure is set to ulData
196 * here. Use whatever you like.
197 *
198 * The thread function must be declared like this:
199 *
200 + void _Optlink fntWhatever(PTHREADINFO ptiMyself)
201 *
202 * You should manually specify _Optlink because if the
203 * function is prototyped somewhere, VAC will automatically
204 * modify the function's linkage, and you'll run into
205 * crashes.
206 *
207 * The thread's ptiMyself is then a pointer to the
208 * THREADINFO structure passed to this function.
209 * In your thread function, ulData may be obtained like this:
210 *
211 + ULONG ulData = ptiMyself->ulData;
212 *
213 * thrCreate does not start your thread func directly,
214 * but only through the thr_fntGeneric wrapper to
215 * provide additional functionality. As a consequence,
216 * in your own thread function, NEVER call _endthread
217 * explicitly, because this would skip the exit processing
218 * (cleanup) in thr_fntGeneric. Instead, just fall out of
219 * your thread function, or return().
220 *
221 * The THREADINFO structure passed to this function must
222 * be accessible all the time while the thread is running
223 * because the thr* functions will use it for maintenance.
224 * This function does NOT check whether a thread is
225 * already running in *pti. Do not use the same THREADINFO
226 * for several threads.
227 *
228 * If you do not want to manage the structure yourself,
229 * you can pass the THRF_TRANSIENT flag (see below).
230 *
231 * ptidRunning is an optional parameter and can be NULL.
232 * If non-null, it must point to a ULONG which will contain
233 * the thread's TID while the thread is still running.
234 * Once the thread exits, the ULONG will automatically
235 * be set to zero; this is useful for a quick check whether
236 * the thread is currently busy.
237 *
238 * Note: ptidRunning is only reliable if you set the
239 * THRF_WAIT or THRF_WAIT_EXPLICIT flags.
240 *
241 * flFlags can be any combination of the following:
242 *
243 * -- THRF_PMMSGQUEUE: creates a PM message queue on the
244 * thread. Your thread function will find the HAB and
245 * the HMQ in its THREADINFO. These are automatically
246 * destroyed when the thread terminates.
247 *
248 * WARNING: Be careful with this setting for short-lived
249 * threads which are started synchronously while some
250 * other thread is locking PM, e.g. in a WinSendMsg
251 * call. While creation of the thread will still
252 * succeed, the thread will _not_ terminate properly
253 * while PM is locked, so do not wait for such a thread
254 * to terminate!! (V0.9.16)
255 *
256 * -- THRF_WAIT: if this is set, thrCreate does not
257 * return to the caller until your thread function
258 * has successfully started running. This is done by
259 * waiting on an event semaphore which is automatically
260 * posted by thr_fntGeneric. This is useful for the
261 * typical PM "Worker" thread where you need to disable
262 * menu items on thread 1 while the thread is running.
263 *
264 * -- THRF_WAIT_EXPLICIT: like THRF_WAIT, but in this case,
265 * your thread function _must_ post THREADINFO.hevRunning
266 * yourself (thr_fntGeneric will not automatically
267 * post it). Useful for waiting until your own thread
268 * function is fully initialized, e.g. if it creates
269 * an object window.
270 *
271 * WARNING: if your thread forgets to post this, we'll
272 * hang.
273 *
274 * Added V0.9.9 (2001-03-14) [umoeller].
275 *
276 * -- THRF_TRANSIENT: creates a "transient" thread where
277 * pti may be NULL. A THREADINFO structure is then
278 * allocated from the heap internally, but not visible
279 * to the caller.
280 *
281 * This now (V0.9.9) returns the TID of the new thread or
282 * null on errors.
283 *
284 *@@changed V0.9.0 [umoeller]: default stack size raised for Watcom (thanks, Rdiger Ihle)
285 *@@changed V0.9.0 [umoeller]: _beginthread is now only called after all variables have been set (thanks, Rdiger Ihle)
286 *@@changed V0.9.2 (2000-03-04) [umoeller]: added stack size parameter
287 *@@changed V0.9.2 (2000-03-06) [umoeller]: now using thr_fntGeneric; thrGoodbye is no longer needed
288 *@@changed V0.9.3 (2000-04-29) [umoeller]: removed stack size param; added fCreateMsgQueue
289 *@@changed V0.9.3 (2000-05-01) [umoeller]: added pbRunning and flFlags
290 *@@changed V0.9.5 (2000-08-26) [umoeller]: now using PTHREADINFO
291 *@@changed V0.9.7 (2000-12-18) [lafaix]: THRF_TRANSIENT support added
292 *@@changed V0.9.9 (2001-02-06) [umoeller]: now returning TID
293 *@@changed V0.9.9 (2001-03-07) [umoeller]: added pcszThreadName
294 *@@changed V0.9.9 (2001-03-14) [umoeller]: added THRF_WAIT_EXPLICIT
295 *@@changed V0.9.12 (2001-05-20) [umoeller]: changed pfRunning to ptidRunning
296 *@@changed V0.9.16 (2001-10-28) [umoeller]: made ptidRunning volatile to prohibit compiler optimizations
297 */
298
299ULONG thrCreate(PTHREADINFO pti, // out: THREADINFO data
300 PTHREADFUNC pfn, // in: _Optlink thread function
301 volatile unsigned long *ptidRunning,
302 // out: variable set to TID while thread is running;
303 // ptr can be NULL
304 const char *pcszThreadName, // in: thread name (for identification)
305 ULONG flFlags, // in: THRF_* flags
306 ULONG ulData) // in: user data to be stored in THREADINFO
307{
308 ULONG ulrc = 0; // V0.9.9 (2001-02-06) [umoeller]
309
310 // (2000-12-18) [lafaix] TRANSIENT
311 if (flFlags & THRF_TRANSIENT)
312 {
313 if (pti == NULL)
314 pti = (PTHREADINFO)malloc(sizeof(THREADINFO));
315 // cleaned up by thr_fntGeneric on exit
316 }
317
318 if (pti)
319 {
320 memset(pti, 0, sizeof(THREADINFO));
321 pti->cbStruct = sizeof(THREADINFO);
322 pti->pThreadFunc = (PVOID)pfn;
323 pti->ptidRunning = ptidRunning;
324 pti->pcszThreadName = pcszThreadName; // V0.9.9 (2001-03-07) [umoeller]
325 pti->flFlags = flFlags;
326 pti->ulData = ulData;
327
328 if (flFlags & (THRF_WAIT | THRF_WAIT_EXPLICIT)) // V0.9.9 (2001-03-14) [umoeller]
329 // "Wait" flag set: create an event semaphore which
330 // will be posted by thr_fntGeneric (THRF_WAIT only)
331 if (DosCreateEventSem(NULL, // unnamed
332 &pti->hevRunning,
333 0, // unshared
334 FALSE) // not posted (reset)
335 != NO_ERROR)
336 {
337 if (flFlags & THRF_TRANSIENT)
338 free(pti);
339
340 // stop right here
341 pti = NULL;
342 }
343
344 if (pti)
345 {
346 pti->tid = _beginthread( // moved, V0.9.0 (hint: Rdiger Ihle)
347 thr_fntGeneric, // changed V0.9.2 (2000-03-06) [umoeller]
348 0, // unused compatibility param
349 3*96000, // plenty of stack
350 pti); // parameter passed to thread
351 ulrc = pti->tid;
352
353 if (pti->ptidRunning)
354 // set "running" flag // V0.9.12 (2001-05-20) [umoeller]
355 *(pti->ptidRunning) = pti->tid;
356
357 if (ulrc)
358 {
359 if (LockThreadInfos())
360 {
361 lstAppendItem(&G_llThreadInfos, pti);
362 UnlockThreadInfos();
363 }
364
365 if (flFlags & (THRF_WAIT | THRF_WAIT_EXPLICIT))
366 {
367 // "Wait" flag set: wait on event semaphore
368 DosWaitEventSem(pti->hevRunning,
369 SEM_INDEFINITE_WAIT);
370 }
371 }
372 }
373 }
374
375 return (ulrc);
376}
377
378/*
379 *@@ thrRunSync:
380 * runs the specified thread function synchronously.
381 *
382 * This is a wrapper around thrCreate. However, this
383 * function does not return until the thread function
384 * finishes. This creates a modal message loop on the
385 * calling thread so that the PM message queue is not
386 * blocked while the thread is running. Naturally this
387 * implies that the calling thread has a message queue.
388 *
389 * As a special requirement, your thread function (pfn)
390 * must post WM_USER to THREADINFO.hwndNotify just before
391 * exiting. The mp1 value of WM_USER will then be returned
392 * by this function.
393 *
394 *@@added V0.9.5 (2000-08-26) [umoeller]
395 *@@changed V0.9.9 (2001-03-07) [umoeller]: added pcszThreadName
396 */
397
398ULONG thrRunSync(HAB hab, // in: anchor block of calling thread
399 PTHREADFUNC pfn, // in: passed to thrCreate
400 const char *pcszThreadName, // in: passed to thrCreate
401 ULONG ulData) // in: passed to thrCreate
402{
403 ULONG ulrc = 0;
404 QMSG qmsg;
405 BOOL fQuit = FALSE;
406 HWND hwndNotify;
407 if (hwndNotify = WinCreateWindow(HWND_OBJECT,
408 WC_BUTTON,
409 (PSZ)"",
410 0,
411 0,0,0,0,
412 0,
413 HWND_BOTTOM,
414 0,
415 0,
416 NULL))
417 {
418 THREADINFO ti = {0};
419 thrCreate(&ti,
420 pfn,
421 NULL,
422 pcszThreadName,
423 THRF_PMMSGQUEUE,
424 ulData);
425 ti.hwndNotify = hwndNotify;
426
427 while (WinGetMsg(hab,
428 &qmsg, 0, 0, 0))
429 {
430 // current message for our object window?
431 if ( (qmsg.hwnd == hwndNotify)
432 && (qmsg.msg == WM_USER)
433 )
434 {
435 fQuit = TRUE;
436 ulrc = (ULONG)qmsg.mp1;
437 }
438
439 WinDispatchMsg(hab, &qmsg);
440 if (fQuit)
441 break;
442 }
443
444 // we must wait for the thread to finish, or
445 // otherwise THREADINFO is deleted from the stack
446 // before the thread exits... will crash!
447 thrWait(&ti);
448
449 WinDestroyWindow(hwndNotify);
450 }
451
452 return (ulrc);
453}
454
455/*
456 *@@ thrListThreads:
457 * returns an array of THREADINFO structures
458 * for all threads that have been started using
459 * thrCreate (or thrRunSync).
460 *
461 * If no threads are running yet, this returns
462 * NULL.
463 *
464 * Otherwise, this returns the pointer to the
465 * first array item, and *pcThreads receives
466 * the array item count (NOT the total array
467 * size). The array is a copied snapshot of all
468 * current THREADINFO's and must be free()'d
469 * by the caller.
470 *
471 *@@added V0.9.9 (2001-03-07) [umoeller]
472 */
473
474PTHREADINFO thrListThreads(PULONG pcThreads)
475{
476 PTHREADINFO pArray = 0;
477
478 if (LockThreadInfos())
479 {
480 PTHREADINFO pThis;
481 PLISTNODE pNode;
482 *pcThreads = lstCountItems(&G_llThreadInfos);
483 _Pmpf((__FUNCTION__ ": got %d threads", *pcThreads));
484 pArray = (PTHREADINFO)malloc(*pcThreads * sizeof(THREADINFO));
485 pThis = pArray;
486
487 pNode = lstQueryFirstNode(&G_llThreadInfos);
488 while (pNode)
489 {
490 memcpy(pThis,
491 (PTHREADINFO)pNode->pItemData,
492 sizeof(THREADINFO));
493 pThis++;
494 pNode = pNode->pNext;
495 }
496
497 UnlockThreadInfos();
498 }
499
500 return (pArray);
501}
502
503/*
504 *@@ thrFindThread:
505 * attempts to find the thread with the specified
506 * TID; if found, returns TRUE and copies its
507 * THREADINFO into *pti.
508 *
509 *@@added V0.9.9 (2001-03-07) [umoeller]
510 */
511
512BOOL thrFindThread(PTHREADINFO pti,
513 ULONG tid)
514{
515 BOOL brc = FALSE;
516 if (LockThreadInfos())
517 {
518 PLISTNODE pNode = lstQueryFirstNode(&G_llThreadInfos);
519 while (pNode)
520 {
521 PTHREADINFO ptiThis = (PTHREADINFO)pNode->pItemData;
522 if (ptiThis->tid == tid)
523 {
524 memcpy(pti, ptiThis, sizeof(THREADINFO));
525 brc = TRUE;
526 break;
527 }
528 pNode = pNode->pNext;
529 }
530
531 UnlockThreadInfos();
532 }
533
534 return (brc);
535}
536
537/*
538 *@@ thrClose:
539 * this functions sets the "fExit" flag in
540 * THREADINFO to TRUE.
541 *
542 * The thread should monitor this flag
543 * periodically and then terminate itself.
544 */
545
546BOOL thrClose(PTHREADINFO pti)
547{
548 if (pti)
549 {
550 pti->fExit = TRUE;
551 return (TRUE);
552 }
553 return (FALSE);
554}
555
556/*
557 *@@ thrWait:
558 * this function waits for a thread to end by calling
559 * DosWaitThread. Note that this blocks the calling
560 * thread, so only use this function when you're sure
561 * the thread will actually terminate.
562 *
563 * Returns FALSE if the thread wasn't running or TRUE
564 * if it was and has terminated.
565 *
566 *@@changed V0.9.0 [umoeller]: now checking for whether pti->tid is still != 0
567 */
568
569BOOL thrWait(PTHREADINFO pti)
570{
571 if (pti)
572 if (pti->tid)
573 {
574 DosWaitThread(&pti->tid, DCWW_WAIT);
575 pti->tid = NULLHANDLE;
576 return (TRUE);
577 }
578 return (FALSE);
579}
580
581/*
582 *@@ thrFree:
583 * this is a combination of thrClose and
584 * thrWait, i.e. this func does not return
585 * until the specified thread has ended.
586 */
587
588BOOL thrFree(PTHREADINFO pti)
589{
590 if (pti->tid)
591 {
592 thrClose(pti);
593 thrWait(pti);
594 }
595 return (TRUE);
596}
597
598/*
599 *@@ thrKill:
600 * just like thrFree, but the thread is
601 * brutally killed, using DosKillThread.
602 *
603 * Note: DO NOT USE THIS. DosKillThread
604 * cannot clean up the C runtime. In the
605 * worst case, this hangs the system
606 * because the runtime hasn't released
607 * a semaphore or something like that.
608 */
609
610BOOL thrKill(PTHREADINFO pti)
611{
612 if (pti->tid)
613 {
614 DosResumeThread(pti->tid);
615 // this returns an error if the thread
616 // is not suspended, but otherwise the
617 // system might hang
618 DosKillThread(pti->tid);
619 }
620 return (TRUE);
621}
622
623/*
624 *@@ thrQueryID:
625 * returns thread ID or NULLHANDLE if
626 * the specified thread is not or no
627 * longer running.
628 */
629
630TID thrQueryID(const THREADINFO* pti)
631{
632 if (pti)
633 if (!(pti->fExitComplete))
634 return (pti->tid);
635
636 return (NULLHANDLE);
637}
638
639/*
640 *@@ thrQueryPriority:
641 * returns the priority of the calling thread.
642 * The low byte of the low word is a hexadecimal value
643 * representing a rank (value 0 to 31) within a priority class.
644 * Class values, found in the high byte of the low word, are
645 * as follows:
646 * -- 0x01 idle
647 * -- 0x02 regular
648 * -- 0x03 time-critical
649 * -- 0x04 server
650 *
651 * Note: This cannot retrieve the priority of a
652 * thread other than the one on which this function
653 * is running. Use prc16QueryThreadInfo for that.
654 */
655
656ULONG thrQueryPriority(VOID)
657{
658 PTIB ptib;
659 PPIB ppib;
660 if (DosGetInfoBlocks(&ptib, &ppib) == NO_ERROR)
661 if (ptib)
662 if (ptib->tib_ptib2)
663 return (ptib->tib_ptib2->tib2_ulpri);
664 return (0);
665}
666
667
Note: See TracBrowser for help on using the repository browser.