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

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

misc. changes.

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