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

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