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

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

misc updates

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