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

Last change on this file since 209 was 196, checked in by umoeller, 23 years ago

Misc fixes.

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