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

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

Misc. updates.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 14.7 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 *@@changed V0.9.0 [umoeller]: default stack size raised for Watcom (thanks, Rdiger Ihle)
185 *@@changed V0.9.0 [umoeller]: _beginthread is now only called after all variables have been set (thanks, Rdiger Ihle)
186 *@@changed V0.9.2 (2000-03-04) [umoeller]: added stack size parameter
187 *@@changed V0.9.2 (2000-03-06) [umoeller]: now using thr_fntGeneric; thrGoodbye is no longer needed
188 *@@changed V0.9.3 (2000-04-29) [umoeller]: removed stack size param; added fCreateMsgQueue
189 *@@changed V0.9.3 (2000-05-01) [umoeller]: added pbRunning and flFlags
190 *@@changed V0.9.5 (2000-08-26) [umoeller]: now using PTHREADINFO
191 *@@changed V0.9.7 (2000-12-18) [lafaix]: THRF_TRANSIENT support added
192 */
193
194BOOL thrCreate(PTHREADINFO pti, // out: THREADINFO data
195 PTHREADFUNC pfn, // in: _Optlink thread function
196 PBOOL pfRunning, // out: variable set to TRUE while thread is running;
197 // ptr can be NULL
198 ULONG flFlags, // in: THRF_* flags
199 ULONG ulData) // in: user data to be stored in THREADINFO
200{
201 BOOL rc = FALSE;
202
203 // (2000-12-18) [lafaix] TRANSIENT
204 if (flFlags & THRF_TRANSIENT)
205 {
206 if (pti == NULL)
207 pti = (PTHREADINFO) malloc(sizeof(THREADINFO));
208 else
209 return (rc);
210 }
211
212 if (pti)
213 {
214 // we arrive here if *ppti was NULL or (*ppti->tid == NULLHANDLE),
215 // i.e. the thread is not already running.
216 // _beginthread is contained both in the VAC++ and EMX
217 // C libraries with this syntax.
218 memset(pti, 0, sizeof(THREADINFO));
219 pti->cbStruct = sizeof(THREADINFO);
220 pti->pThreadFunc = (PVOID)pfn;
221 pti->pfRunning = pfRunning;
222 pti->flFlags = flFlags;
223 pti->ulData = ulData;
224
225 rc = TRUE;
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 rc = FALSE;
236
237 pti->fExit = FALSE;
238
239 if (rc)
240 {
241 pti->tid = _beginthread( // moved, V0.9.0 (hint: Rdiger Ihle)
242 thr_fntGeneric, // changed V0.9.2 (2000-03-06) [umoeller]
243 0, // unused compatibility param
244 3*96000, // plenty of stack
245 pti); // parameter passed to thread
246 rc = (pti->tid != 0);
247
248 if (rc)
249 if (flFlags & THRF_WAIT)
250 {
251 // "Wait" flag set: wait on event semaphore
252 // posted by thr_fntGeneric
253 DosWaitEventSem(pti->hevRunning,
254 SEM_INDEFINITE_WAIT);
255 }
256 }
257 }
258
259 return (rc);
260}
261
262/*
263 *@@ thrRunSync:
264 * runs the specified thread function synchronously.
265 *
266 * This is a wrapper around thrCreate. However, this
267 * function does not return until the thread function
268 * finishes. This creates a modal message loop on the
269 * calling thread so that the PM message queue is not
270 * blocked while the thread is running. Naturally this
271 * implies that the calling thread has a message queue.
272 *
273 * As a special requirement, your thread function (pfn)
274 * must post WM_USER to THREADINFO.hwndNotify just before
275 * exiting. The mp1 value of WM_USER will then be returned
276 * by this function.
277 *
278 *@@added V0.9.5 (2000-08-26) [umoeller]
279 */
280
281ULONG thrRunSync(HAB hab, // in: anchor block of calling thread
282 PTHREADFUNC pfn, // in: thread function
283 ULONG ulData) // in: data for thread function
284{
285 ULONG ulrc = 0;
286 QMSG qmsg;
287 BOOL fQuit = FALSE;
288 HWND hwndNotify = WinCreateWindow(HWND_OBJECT,
289 WC_BUTTON,
290 (PSZ)"",
291 0,
292 0,0,0,0,
293 0,
294 HWND_BOTTOM,
295 0,
296 0,
297 NULL);
298 if (hwndNotify)
299 {
300 THREADINFO ti = {0};
301 thrCreate(&ti,
302 pfn,
303 NULL,
304 THRF_PMMSGQUEUE,
305 ulData);
306 ti.hwndNotify = hwndNotify;
307
308 while (WinGetMsg(hab,
309 &qmsg, 0, 0, 0))
310 {
311 // current message for our object window?
312 if ( (qmsg.hwnd == hwndNotify)
313 && (qmsg.msg == WM_USER)
314 )
315 {
316 fQuit = TRUE;
317 ulrc = (ULONG)qmsg.mp1;
318 }
319
320 WinDispatchMsg(hab, &qmsg);
321 if (fQuit)
322 break;
323 }
324
325 // we must wait for the thread to finish, or
326 // otherwise THREADINFO is deleted from the stack
327 // before the thread exits... will crash!
328 thrWait(&ti);
329
330 WinDestroyWindow(hwndNotify);
331 }
332
333 return (ulrc);
334}
335
336/*
337 *@@ thrClose:
338 * this functions sets the "fExit" flag in
339 * THREADINFO to TRUE.
340 *
341 * The thread should monitor this flag
342 * periodically and then terminate itself.
343 */
344
345BOOL thrClose(PTHREADINFO pti)
346{
347 if (pti)
348 {
349 pti->fExit = TRUE;
350 return (TRUE);
351 }
352 return (FALSE);
353}
354
355/*
356 *@@ thrWait:
357 * this function waits for a thread to end by calling
358 * DosWaitThread. Note that this blocks the calling
359 * thread, so only use this function when you're sure
360 * the thread will actually terminate.
361 *
362 * Returns FALSE if the thread wasn't running or TRUE
363 * if it was and has terminated.
364 *
365 *@@changed V0.9.0 [umoeller]: now checking for whether pti->tid is still != 0
366 */
367
368BOOL thrWait(PTHREADINFO pti)
369{
370 if (pti)
371 if (pti->tid)
372 {
373 DosWaitThread(&pti->tid, DCWW_WAIT);
374 pti->tid = NULLHANDLE;
375 return (TRUE);
376 }
377 return (FALSE);
378}
379
380/*
381 *@@ thrFree:
382 * this is a combination of thrClose and
383 * thrWait, i.e. this func does not return
384 * until the specified thread has ended.
385 */
386
387BOOL thrFree(PTHREADINFO pti)
388{
389 if (pti->tid)
390 {
391 thrClose(pti);
392 thrWait(pti);
393 }
394 return (TRUE);
395}
396
397/*
398 *@@ thrKill:
399 * just like thrFree, but the thread is
400 * brutally killed, using DosKillThread.
401 *
402 * Note: DO NOT USE THIS. DosKillThread
403 * cannot clean up the C runtime. In the
404 * worst case, this hangs the system
405 * because the runtime hasn't released
406 * a semaphore or something like that.
407 */
408
409BOOL thrKill(PTHREADINFO pti)
410{
411 if (pti->tid)
412 {
413 DosResumeThread(pti->tid);
414 // this returns an error if the thread
415 // is not suspended, but otherwise the
416 // system might hang
417 DosKillThread(pti->tid);
418 }
419 return (TRUE);
420}
421
422/*
423 *@@ thrQueryID:
424 * returns thread ID or NULLHANDLE if
425 * the specified thread is not or no
426 * longer running.
427 */
428
429TID thrQueryID(const THREADINFO* pti)
430{
431 if (pti)
432 if (!(pti->fExitComplete))
433 return (pti->tid);
434
435 return (NULLHANDLE);
436}
437
438/*
439 *@@ thrQueryPriority:
440 * returns the priority of the calling thread.
441 * The low byte of the low word is a hexadecimal value
442 * representing a rank (value 0 to 31) within a priority class.
443 * Class values, found in the high byte of the low word, are
444 * as follows:
445 * -- 0x01 idle
446 * -- 0x02 regular
447 * -- 0x03 time-critical
448 * -- 0x04 server
449 *
450 * Note: This cannot retrieve the priority of a
451 * thread other than the one on which this function
452 * is running. Use prc16QueryThreadInfo for that.
453 */
454
455ULONG thrQueryPriority(VOID)
456{
457 PTIB ptib;
458 PPIB ppib;
459 if (DosGetInfoBlocks(&ptib, &ppib) == NO_ERROR)
460 if (ptib)
461 if (ptib->tib_ptib2)
462 return (ptib->tib_ptib2->tib2_ulpri);
463 return (0);
464}
465
466
Note: See TracBrowser for help on using the repository browser.