source: trunk/src/user32/auxthread.cpp@ 21595

Last change on this file since 21595 was 21595, checked in by dmik, 14 years ago

user32: Added internal helper RunOnAuxThread() API to perform function calls on a dedicated helper thread.

  • Property svn:eol-style set to native
File size: 7.9 KB
Line 
1/*
2 * Auxiliary thread API
3 *
4 * Used to execute code on a dedicated thread.
5 *
6 * Project Odin Software License can be found in LICENSE.TXT
7 *
8 */
9
10#define INCL_PM
11#define INCL_DOS
12#define INCL_DOSERRORS
13#include <os2wrap.h> // Odin32 OS/2 api wrappers
14
15#include <win32api.h>
16
17#include "auxthread.h"
18
19#include "dbglog.h"
20
21struct AUXMSG
22{
23 PAUXTHREADFN pfn;
24 PVOID arg1;
25 PVOID arg2;
26 PVOID arg3;
27 PVOID arg4;
28 BOOL del;
29};
30
31static HEV hevAux = 0;
32static HANDLE hthreadAux = 0;
33static HWND hwndAux = NULLHANDLE;
34
35static MRESULT EXPENTRY AuxWndProc(HWND hWnd, ULONG msg, MPARAM mp1, MPARAM mp2)
36{
37 if (msg == WM_USER)
38 {
39 AUXMSG *msg = (AUXMSG *)mp1;
40 MRESULT mrc = msg->pfn(msg->arg1, msg->arg2, msg->arg3, msg->arg4);
41 if (msg->del)
42 delete msg;
43 return mrc;
44 }
45
46 return FALSE;
47}
48
49static DWORD CALLBACK AuxThread(LPVOID arg)
50{
51 // take hev from the parameter instead of hevAux which will have gone right
52 // after calling StopAuxThread() so that we won't be able to post it
53 HEV hev = (HEV)arg;
54
55 dprintf(("USER32: AuxThread: Start (hev %x)", hev));
56
57 // Note: Since this thread is started by CreateThread(), hab and hmq are
58 // already created
59
60 if (WinRegisterClass(0, "OdinAuxWnd", AuxWndProc, 0, 0))
61 {
62 hwndAux = WinCreateWindow(HWND_OBJECT, "OdinAuxWnd", NULL,
63 0, 0, 0, 0, 0, NULL, HWND_BOTTOM, 0,
64 NULL, NULL);
65 if (hwndAux)
66 {
67 // report becoming ready (non-NULL hwndAux will indicate success)
68 DosPostEventSem(hev);
69
70 dprintf(("USER32: AuxThread: Enter message loop"));
71
72 QMSG qmsg;
73 while (WinGetMsg(0, &qmsg, NULLHANDLE, 0, 0))
74 WinDispatchMsg(0, &qmsg);
75
76 dprintf(("USER32: AuxThread: Exit message loop"));
77
78 WinDestroyWindow(hwndAux);
79 }
80 else
81 dprintf(("USER32: AuxThread: WinCreateWindow() failed with %x",
82 WinGetLastError(0)));
83
84 }
85 else
86 dprintf(("USER32: AuxThread: WinRegisterClass() failed with %x",
87 WinGetLastError(0)));
88
89 dprintf(("USER32: AuxThread: Stop"));
90
91 // report termination
92 DosPostEventSem(hev);
93}
94
95static BOOL DoRunOnAuxThread(PAUXTHREADFN pfn, PVOID arg1, PVOID arg2,
96 PVOID arg3, PVOID arg4, PVOID *ret)
97{
98 APIRET arc;
99
100 if (hevAux == 0)
101 {
102 HEV hev;
103 arc = DosCreateEventSem(NULL, &hev, 0, FALSE);
104 if (arc != NO_ERROR)
105 {
106 dprintf(("USER32: AuxThread: DosCreateEventSem failed with %d",
107 arc));
108 return FALSE;
109 }
110
111 // protect from two or more concurrent "first" calls
112 if (InterlockedCompareExchange((PLONG)&hevAux, hev, 0) == 0)
113 {
114 // we are the first, do the rest
115
116 // Note: Use CreateThread() instead of DosCreateThread() so that the
117 // thread gets all initialization necessary to call Win32 APIs which
118 // may be potentially called form PAUXTHREADFN
119 hthreadAux = CreateThread(NULL, 0, AuxThread, (LPVOID)hev, 0, NULL);
120 if (hthreadAux)
121 {
122 // wait for the thread to get ready
123 arc = DosWaitEventSem(hev, SEM_INDEFINITE_WAIT);
124 }
125 else
126 {
127 dprintf(("USER32: AuxThread: CreateThread() failed with %x",
128 GetLastError()));
129
130 // the second caller may be already waiting so inform it
131 DosPostEventSem(hev);
132 }
133 }
134 else
135 {
136 // we are the second, give up our attempt
137 DosCloseEventSem(hev);
138
139 // wait for the thread to get ready
140 arc = DosWaitEventSem(hevAux, SEM_INDEFINITE_WAIT);
141 }
142
143 if (hthreadAux == 0 || arc != NO_ERROR || hwndAux == NULLHANDLE)
144 {
145 StopAuxThread(); // cleanup
146 return FALSE;
147 }
148 }
149
150 if (ret)
151 {
152 AUXMSG msg = { pfn, arg1, arg2, arg3, arg4, FALSE };
153 *ret = WinSendMsg(hwndAux, WM_USER, &msg, 0);
154 return TRUE;
155 }
156
157 AUXMSG *msg = new AUXMSG;
158 msg->pfn = pfn;
159 msg->arg1 = arg1;
160 msg->arg2 = arg2;
161 msg->arg3 = arg3;
162 msg->arg4 = arg4;
163 msg->del = TRUE;
164 if (WinPostMsg(hwndAux, WM_USER, msg, 0))
165 return TRUE;
166
167 delete msg;
168 return FALSE;
169}
170
171/**
172 * Schedules execution of a given user function with given arguments on a
173 * dedicated global helper thread and immediately returns to the caller.
174 *
175 * All calls to RunOnAuxThread() schedule execution of the user function on the
176 * same thread which, once started, gets terminated automatically at process
177 * termination.
178 *
179 * Note that since this thread is global (shared by all requests as described
180 * above), the user function must return as soon as possible as other threads
181 * may be trying to schedule a function call on this thread through
182 * RunOnAuxThread() (or even waiting for the result of the function execution
183 * through RunOnAuxThreadAndWait()) as well.
184 *
185 * The main purpose of this API is to make sure that a series of function calls
186 * is guaranteedly performed on the same thread so that subsequent calls share
187 * the context established by the fisrt call. This, in particular includes
188 * situations when it is necessary to make sure that an object window is
189 * destroyed on the same thead where it was created no matter what thread calls
190 * the create or destroy code. This also implies that this dedicated thread runs
191 * the message loop and therefore may respond to posted messages.
192 *
193 * @author dmik (3/23/2011)
194 *
195 * @param pfn User function to execute.
196 * @param arg1 Function argument 1.
197 * @param arg2 Function argument 2.
198 * @param arg3 Function argument 3.
199 * @param arg4 Function argument 4.
200 *
201 * @return TRUE on success, FALSE otherwise.
202 */
203BOOL WIN32API RunOnAuxThread(PAUXTHREADFN pfn, PVOID arg1, PVOID arg2,
204 PVOID arg3, PVOID arg4)
205{
206 return DoRunOnAuxThread(pfn, arg1, arg2, arg3, arg4, NULL);
207}
208
209/**
210 * Schedules execution of a given user function with given arguments on a
211 * dedicated global helper thread and waits for it to return a value.
212 *
213 * See RunOnAuxThread() for more details on the dedicated thread and purpose of
214 * this API.
215 *
216 * @author dmik (3/23/2011)
217 *
218 * @param pfn User function to execute.
219 * @param arg1 Function argument 1.
220 * @param arg2 Function argument 2.
221 * @param arg3 Function argument 3.
222 * @param arg4 Function argument 4.
223 * @param ret Where to store the user function's return value. May be NULL if
224 * the caller is not interested in the return value.
225 *
226 * @return TRUE on success, FALSE otherwise.
227 */
228BOOL WIN32API RunOnAuxThreadAndWait(PAUXTHREADFN pfn, PVOID arg1, PVOID arg2,
229 PVOID arg3, PVOID arg4, PPVOID ret)
230{
231 PVOID ret2;
232 BOOL brc = DoRunOnAuxThread(pfn, arg1, arg2, arg3, arg4, &ret2);
233 if (ret)
234 *ret = ret2;
235 return brc;
236}
237
238//******************************************************************************
239//
240//******************************************************************************
241VOID StopAuxThread()
242{
243 if (hevAux != 0)
244 {
245 HEV hev = hevAux;
246
247 // protect from two or more concurrent "first" calls
248 if (InterlockedCompareExchange((PLONG)&hevAux, 0, hev) == hev)
249 {
250 // we are the first, do the rest
251
252 // ask to exit the message loop
253 if (hwndAux)
254 {
255 ULONG dummy;
256 DosResetEventSem(hev, &dummy);
257
258 WinPostMsg(hwndAux, WM_QUIT, 0, 0);
259 hwndAux = NULLHANDLE;
260 }
261
262 if (hthreadAux != 0)
263 {
264 // wait for the normal thread termination
265 DosWaitEventSem(hev, 3000);
266 CloseHandle(hthreadAux);
267 hthreadAux = 0;
268 }
269
270 DosCloseEventSem(hev);
271 }
272 }
273}
Note: See TracBrowser for help on using the repository browser.