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

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

Tons of updates.

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