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

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

Reworked tooltip.

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