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

Last change on this file since 38 was 38, checked in by umoeller, 25 years ago

Updates to XML.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 14.9 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\threads.h"
53
54#pragma hdrstop
55
56/*
57 *@@category: Helpers\Control program helpers\Thread management
58 * see threads.c.
59 */
60
61/*
62 *@@ thr_fntGeneric:
63 * generic thread function used by thrCreate.
64 * This in turn calls the actual thread function
65 * specified with thrCreate.
66 *
67 *@@added V0.9.2 (2000-03-05) [umoeller]
68 *@@changed V0.9.7 (2000-12-18) [lafaix]: THRF_TRANSIENT support added
69 */
70
71VOID _Optlink thr_fntGeneric(PVOID ptiMyself)
72{
73 PTHREADINFO pti = (PTHREADINFO)ptiMyself;
74
75 if (pti)
76 {
77 if (pti->pfRunning)
78 // set "running" flag
79 *(pti->pfRunning) = TRUE;
80
81 if (pti->flFlags & THRF_WAIT)
82 // "Wait" flag set: thrCreate is then
83 // waiting on the wait event sem posted
84 DosPostEventSem(pti->hevRunning);
85
86 if (pti->flFlags & THRF_PMMSGQUEUE)
87 {
88 // create msg queue
89 if ((pti->hab = WinInitialize(0)))
90 {
91 if ((pti->hmq = WinCreateMsgQueue(pti->hab, 4000)))
92 {
93 // run thread func
94 ((PTHREADFUNC)pti->pThreadFunc)(pti);
95
96 WinDestroyMsgQueue(pti->hmq);
97
98 }
99 WinTerminate(pti->hab);
100 }
101 }
102 else
103 // no msgqueue:
104 ((PTHREADFUNC)pti->pThreadFunc)(pti);
105
106 if (pti->flFlags & THRF_WAIT)
107 // "Wait" flag set: delete semaphore
108 DosCloseEventSem(pti->hevRunning);
109
110 // (2000-12-18) [lafaix] clean up pti if thread is transient.
111 if (pti->flFlags & THRF_TRANSIENT)
112 free(pti);
113 else
114 {
115 // for non-transient threads: set exit flags
116 // V0.9.7 (2000-12-20) [umoeller]
117 // thread func returns:
118 pti->fExitComplete = TRUE;
119 pti->tid = NULLHANDLE;
120
121 if (pti->pfRunning)
122 // clear "running" flag
123 *(pti->pfRunning) = FALSE;
124 }
125 }
126}
127
128/*
129 *@@ thrCreate:
130 * this function fills a THREADINFO structure in *pti
131 * and starts a new thread using _beginthread.
132 *
133 * You must pass the thread function in pfn, which will
134 * then be executed. The thread will be passed a pointer
135 * to the THREADINFO structure as its thread parameter.
136 * The ulData field in that structure is set to ulData
137 * here. Use whatever you like.
138 *
139 * The thread function must be declared like this:
140 *
141 + void _Optlink fntWhatever(PTHREADINFO ptiMyself)
142 *
143 * You should manually specify _Optlink because if the
144 * function is prototyped somewhere, VAC will automatically
145 * modify the function's linkage, and you'll run into
146 * compiler warnings.
147 *
148 * ptiMyself is then a pointer to the THREADINFO structure.
149 * ulData may be obtained like this:
150 + ULONG ulData = ((PTHREADINFO)ptiMyself)->ulData;
151 *
152 * thrCreate does not call your thread func directly,
153 * but only through the thr_fntGeneric wrapper to
154 * provide additional functionality. As a consequence,
155 * in your own thread function, NEVER call _endthread
156 * explicitly, because this would skip the exit processing
157 * (cleanup) in thr_fntGeneric. Instead, just fall out of
158 * your thread function.
159 *
160 * This function does NOT check whether a thread is
161 * already running in *pti. If it is, that information
162 * will be lost.
163 *
164 * flFlags can be any combination of the following:
165 *
166 * -- THRF_PMMSGQUEUE: creates a PM message queue on the
167 * thread. Your thread function will find the HAB and
168 * the HMQ in its THREADINFO. These are automatically
169 * destroyed when the thread terminates.
170 *
171 * -- THRF_WAIT: if this is set, thrCreate does not
172 * return to the caller until your thread function
173 * has successfully started running. This is done by
174 * waiting on an event semaphore which is automatically
175 * posted by thr_fntGeneric. This is useful for the
176 * typical PM "Worker" thread where you need to disable
177 * menu items on thread 1 while the thread is running.
178 *
179 * -- THRF_TRANSIENT: creates a "transient" thread where
180 * pti may be NULL. A THREADINFO structure is then
181 * allocated from the heap internally, but not visible
182 * to the caller.
183 *
184 * This now (V0.9.9) returns the TID of the new thread or
185 * null on errors.
186 *
187 *@@changed V0.9.0 [umoeller]: default stack size raised for Watcom (thanks, Rdiger Ihle)
188 *@@changed V0.9.0 [umoeller]: _beginthread is now only called after all variables have been set (thanks, Rdiger Ihle)
189 *@@changed V0.9.2 (2000-03-04) [umoeller]: added stack size parameter
190 *@@changed V0.9.2 (2000-03-06) [umoeller]: now using thr_fntGeneric; thrGoodbye is no longer needed
191 *@@changed V0.9.3 (2000-04-29) [umoeller]: removed stack size param; added fCreateMsgQueue
192 *@@changed V0.9.3 (2000-05-01) [umoeller]: added pbRunning and flFlags
193 *@@changed V0.9.5 (2000-08-26) [umoeller]: now using PTHREADINFO
194 *@@changed V0.9.7 (2000-12-18) [lafaix]: THRF_TRANSIENT support added
195 *@@changed V0.9.9 (2001-02-06) [umoeller]: now returning TID
196 */
197
198ULONG thrCreate(PTHREADINFO pti, // out: THREADINFO data
199 PTHREADFUNC pfn, // in: _Optlink thread function
200 PBOOL pfRunning, // out: variable set to TRUE while thread is running;
201 // ptr can be NULL
202 ULONG flFlags, // in: THRF_* flags
203 ULONG ulData) // in: user data to be stored in THREADINFO
204{
205 ULONG ulrc = 0; // V0.9.9 (2001-02-06) [umoeller]
206
207 // (2000-12-18) [lafaix] TRANSIENT
208 if (flFlags & THRF_TRANSIENT)
209 {
210 if (pti == NULL)
211 pti = (PTHREADINFO) malloc(sizeof(THREADINFO));
212 }
213
214 if (pti)
215 {
216 // we arrive here if *ppti was NULL or (*ppti->tid == NULLHANDLE),
217 // i.e. the thread is not already running.
218 // _beginthread is contained both in the VAC++ and EMX
219 // C libraries with this syntax.
220 memset(pti, 0, sizeof(THREADINFO));
221 pti->cbStruct = sizeof(THREADINFO);
222 pti->pThreadFunc = (PVOID)pfn;
223 pti->pfRunning = pfRunning;
224 pti->flFlags = flFlags;
225 pti->ulData = ulData;
226
227 if (flFlags & THRF_WAIT)
228 // "Wait" flag set: create an event semaphore which
229 // will be posted by thr_fntGeneric
230 if (DosCreateEventSem(NULL, // unnamed
231 &pti->hevRunning,
232 0, // unshared
233 FALSE) // not posted (reset)
234 != NO_ERROR)
235 {
236 if (flFlags & THRF_TRANSIENT)
237 free(pti);
238
239 // stop right here
240 pti = NULL;
241 }
242
243 if (pti)
244 {
245 pti->tid = _beginthread( // moved, V0.9.0 (hint: Rdiger Ihle)
246 thr_fntGeneric, // changed V0.9.2 (2000-03-06) [umoeller]
247 0, // unused compatibility param
248 3*96000, // plenty of stack
249 pti); // parameter passed to thread
250 ulrc = pti->tid;
251
252 if (ulrc)
253 if (flFlags & THRF_WAIT)
254 {
255 // "Wait" flag set: wait on event semaphore
256 // posted by thr_fntGeneric
257 DosWaitEventSem(pti->hevRunning,
258 SEM_INDEFINITE_WAIT);
259 }
260 }
261 }
262
263 return (ulrc);
264}
265
266/*
267 *@@ thrRunSync:
268 * runs the specified thread function synchronously.
269 *
270 * This is a wrapper around thrCreate. However, this
271 * function does not return until the thread function
272 * finishes. This creates a modal message loop on the
273 * calling thread so that the PM message queue is not
274 * blocked while the thread is running. Naturally this
275 * implies that the calling thread has a message queue.
276 *
277 * As a special requirement, your thread function (pfn)
278 * must post WM_USER to THREADINFO.hwndNotify just before
279 * exiting. The mp1 value of WM_USER will then be returned
280 * by this function.
281 *
282 *@@added V0.9.5 (2000-08-26) [umoeller]
283 */
284
285ULONG thrRunSync(HAB hab, // in: anchor block of calling thread
286 PTHREADFUNC pfn, // in: thread function
287 ULONG ulData) // in: data for thread function
288{
289 ULONG ulrc = 0;
290 QMSG qmsg;
291 BOOL fQuit = FALSE;
292 HWND hwndNotify = WinCreateWindow(HWND_OBJECT,
293 WC_BUTTON,
294 (PSZ)"",
295 0,
296 0,0,0,0,
297 0,
298 HWND_BOTTOM,
299 0,
300 0,
301 NULL);
302 if (hwndNotify)
303 {
304 THREADINFO ti = {0};
305 thrCreate(&ti,
306 pfn,
307 NULL,
308 THRF_PMMSGQUEUE,
309 ulData);
310 ti.hwndNotify = hwndNotify;
311
312 while (WinGetMsg(hab,
313 &qmsg, 0, 0, 0))
314 {
315 // current message for our object window?
316 if ( (qmsg.hwnd == hwndNotify)
317 && (qmsg.msg == WM_USER)
318 )
319 {
320 fQuit = TRUE;
321 ulrc = (ULONG)qmsg.mp1;
322 }
323
324 WinDispatchMsg(hab, &qmsg);
325 if (fQuit)
326 break;
327 }
328
329 // we must wait for the thread to finish, or
330 // otherwise THREADINFO is deleted from the stack
331 // before the thread exits... will crash!
332 thrWait(&ti);
333
334 WinDestroyWindow(hwndNotify);
335 }
336
337 return (ulrc);
338}
339
340/*
341 *@@ thrClose:
342 * this functions sets the "fExit" flag in
343 * THREADINFO to TRUE.
344 *
345 * The thread should monitor this flag
346 * periodically and then terminate itself.
347 */
348
349BOOL thrClose(PTHREADINFO pti)
350{
351 if (pti)
352 {
353 pti->fExit = TRUE;
354 return (TRUE);
355 }
356 return (FALSE);
357}
358
359/*
360 *@@ thrWait:
361 * this function waits for a thread to end by calling
362 * DosWaitThread. Note that this blocks the calling
363 * thread, so only use this function when you're sure
364 * the thread will actually terminate.
365 *
366 * Returns FALSE if the thread wasn't running or TRUE
367 * if it was and has terminated.
368 *
369 *@@changed V0.9.0 [umoeller]: now checking for whether pti->tid is still != 0
370 */
371
372BOOL thrWait(PTHREADINFO pti)
373{
374 if (pti)
375 if (pti->tid)
376 {
377 DosWaitThread(&pti->tid, DCWW_WAIT);
378 pti->tid = NULLHANDLE;
379 return (TRUE);
380 }
381 return (FALSE);
382}
383
384/*
385 *@@ thrFree:
386 * this is a combination of thrClose and
387 * thrWait, i.e. this func does not return
388 * until the specified thread has ended.
389 */
390
391BOOL thrFree(PTHREADINFO pti)
392{
393 if (pti->tid)
394 {
395 thrClose(pti);
396 thrWait(pti);
397 }
398 return (TRUE);
399}
400
401/*
402 *@@ thrKill:
403 * just like thrFree, but the thread is
404 * brutally killed, using DosKillThread.
405 *
406 * Note: DO NOT USE THIS. DosKillThread
407 * cannot clean up the C runtime. In the
408 * worst case, this hangs the system
409 * because the runtime hasn't released
410 * a semaphore or something like that.
411 */
412
413BOOL thrKill(PTHREADINFO pti)
414{
415 if (pti->tid)
416 {
417 DosResumeThread(pti->tid);
418 // this returns an error if the thread
419 // is not suspended, but otherwise the
420 // system might hang
421 DosKillThread(pti->tid);
422 }
423 return (TRUE);
424}
425
426/*
427 *@@ thrQueryID:
428 * returns thread ID or NULLHANDLE if
429 * the specified thread is not or no
430 * longer running.
431 */
432
433TID thrQueryID(const THREADINFO* pti)
434{
435 if (pti)
436 if (!(pti->fExitComplete))
437 return (pti->tid);
438
439 return (NULLHANDLE);
440}
441
442/*
443 *@@ thrQueryPriority:
444 * returns the priority of the calling thread.
445 * The low byte of the low word is a hexadecimal value
446 * representing a rank (value 0 to 31) within a priority class.
447 * Class values, found in the high byte of the low word, are
448 * as follows:
449 * -- 0x01 idle
450 * -- 0x02 regular
451 * -- 0x03 time-critical
452 * -- 0x04 server
453 *
454 * Note: This cannot retrieve the priority of a
455 * thread other than the one on which this function
456 * is running. Use prc16QueryThreadInfo for that.
457 */
458
459ULONG thrQueryPriority(VOID)
460{
461 PTIB ptib;
462 PPIB ppib;
463 if (DosGetInfoBlocks(&ptib, &ppib) == NO_ERROR)
464 if (ptib)
465 if (ptib->tib_ptib2)
466 return (ptib->tib_ptib2->tib2_ulpri);
467 return (0);
468}
469
470
Note: See TracBrowser for help on using the repository browser.