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

Last change on this file since 129 was 123, checked in by umoeller, 24 years ago

Lots of changes for icons and refresh.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 22.0 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_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
86BOOL 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
112VOID 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
127VOID _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 pArray = (PTHREADINFO)malloc(*pcThreads * sizeof(THREADINFO));
504 pThis = pArray;
505
506 pNode = lstQueryFirstNode(&G_llThreadInfos);
507 while (pNode)
508 {
509 memcpy(pThis,
510 (PTHREADINFO)pNode->pItemData,
511 sizeof(THREADINFO));
512 pThis++;
513 pNode = pNode->pNext;
514 }
515
516 UnlockThreadInfos();
517 }
518
519 return (pArray);
520}
521
522/*
523 *@@ thrFindThread:
524 * attempts to find the thread with the specified
525 * TID; if found, returns TRUE and copies its
526 * THREADINFO into *pti.
527 *
528 *@@added V0.9.9 (2001-03-07) [umoeller]
529 */
530
531BOOL thrFindThread(PTHREADINFO pti,
532 ULONG tid)
533{
534 BOOL brc = FALSE;
535 if (LockThreadInfos())
536 {
537 PLISTNODE pNode = lstQueryFirstNode(&G_llThreadInfos);
538 while (pNode)
539 {
540 PTHREADINFO ptiThis = (PTHREADINFO)pNode->pItemData;
541 if (ptiThis->tid == tid)
542 {
543 memcpy(pti, ptiThis, sizeof(THREADINFO));
544 brc = TRUE;
545 break;
546 }
547 pNode = pNode->pNext;
548 }
549
550 UnlockThreadInfos();
551 }
552
553 return (brc);
554}
555
556/*
557 *@@ thrClose:
558 * this functions sets the "fExit" flag in
559 * THREADINFO to TRUE.
560 *
561 * The thread should monitor this flag
562 * periodically and then terminate itself.
563 */
564
565BOOL thrClose(PTHREADINFO pti)
566{
567 if (pti)
568 {
569 pti->fExit = TRUE;
570 return (TRUE);
571 }
572 return (FALSE);
573}
574
575/*
576 *@@ thrWait:
577 * this function waits for a thread to end by calling
578 * DosWaitThread. Note that this blocks the calling
579 * thread, so only use this function when you're sure
580 * the thread will actually terminate.
581 *
582 * Update V0.9.16: Do not use this with PM theads at
583 * all. DosWaitThread can hang the system then.
584 *
585 * Returns FALSE if the thread wasn't running or TRUE
586 * if it was and has terminated.
587 *
588 *@@changed V0.9.0 [umoeller]: now checking for whether pti->tid is still != 0
589 */
590
591BOOL thrWait(PTHREADINFO pti)
592{
593 if (pti)
594 if (pti->tid)
595 {
596 DosWaitThread(&pti->tid, DCWW_WAIT);
597 pti->tid = NULLHANDLE;
598 return (TRUE);
599 }
600 return (FALSE);
601}
602
603/*
604 *@@ thrFree:
605 * this is a combination of thrClose and
606 * thrWait, i.e. this func does not return
607 * until the specified thread has ended.
608 */
609
610BOOL thrFree(PTHREADINFO pti)
611{
612 if (pti->tid)
613 {
614 thrClose(pti);
615 thrWait(pti);
616 }
617 return (TRUE);
618}
619
620/*
621 *@@ thrKill:
622 * just like thrFree, but the thread is
623 * brutally killed, using DosKillThread.
624 *
625 * Note: DO NOT USE THIS. DosKillThread
626 * cannot clean up the C runtime. In the
627 * worst case, this hangs the system
628 * because the runtime hasn't released
629 * a semaphore or something like that.
630 */
631
632BOOL thrKill(PTHREADINFO pti)
633{
634 if (pti->tid)
635 {
636 DosResumeThread(pti->tid);
637 // this returns an error if the thread
638 // is not suspended, but otherwise the
639 // system might hang
640 DosKillThread(pti->tid);
641 }
642 return (TRUE);
643}
644
645/*
646 *@@ thrQueryID:
647 * returns thread ID or NULLHANDLE if
648 * the specified thread is not or no
649 * longer running.
650 */
651
652TID thrQueryID(const THREADINFO* pti)
653{
654 if (pti)
655 return (pti->tid);
656
657 return (NULLHANDLE);
658}
659
660/*
661 *@@ thrQueryPriority:
662 * returns the priority of the calling thread.
663 * The low byte of the low word is a hexadecimal value
664 * representing a rank (value 0 to 31) within a priority class.
665 * Class values, found in the high byte of the low word, are
666 * as follows:
667 * -- 0x01 idle
668 * -- 0x02 regular
669 * -- 0x03 time-critical
670 * -- 0x04 server
671 *
672 * Note: This cannot retrieve the priority of a
673 * thread other than the one on which this function
674 * is running. Use prc16QueryThreadInfo for that.
675 */
676
677ULONG thrQueryPriority(VOID)
678{
679 PTIB ptib;
680 PPIB ppib;
681 if (DosGetInfoBlocks(&ptib, &ppib) == NO_ERROR)
682 if (ptib)
683 if (ptib->tib_ptib2)
684 return (ptib->tib_ptib2->tib2_ulpri);
685 return (0);
686}
687
688
Note: See TracBrowser for help on using the repository browser.