source: branches/branch-1-0/src/helpers/threads.c@ 472

Last change on this file since 472 was 229, checked in by umoeller, 23 years ago

Sources as of 1.0.0.

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