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

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

Final changes for 0.9.7, i hope...

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 14.5 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 THREADINFO ti = {0};
299 thrCreate(&ti,
300 pfn,
301 NULL,
302 THRF_PMMSGQUEUE,
303 ulData);
304 ti.hwndNotify = hwndNotify;
305
306 while (WinGetMsg(hab,
307 &qmsg, 0, 0, 0))
308 {
309 // current message for our object window?
310 if ( (qmsg.hwnd == hwndNotify)
311 && (qmsg.msg == WM_USER)
312 )
313 {
314 fQuit = TRUE;
315 ulrc = (ULONG)qmsg.mp1;
316 }
317
318 WinDispatchMsg(hab, &qmsg);
319 if (fQuit)
320 break;
321 }
322
323 // we must wait for the thread to finish, or
324 // otherwise THREADINFO is deleted from the stack
325 // before the thread exits... will crash!
326 thrWait(&ti);
327
328 WinDestroyWindow(hwndNotify);
329 return (ulrc);
330}
331
332/*
333 *@@ thrClose:
334 * this functions sets the "fExit" flag in
335 * THREADINFO to TRUE.
336 *
337 * The thread should monitor this flag
338 * periodically and then terminate itself.
339 */
340
341BOOL thrClose(PTHREADINFO pti)
342{
343 if (pti)
344 {
345 pti->fExit = TRUE;
346 return (TRUE);
347 }
348 return (FALSE);
349}
350
351/*
352 *@@ thrWait:
353 * this function waits for a thread to end by calling
354 * DosWaitThread. Note that this blocks the calling
355 * thread, so only use this function when you're sure
356 * the thread will actually terminate.
357 *
358 * Returns FALSE if the thread wasn't running or TRUE
359 * if it was and has terminated.
360 *
361 *@@changed V0.9.0 [umoeller]: now checking for whether pti->tid is still != 0
362 */
363
364BOOL thrWait(PTHREADINFO pti)
365{
366 if (pti)
367 if (pti->tid)
368 {
369 DosWaitThread(&(pti->tid), DCWW_WAIT);
370 pti->tid = NULLHANDLE;
371 return (TRUE);
372 }
373 return (FALSE);
374}
375
376/*
377 *@@ thrFree:
378 * this is a combination of thrClose and
379 * thrWait, i.e. this func does not return
380 * until the specified thread has ended.
381 */
382
383BOOL thrFree(PTHREADINFO pti)
384{
385 if (pti->tid)
386 {
387 thrClose(pti);
388 thrWait(pti);
389 }
390 return (TRUE);
391}
392
393/*
394 *@@ thrKill:
395 * just like thrFree, but the thread is
396 * brutally killed, using DosKillThread.
397 *
398 * Note: DO NOT USE THIS. DosKillThread
399 * cannot clean up the C runtime. In the
400 * worst case, this hangs the system
401 * because the runtime hasn't released
402 * a semaphore or something like that.
403 */
404
405BOOL thrKill(PTHREADINFO pti)
406{
407 if (pti->tid)
408 {
409 DosResumeThread(pti->tid);
410 // this returns an error if the thread
411 // is not suspended, but otherwise the
412 // system might hang
413 DosKillThread(pti->tid);
414 }
415 return (TRUE);
416}
417
418/*
419 *@@ thrQueryID:
420 * returns thread ID or NULLHANDLE if
421 * the specified thread is not or no
422 * longer running.
423 */
424
425TID thrQueryID(const THREADINFO* pti)
426{
427 if (pti)
428 if (!(pti->fExitComplete))
429 return (pti->tid);
430
431 return (NULLHANDLE);
432}
433
434/*
435 *@@ thrQueryPriority:
436 * returns the priority of the calling thread.
437 * The low byte of the low word is a hexadecimal value
438 * representing a rank (value 0 to 31) within a priority class.
439 * Class values, found in the high byte of the low word, are
440 * as follows:
441 * -- 0x01 idle
442 * -- 0x02 regular
443 * -- 0x03 time-critical
444 * -- 0x04 server
445 *
446 * Note: This cannot retrieve the priority of a
447 * thread other than the one on which this function
448 * is running. Use prc16QueryThreadInfo for that.
449 */
450
451ULONG thrQueryPriority(VOID)
452{
453 PTIB ptib;
454 PPIB ppib;
455 if (DosGetInfoBlocks(&ptib, &ppib) == NO_ERROR)
456 if (ptib)
457 if (ptib->tib_ptib2)
458 return (ptib->tib_ptib2->tib2_ulpri);
459 return (0);
460}
461
462
Note: See TracBrowser for help on using the repository browser.