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

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