/* * Auxiliary thread API * * Used to execute code on a dedicated thread. * * Project Odin Software License can be found in LICENSE.TXT * */ #define INCL_PM #define INCL_DOS #define INCL_DOSERRORS #include // Odin32 OS/2 api wrappers #include #include "auxthread.h" #include "dbglog.h" struct AUXMSG { PAUXTHREADFN pfn; PVOID arg1; PVOID arg2; PVOID arg3; PVOID arg4; BOOL del; }; static HEV hevAux = 0; static HANDLE hthreadAux = 0; static HWND hwndAux = NULLHANDLE; static MRESULT EXPENTRY AuxWndProc(HWND hWnd, ULONG msg, MPARAM mp1, MPARAM mp2) { if (msg == WM_USER) { AUXMSG *msg = (AUXMSG *)mp1; dprintf(("USER32: AuxWndProc: pfn %p, arg1 %x, arg2 %x, arg3 %x, " "arg4 %x, del %d", msg->pfn, msg->arg1, msg->arg2, msg->arg3, msg->arg4, msg->del)); MRESULT mrc = msg->pfn(msg->arg1, msg->arg2, msg->arg3, msg->arg4); if (msg->del) delete msg; return mrc; } return FALSE; } static DWORD CALLBACK AuxThread(LPVOID arg) { // take hev from the parameter instead of hevAux which will have gone right // after calling StopAuxThread() so that we won't be able to post it HEV hev = (HEV)arg; dprintf(("USER32: AuxThread: Start (hev %x)", hev)); // Note: Since this thread is started by CreateThread(), hab and hmq are // already created if (WinRegisterClass(0, "OdinAuxWnd", AuxWndProc, 0, 0)) { hwndAux = WinCreateWindow(HWND_OBJECT, "OdinAuxWnd", NULL, 0, 0, 0, 0, 0, NULL, HWND_BOTTOM, 0, NULL, NULL); if (hwndAux) { // report becoming ready (non-NULL hwndAux will indicate success) DosPostEventSem(hev); dprintf(("USER32: AuxThread: Enter message loop")); QMSG qmsg; while (WinGetMsg(0, &qmsg, NULLHANDLE, 0, 0)) WinDispatchMsg(0, &qmsg); dprintf(("USER32: AuxThread: Exit message loop")); WinDestroyWindow(hwndAux); } else dprintf(("USER32: AuxThread: WinCreateWindow() failed with %x", WinGetLastError(0))); } else dprintf(("USER32: AuxThread: WinRegisterClass() failed with %x", WinGetLastError(0))); dprintf(("USER32: AuxThread: Stop")); // report termination DosPostEventSem(hev); return 0; } static BOOL DoRunOnAuxThread(PAUXTHREADFN pfn, PVOID arg1, PVOID arg2, PVOID arg3, PVOID arg4, PVOID *ret) { dprintf(("USER32: DoRunOnAuxThread: pfn %p, arg1 %x, arg2 %x, arg3 %x, " "arg4 %x, ret %x", pfn, arg1, arg2, arg3, arg4, ret)); APIRET arc; if (hevAux == 0) { HEV hev; arc = DosCreateEventSem(NULL, &hev, 0, FALSE); if (arc != NO_ERROR) { dprintf(("USER32: DoRunOnAuxThread: DosCreateEventSem failed with %d", arc)); return FALSE; } // protect from two or more concurrent "first" calls if (InterlockedCompareExchange((PLONG)&hevAux, hev, 0) == 0) { // we are the first, do the rest // Note: Use CreateThread() instead of DosCreateThread() so that the // thread gets all initialization necessary to call Win32 APIs which // may be potentially called form PAUXTHREADFN hthreadAux = CreateThread(NULL, 0, AuxThread, (LPVOID)hev, 0, NULL); if (hthreadAux) { // wait for the thread to get ready arc = DosWaitEventSem(hev, SEM_INDEFINITE_WAIT); } else { dprintf(("USER32: DoRunOnAuxThread: CreateThread() failed with %x", GetLastError())); // the second caller may be already waiting so inform it DosPostEventSem(hev); } } else { // we are the second, give up our attempt DosCloseEventSem(hev); // wait for the thread to get ready arc = DosWaitEventSem(hevAux, SEM_INDEFINITE_WAIT); } if (hthreadAux == 0 || arc != NO_ERROR || hwndAux == NULLHANDLE) { StopAuxThread(); // cleanup return FALSE; } } if (ret) { AUXMSG msg = { pfn, arg1, arg2, arg3, arg4, FALSE }; *ret = WinSendMsg(hwndAux, WM_USER, &msg, 0); return TRUE; } AUXMSG *msg = new AUXMSG; msg->pfn = pfn; msg->arg1 = arg1; msg->arg2 = arg2; msg->arg3 = arg3; msg->arg4 = arg4; msg->del = TRUE; if (WinPostMsg(hwndAux, WM_USER, msg, 0)) return TRUE; delete msg; return FALSE; } /** * Schedules execution of a given user function with given arguments on a * dedicated global helper thread and immediately returns to the caller. * * All calls to RunOnAuxThread() schedule execution of the user function on the * same thread which, once started, gets terminated automatically at process * termination. * * Note that since this thread is global (shared by all requests as described * above), the user function must return as soon as possible as other threads * may be trying to schedule a function call on this thread through * RunOnAuxThread() (or even waiting for the result of the function execution * through RunOnAuxThreadAndWait()) as well. * * The main purpose of this API is to make sure that a series of function calls * is guaranteedly performed on the same thread so that subsequent calls share * the context established by the fisrt call. This, in particular includes * situations when it is necessary to make sure that an object window is * destroyed on the same thead where it was created no matter what thread calls * the create or destroy code. This also implies that this dedicated thread runs * the message loop and therefore may respond to posted messages. * * @author dmik (3/23/2011) * * @param pfn User function to execute. * @param arg1 Function argument 1. * @param arg2 Function argument 2. * @param arg3 Function argument 3. * @param arg4 Function argument 4. * * @return TRUE on success, FALSE otherwise. */ BOOL WIN32API RunOnAuxThread(PAUXTHREADFN pfn, PVOID arg1, PVOID arg2, PVOID arg3, PVOID arg4) { return DoRunOnAuxThread(pfn, arg1, arg2, arg3, arg4, NULL); } /** * Schedules execution of a given user function with given arguments on a * dedicated global helper thread and waits for it to return a value. * * See RunOnAuxThread() for more details on the dedicated thread and purpose of * this API. * * @author dmik (3/23/2011) * * @param pfn User function to execute. * @param arg1 Function argument 1. * @param arg2 Function argument 2. * @param arg3 Function argument 3. * @param arg4 Function argument 4. * @param ret Where to store the user function's return value. May be NULL if * the caller is not interested in the return value. * * @return TRUE on success, FALSE otherwise. */ BOOL WIN32API RunOnAuxThreadAndWait(PAUXTHREADFN pfn, PVOID arg1, PVOID arg2, PVOID arg3, PVOID arg4, PPVOID ret) { PVOID ret2; BOOL brc = DoRunOnAuxThread(pfn, arg1, arg2, arg3, arg4, &ret2); if (ret) *ret = ret2; return brc; } //****************************************************************************** // //****************************************************************************** VOID StopAuxThread() { if (hevAux != 0) { HEV hev = hevAux; // protect from two or more concurrent "first" calls if (InterlockedCompareExchange((PLONG)&hevAux, 0, hev) == hev) { // we are the first, do the rest // ask to exit the message loop if (hwndAux) { ULONG dummy; DosResetEventSem(hev, &dummy); WinPostMsg(hwndAux, WM_QUIT, 0, 0); hwndAux = NULLHANDLE; } if (hthreadAux != 0) { // wait for the normal thread termination DosWaitEventSem(hev, 3000); CloseHandle(hthreadAux); hthreadAux = 0; } DosCloseEventSem(hev); } } }