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

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

Major updates; timers, LVM, miscellaneous.

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