From 3cde13952cfbf4f907caae3cada8d1329cfa076c Mon Sep 17 00:00:00 2001 From: "wtc%netscape.com" Date: Tue, 25 Jan 2000 14:48:51 +0000 Subject: [PATCH] Bugzilla bug #17101: added plevent.h (same as mozilla/nsprpub/lib/ds/plevent.h, revision 3.12) and plevent.c (same as mozilla/nsprpub/lib/ds/plevent.c, revision 3.36) to mozilla/xpcom/threads. These two new files have not been added to the makefiles or project file, so they are not picked up by the build. git-svn-id: svn://10.0.0.236/trunk@58589 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/xpcom/threads/plevent.c | 1114 +++++++++++++++++++++++++++++++ mozilla/xpcom/threads/plevent.h | 497 ++++++++++++++ 2 files changed, 1611 insertions(+) create mode 100644 mozilla/xpcom/threads/plevent.c create mode 100644 mozilla/xpcom/threads/plevent.h diff --git a/mozilla/xpcom/threads/plevent.c b/mozilla/xpcom/threads/plevent.c new file mode 100644 index 00000000000..237bffef56c --- /dev/null +++ b/mozilla/xpcom/threads/plevent.c @@ -0,0 +1,1114 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* + * The contents of this file are subject to the Netscape Public License + * Version 1.1 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + +#if defined(_WIN32) || defined(WIN16) +#include +#endif + +#if defined(XP_OS2) +#define INCL_WIN +#include +#define DefWindowProc WinDefWindowProc +typedef MPARAM WPARAM,LPARAM; +#endif /* XP_OS2 */ + +#include "nspr.h" +#include "plevent.h" + +#if !defined(WIN32) +#include +#include +#if !defined(XP_OS2) +#include +#endif /* !XP_OS2 */ +#endif /* !Win32 */ + +#if defined(XP_UNIX) +/* for fcntl */ +#include +#include +#endif + +#if defined(XP_BEOS) +#include +#endif + +#if defined(XP_MAC) +#include +#include "pprthred.h" +#else +#include "private/pprthred.h" +#endif /* XP_MAC */ + +#if defined(VMS) +/* +** On OpenVMS, XtAppAddInput doesn't want a regular fd, instead it +** wants an event flag. So, we don't create and use a pipe for +** notification of when an event queue has something ready, instead +** we use an event flag. Shouldn't be a problem if we only have +** a few event queues. +*/ +#include +#include +#include +#endif /* VMS */ + + +static PRLogModuleInfo *event_lm = NULL; + +/******************************************************************************* + * Private Stuff + ******************************************************************************/ + +/* +** EventQueueType -- Defines notification type for an event queue +** +*/ +typedef enum +{ + EventQueueIsNative = 1, + EventQueueIsMonitored = 2 +} EventQueueType; + + +struct PLEventQueue { + char* name; + PRCList queue; + PRMonitor* monitor; + PRThread* handlerThread; + EventQueueType type; + PRBool processingEvents; +#if defined(VMS) + int efn; + int notifyCount; +#elif defined(XP_UNIX) + PRInt32 eventPipe[2]; + int notifyCount; +#elif defined(_WIN32) || defined(WIN16) + HWND eventReceiverWindow; + PRBool removeMsg; +#elif defined(XP_OS2) + HWND eventReceiverWindow; +#elif defined(XP_BEOS) + port_id eventport; +#endif +}; + +#define PR_EVENT_PTR(_qp) \ + ((PLEvent*) ((char*) (_qp) - offsetof(PLEvent, link))) + +static PRStatus _pl_SetupNativeNotifier(PLEventQueue* self); +static void _pl_CleanupNativeNotifier(PLEventQueue* self); +static PRStatus _pl_NativeNotify(PLEventQueue* self); +static PRStatus _pl_AcknowledgeNativeNotify(PLEventQueue* self); +static void _md_CreateEventQueue( PLEventQueue *eventQueue ); +static PRInt32 _pl_GetEventCount(PLEventQueue* self); + + +#if defined(_WIN32) || defined(WIN16) || defined(XP_OS2) +#if defined(XP_OS2) +ULONG _pr_PostEventMsgId; +static char *_pr_eventWindowClass = "NSPR:EventWindow"; +#else +UINT _pr_PostEventMsgId; +static char *_pr_eventWindowClass = "NSPR:EventWindow"; +#endif /* OS2 */ +#endif /* Win32, Win16, OS2 */ + +/******************************************************************************* + * Event Queue Operations + ******************************************************************************/ + +/* +** _pl_CreateEventQueue() -- Create the event queue +** +** +*/ +static PLEventQueue * + _pl_CreateEventQueue( + char *name, + PRThread *handlerThread, + EventQueueType qtype +) +{ + PRStatus err; + PLEventQueue* self = NULL; + PRMonitor* mon = NULL; + + if (event_lm == NULL) + event_lm = PR_NewLogModule("event"); + + self = PR_NEWZAP(PLEventQueue); + if (self == NULL) return NULL; + + mon = PR_NewNamedMonitor(name); + if (mon == NULL) goto error; + + self->name = name; + self->monitor = mon; + self->handlerThread = handlerThread; + self->processingEvents = PR_FALSE; + self->type = qtype; +#if defined(_WIN32) || defined(WIN16) + self->removeMsg = PR_TRUE; +#endif + PR_INIT_CLIST(&self->queue); + if ( qtype == EventQueueIsNative ) + { + err = _pl_SetupNativeNotifier(self); + if (err) goto error; + } + _md_CreateEventQueue( self ); + return self; + + error: + if (mon != NULL) + PR_DestroyMonitor(mon); + PR_DELETE(self); + return NULL; +} /* end _pl_CreateEventQueue() */ + + +PR_IMPLEMENT(PLEventQueue*) +PL_CreateEventQueue(char* name, PRThread* handlerThread) +{ + return( _pl_CreateEventQueue( name, handlerThread, EventQueueIsNative )); +} + +PR_EXTERN(PLEventQueue *) + PL_CreateNativeEventQueue( + char *name, + PRThread *handlerThread + ) +{ + return( _pl_CreateEventQueue( name, handlerThread, EventQueueIsNative )); +} /* --- end PL_CreateNativeEventQueue() --- */ + +PR_EXTERN(PLEventQueue *) + PL_CreateMonitoredEventQueue( + char *name, + PRThread *handlerThread + ) +{ + return( _pl_CreateEventQueue( name, handlerThread, EventQueueIsMonitored )); +} /* --- end PL_CreateMonitoredEventQueue() --- */ + + + +PR_IMPLEMENT(PRMonitor*) +PL_GetEventQueueMonitor(PLEventQueue* self) +{ + return self->monitor; +} + +static void PR_CALLBACK +_pl_destroyEvent(PLEvent* event, void* data, PLEventQueue* queue) +{ +#ifdef XP_MAC +#pragma unused (data, queue) +#endif + PL_DequeueEvent(event, queue); + PL_DestroyEvent(event); +} + +PR_IMPLEMENT(void) +PL_DestroyEventQueue(PLEventQueue* self) +{ + PR_EnterMonitor(self->monitor); + + /* destroy undelivered events */ + PL_MapEvents(self, _pl_destroyEvent, NULL); + + if ( self->type == EventQueueIsNative ) + _pl_CleanupNativeNotifier(self); + + /* destroying the monitor also destroys the name */ + PR_ExitMonitor(self->monitor); + PR_DestroyMonitor(self->monitor); + PR_DELETE(self); + +} + +PR_IMPLEMENT(PRStatus) +PL_PostEvent(PLEventQueue* self, PLEvent* event) +{ + PRStatus err = PR_SUCCESS; + PRMonitor* mon; + + if (self == NULL) + return PR_FAILURE; + + mon = self->monitor; + PR_EnterMonitor(mon); + + /* insert event into thread's event queue: */ + if (event != NULL) { + PR_APPEND_LINK(&event->link, &self->queue); + } + + /* notify even if event is NULL */ + if ( self->type == EventQueueIsNative ) + err = _pl_NativeNotify(self); + + /* + * This may fall on deaf ears if we're really notifying the native + * thread, and no one has called PL_WaitForEvent (or PL_EventLoop): + */ + err = PR_Notify(mon); + PR_ExitMonitor(mon); + return err; +} + +PR_IMPLEMENT(void*) +PL_PostSynchronousEvent(PLEventQueue* self, PLEvent* event) +{ + void* result; + + if (self == NULL) + return NULL; + + PR_ASSERT(event != NULL); + PR_CEnterMonitor(event); + + if (PR_CurrentThread() == self->handlerThread) { + /* Handle the case where the thread requesting the event handling + is also the thread that's supposed to do the handling. */ + result = event->handler(event); + } + else { + int i, entryCount = PR_GetMonitorEntryCount(self->monitor); + + event->synchronousResult = (void*)PR_TRUE; + PL_PostEvent(self, event); + /* We need to temporarily give up our event queue monitor if + we're holding it, otherwise, the thread we're going to wait + for notification from won't be able to enter it to process + the event. */ + if (entryCount) { + for (i = 0; i < entryCount; i++) + PR_ExitMonitor(self->monitor); + } + PR_CWait(event, PR_INTERVAL_NO_TIMEOUT); /* wait for event to be handled or destroyed */ + if (entryCount) { + for (i = 0; i < entryCount; i++) + PR_EnterMonitor(self->monitor); + } + result = event->synchronousResult; + event->synchronousResult = NULL; + } + + PR_CExitMonitor(event); + + /* For synchronous events, they're destroyed here on the caller's + thread before the result is returned. See PL_HandleEvent. */ + PL_DestroyEvent(event); + + return result; +} + +PR_IMPLEMENT(PLEvent*) +PL_GetEvent(PLEventQueue* self) +{ + PLEvent* event = NULL; + PRStatus err = PR_SUCCESS; + + if (self == NULL) + return NULL; + + PR_EnterMonitor(self->monitor); + + if (!PR_CLIST_IS_EMPTY(&self->queue)) { + if ( self->type == EventQueueIsNative ) + err = _pl_AcknowledgeNativeNotify(self); + + if (err) goto done; + + /* then grab the event and return it: */ + event = PR_EVENT_PTR(self->queue.next); + PR_REMOVE_AND_INIT_LINK(&event->link); + } + + done: + PR_ExitMonitor(self->monitor); + return event; +} + +PR_IMPLEMENT(PRBool) +PL_EventAvailable(PLEventQueue* self) +{ + PRBool result = PR_FALSE; + + if (self == NULL) + return PR_FALSE; + + PR_EnterMonitor(self->monitor); + + if (!PR_CLIST_IS_EMPTY(&self->queue)) + result = PR_TRUE; + + PR_ExitMonitor(self->monitor); + return result; +} + +PR_IMPLEMENT(void) +PL_MapEvents(PLEventQueue* self, PLEventFunProc fun, void* data) +{ + PRCList* qp; + + if (self == NULL) + return; + + PR_EnterMonitor(self->monitor); + qp = self->queue.next; + while (qp != &self->queue) { + PLEvent* event = PR_EVENT_PTR(qp); + qp = qp->next; + (*fun)(event, data, self); + } + PR_ExitMonitor(self->monitor); +} + +static void PR_CALLBACK +_pl_DestroyEventForOwner(PLEvent* event, void* owner, PLEventQueue* queue) +{ + PR_ASSERT(PR_GetMonitorEntryCount(queue->monitor) > 0); + if (event->owner == owner) { + PR_LOG(event_lm, PR_LOG_DEBUG, + ("$$$ \tdestroying event %0x for owner %0x", event, owner)); + PL_DequeueEvent(event, queue); + if (event->synchronousResult == (void*)PR_TRUE) { + PR_CEnterMonitor(event); + event->synchronousResult = NULL; + PR_CNotify(event); + PR_CExitMonitor(event); + } + else { + PL_DestroyEvent(event); + } + } + else { + PR_LOG(event_lm, PR_LOG_DEBUG, + ("$$$ \tskipping event %0x for owner %0x", event, owner)); + } +} + +PR_IMPLEMENT(void) +PL_RevokeEvents(PLEventQueue* self, void* owner) +{ + if (self == NULL) + return; + + PR_LOG(event_lm, PR_LOG_DEBUG, + ("$$$ revoking events for owner %0x", owner)); + + /* + ** First we enter the monitor so that no one else can post any events + ** to the queue: + */ + PR_EnterMonitor(self->monitor); + PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ owner %0x, entered monitor", owner)); + + /* + ** Discard any pending events for this owner: + */ + PL_MapEvents(self, _pl_DestroyEventForOwner, owner); + +#ifdef DEBUG + { + PRCList* qp = self->queue.next; + while (qp != &self->queue) { + PLEvent* event = PR_EVENT_PTR(qp); + qp = qp->next; + PR_ASSERT(event->owner != owner); + } + } +#endif /* DEBUG */ + + PR_ExitMonitor(self->monitor); + + PR_LOG(event_lm, PR_LOG_DEBUG, + ("$$$ revoking events for owner %0x", owner)); +} + +static PRInt32 +_pl_GetEventCount(PLEventQueue* self) +{ + PRCList* node; + PRInt32 count = 0; + + PR_EnterMonitor(self->monitor); + node = PR_LIST_HEAD(&self->queue); + while (node != &self->queue) { + count++; + node = PR_NEXT_LINK(node); + } + PR_ExitMonitor(self->monitor); + + return count; +} + +PR_IMPLEMENT(void) +PL_ProcessPendingEvents(PLEventQueue* self) +{ + PRInt32 count; + + if (self == NULL) + return; + + if (PR_FALSE != self->processingEvents) return; + + self->processingEvents = PR_TRUE; + + /* Only process the events that are already in the queue, and + * not any new events that get added. Do this by counting the + * number of events currently in the queue + */ + count = _pl_GetEventCount(self); + while (count-- > 0) { + PLEvent* event = PL_GetEvent(self); + if (event == NULL) break; + + PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ processing event")); + PL_HandleEvent(event); + PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ done processing event")); + } + self->processingEvents = PR_FALSE; +} + +/******************************************************************************* + * Event Operations + ******************************************************************************/ + +PR_IMPLEMENT(void) +PL_InitEvent(PLEvent* self, void* owner, + PLHandleEventProc handler, + PLDestroyEventProc destructor) +{ + PR_INIT_CLIST(&self->link); + self->handler = handler; + self->destructor = destructor; + self->owner = owner; + self->synchronousResult = NULL; +} + +PR_IMPLEMENT(void*) +PL_GetEventOwner(PLEvent* self) +{ + return self->owner; +} + +PR_IMPLEMENT(void) +PL_HandleEvent(PLEvent* self) +{ + void* result; + + if (self == NULL) + return; + + /* This event better not be on an event queue anymore. */ + PR_ASSERT(PR_CLIST_IS_EMPTY(&self->link)); + + result = (*self->handler)(self); + if (NULL != self->synchronousResult) { + PR_CEnterMonitor(self); + self->synchronousResult = result; + PR_CNotify(self); /* wake up the guy waiting for the result */ + PR_CExitMonitor(self); + } + else { + /* For asynchronous events, they're destroyed by the event-handler + thread. See PR_PostSynchronousEvent. */ + PL_DestroyEvent(self); + } +} + +PR_IMPLEMENT(void) +PL_DestroyEvent(PLEvent* self) +{ + if (self == NULL) + return; + + /* This event better not be on an event queue anymore. */ + PR_ASSERT(PR_CLIST_IS_EMPTY(&self->link)); + + (*self->destructor)(self); +} + +PR_IMPLEMENT(void) +PL_DequeueEvent(PLEvent* self, PLEventQueue* queue) +{ +#ifdef XP_MAC +#pragma unused (queue) +#endif + if (self == NULL) + return; + + /* Only the owner is allowed to dequeue events because once the + client has put it in the queue, they have no idea whether it's + been processed and destroyed or not. */ +/* PR_ASSERT(queue->handlerThread == PR_CurrentThread());*/ + + PR_EnterMonitor(queue->monitor); + + PR_ASSERT(!PR_CLIST_IS_EMPTY(&self->link)); + PR_REMOVE_AND_INIT_LINK(&self->link); + + PR_ExitMonitor(queue->monitor); +} + +/******************************************************************************* + * Pure Event Queues + * + * For when you're only processing PLEvents and there is no native + * select, thread messages, or AppleEvents. + ******************************************************************************/ + +PR_IMPLEMENT(PLEvent*) +PL_WaitForEvent(PLEventQueue* self) +{ + PLEvent* event; + PRMonitor* mon; + + if (self == NULL) + return NULL; + + mon = self->monitor; + PR_EnterMonitor(mon); + + while ((event = PL_GetEvent(self)) == NULL) { + PRStatus err; + PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ waiting for event")); + err = PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT); + if ((err == PR_FAILURE) + && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) break; + } + + PR_ExitMonitor(mon); + return event; +} + +PR_IMPLEMENT(void) +PL_EventLoop(PLEventQueue* self) +{ + if (self == NULL) + return; + + while (PR_TRUE) { + PLEvent* event = PL_WaitForEvent(self); + if (event == NULL) { + /* This can only happen if the current thread is interrupted */ + return; + } + + PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ processing event")); + PL_HandleEvent(event); + PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ done processing event")); + } +} + +/******************************************************************************* + * Native Event Queues + * + * For when you need to call select, or WaitNextEvent, and yet also want + * to handle PLEvents. + ******************************************************************************/ + +static PRStatus +_pl_SetupNativeNotifier(PLEventQueue* self) +{ +#if defined(XP_MAC) +#pragma unused (self) +#endif + +#if defined(VMS) + { + unsigned int status; + status = LIB$GET_EF(&self->efn); + if (!$VMS_STATUS_SUCCESS(status)) + return PR_FAILURE; + PR_LOG(event_lm, PR_LOG_DEBUG, + ("$$$ Allocated event flag %d", self->efn)); + return PR_SUCCESS; + } +#elif defined(XP_UNIX) + int err; + int flags; + + err = pipe(self->eventPipe); + if (err != 0) { + return PR_FAILURE; + } + + /* make the pipe nonblocking */ + flags = fcntl(self->eventPipe[0], F_GETFL, 0); + if (flags == -1) { + goto failed; + } + err = fcntl(self->eventPipe[0], F_SETFL, flags | O_NONBLOCK); + if (err == -1) { + goto failed; + } + flags = fcntl(self->eventPipe[1], F_GETFL, 0); + if (flags == -1) { + goto failed; + } + err = fcntl(self->eventPipe[1], F_SETFL, flags | O_NONBLOCK); + if (err == -1) { + goto failed; + } + return PR_SUCCESS; + +failed: + close(self->eventPipe[0]); + close(self->eventPipe[1]); + return PR_FAILURE; +#elif defined(XP_BEOS) + /* hook up to the nsToolkit queue, however the appshell + * isn't necessairly started, so we might have to create + * the queue ourselves + */ + char portname[64]; + char semname[64]; + sprintf(portname, "event%lx", self->handlerThread); + sprintf(semname, "sync%lx", self->handlerThread); + + if((self->eventport = find_port(portname)) < 0) + { + /* create port + */ + self->eventport = create_port(100, portname); + + /* We don't use the sem, but it has to be there + */ + create_sem(0, semname); + } + + return PR_SUCCESS; +#else + return PR_SUCCESS; +#endif +} + +static void +_pl_CleanupNativeNotifier(PLEventQueue* self) +{ +#if defined(XP_MAC) +#pragma unused (self) +#endif + +#if defined(VMS) + { + unsigned int status; + PR_LOG(event_lm, PR_LOG_DEBUG, + ("$$$ Freeing event flag %d", self->efn)); + status = LIB$FREE_EF(&self->efn); + } +#elif defined(XP_UNIX) + close(self->eventPipe[0]); + close(self->eventPipe[1]); +#elif defined(_WIN32) || defined(WIN16) + DestroyWindow(self->eventReceiverWindow); +#endif +} + +#if defined(_WIN32) || defined(WIN16) +static PRStatus +_pl_NativeNotify(PLEventQueue* self) +{ + PostMessage( self->eventReceiverWindow, _pr_PostEventMsgId, + (WPARAM)0, (LPARAM)self); + return PR_SUCCESS; +}/* --- end _pl_NativeNotify() --- */ +#endif + +#if defined(XP_OS2) +static PRStatus +_pl_NativeNotify(PLEventQueue* self) +{ + BOOL rc = WinPostMsg( self->eventReceiverWindow, _pr_PostEventMsgId, + 0, MPFROMP(self)); + return (rc == TRUE) ? PR_SUCCESS : PR_FAILURE; +}/* --- end _pl_NativeNotify() --- */ +#endif /* XP_OS2 */ + +#if defined(VMS) +/* Just set the event flag */ +static PRStatus +_pl_NativeNotify(PLEventQueue* self) +{ + unsigned int status; + PR_LOG(event_lm, PR_LOG_DEBUG, + ("_pl_NativeNotify: self=%p notifyCount=%d efn=%d", + self, self->notifyCount, self->efn)); + self->notifyCount++; + status = SYS$SETEF(self->efn); + if ($VMS_STATUS_SUCCESS(status)) + return PR_SUCCESS; + else + return PR_FAILURE; +}/* --- end _pl_NativeNotify() --- */ +#elif defined(XP_UNIX) +static PRStatus +_pl_NativeNotify(PLEventQueue* self) +{ +#define NOTIFY_TOKEN 0xFA + PRInt32 count; + unsigned char buf[] = { NOTIFY_TOKEN }; + + PR_LOG(event_lm, PR_LOG_DEBUG, + ("_pl_NativeNotify: self=%p notifyCount=%d", + self, self->notifyCount)); + count = write(self->eventPipe[1], buf, 1); + if (count == 1) { + self->notifyCount++; + return PR_SUCCESS; + } else if ((count == -1) && ((errno == EAGAIN) || (errno == EWOULDBLOCK))) { + return PR_SUCCESS; + } else + return PR_FAILURE; +}/* --- end _pl_NativeNotify() --- */ +#endif /* XP_UNIX */ + +#if defined(XP_BEOS) +struct ThreadInterfaceData +{ + void *data; + int32 sync; +}; + +static PRStatus +_pl_NativeNotify(PLEventQueue* self) +{ + struct ThreadInterfaceData id; + id.data = self; + id.sync = false; + write_port(self->eventport, 'natv', &id, sizeof(id)); + + return PR_SUCCESS; /* Is this correct? */ +} +#endif /* XP_BEOS */ + +#if defined(XP_MAC) +static PRStatus +_pl_NativeNotify(PLEventQueue* self) +{ +#pragma unused (self) + return PR_SUCCESS; /* XXX can fail? */ +} +#endif /* XP_MAC */ + +static PRStatus +_pl_AcknowledgeNativeNotify(PLEventQueue* self) +{ +#if defined(_WIN32) || defined(WIN16) + MSG aMsg; + /* + * only remove msg when we've been called directly by + * PL_ProcessPendingEvents, not when we've been called by + * the window proc because the window proc will remove the + * msg for us. + */ + if (self->removeMsg) { + PR_LOG(event_lm, PR_LOG_DEBUG, + ("_pl_AcknowledgeNativeNotify: self=%p", self)); + PeekMessage(&aMsg, self->eventReceiverWindow, + _pr_PostEventMsgId, _pr_PostEventMsgId, PM_REMOVE); + } + return PR_SUCCESS; +#elif defined(VMS) + PR_LOG(event_lm, PR_LOG_DEBUG, + ("_pl_AcknowledgeNativeNotify: self=%p notifyCount=%d efn=%d", + self, self->notifyCount, self->efn)); + /* + ** If this is the last entry, then clear the event flag. Also make sure + ** the flag is cleared on any spurious wakeups. + */ + if (self->notifyCount <= 1) + sys$clref(self->efn); + + if (self->notifyCount <= 0) + return PR_SUCCESS; + + self->notifyCount--; + + return PR_SUCCESS; +#elif defined(XP_UNIX) + + PRInt32 count; + unsigned char c; + + PR_LOG(event_lm, PR_LOG_DEBUG, + ("_pl_AcknowledgeNativeNotify: self=%p notifyCount=%d", + self, self->notifyCount)); + if (self->notifyCount <= 0) return PR_SUCCESS; + /* consume the byte NativeNotify put in our pipe: */ + count = read(self->eventPipe[0], &c, 1); + if ((count == 1) && (c == NOTIFY_TOKEN)) { + self->notifyCount--; + return PR_SUCCESS; + } else if ((count == -1) && ((errno == EAGAIN) || (errno == EWOULDBLOCK))) { + return PR_SUCCESS; + } else + return PR_FAILURE; +#else + +#if defined(XP_MAC) +#pragma unused (self) +#endif + + /* nothing to do on the other platforms */ + return PR_SUCCESS; +#endif +} + +PR_IMPLEMENT(PRInt32) +PL_GetEventQueueSelectFD(PLEventQueue* self) +{ + if (self == NULL) + return -1; + +#if defined(VMS) + return -(self->efn); +#elif defined(XP_UNIX) + return self->eventPipe[0]; +#else + return -1; /* other platforms don't handle this (yet) */ +#endif +} + +PR_IMPLEMENT(PRBool) +PL_IsQueueOnCurrentThread( PLEventQueue *queue ) +{ + PRThread *me = PR_GetCurrentThread(); + if ( me == queue->handlerThread ) + return PR_TRUE; + else + return PR_FALSE; +} /* end PL_IsQueueOnCurrentThread() */ + +#if defined(WIN16) || defined(_WIN32) +/* +** Global Instance handle... +** In Win32 this is the module handle of the DLL. +** +*/ +static HINSTANCE _pr_hInstance; +#endif + +#if defined(WIN16) +/* +** Function LibMain() is required by Win16 +** +*/ +int CALLBACK LibMain( HINSTANCE hInst, WORD wDataSeg, + WORD cbHeapSize, LPSTR lpszCmdLine ) +{ + _pr_hInstance = hInst; + return TRUE; +} +#endif /* WIN16 */ + +#if defined(_WIN32) + +/* +** Initialization routine for the NSPR DLL... +*/ + +BOOL WINAPI DllMain (HINSTANCE hDLL, DWORD dwReason, LPVOID lpReserved) +{ + switch (dwReason) + { + case DLL_PROCESS_ATTACH: + _pr_hInstance = hDLL; + break; + + case DLL_THREAD_ATTACH: + break; + + case DLL_THREAD_DETACH: + break; + + case DLL_PROCESS_DETACH: + _pr_hInstance = NULL; + break; + } + + return TRUE; +} +#endif + + +#if defined(WIN16) || defined(_WIN32) || defined(XP_OS2) +#ifdef XP_OS2 +MRESULT EXPENTRY +_md_EventReceiverProc(HWND hwnd, ULONG uMsg, MPARAM wParam, MPARAM lParam) +#else +LRESULT CALLBACK +#if defined(WIN16) +__loadds +#endif +_md_EventReceiverProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +#endif +{ + if (_pr_PostEventMsgId == uMsg ) + { + PREventQueue *queue = (PREventQueue *)lParam; + +#if defined(_WIN32) || defined(WIN16) + queue->removeMsg = PR_FALSE; +#endif + PL_ProcessPendingEvents(queue); +#if defined(_WIN32) || defined(WIN16) + queue->removeMsg = PR_TRUE; +#endif +#ifdef XP_OS2 + return MRFROMLONG(TRUE); +#else + return TRUE; +#endif + } + return DefWindowProc(hwnd, uMsg, wParam, lParam); +} + + +static PRBool isInitialized; +static PRCallOnceType once; +static PRLock *initLock; + +/* +** InitWinEventLib() -- Create the Windows initialization lock +** +*/ +static PRStatus InitEventLib( void ) +{ + PR_ASSERT( initLock == NULL ); + + initLock = PR_NewLock(); + if ( NULL == initLock ) + return PR_FAILURE; + else + return PR_SUCCESS; +} /* end InitWinEventLib() */ + +#endif /* Win16, Win32, OS2 */ + +#if defined(_WIN32) || defined(WIN16) +/* +** _md_CreateEventQueue() -- ModelDependent initializer +*/ +static void _md_CreateEventQueue( PLEventQueue *eventQueue ) +{ + WNDCLASS wc; + + /* + ** If this is the first call to PL_InitializeEventsLib(), + ** make the call to InitWinEventLib() to create the initLock. + ** + ** Then lock the initializer lock to insure that + ** we have exclusive control over the initialization sequence. + ** + */ + + + /* Register the windows message for NSPR Event notification */ + _pr_PostEventMsgId = RegisterWindowMessage("NSPR_PostEvent"); + + /* Register the class for the event receiver window */ + if (!GetClassInfo(_pr_hInstance, _pr_eventWindowClass, &wc)) { + wc.style = 0; + wc.lpfnWndProc = _md_EventReceiverProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = _pr_hInstance; + wc.hIcon = NULL; + wc.hCursor = NULL; + wc.hbrBackground = (HBRUSH) NULL; + wc.lpszMenuName = (LPCSTR) NULL; + wc.lpszClassName = _pr_eventWindowClass; + RegisterClass(&wc); + } + + /* Create the event receiver window */ + eventQueue->eventReceiverWindow = CreateWindow(_pr_eventWindowClass, + "NSPR:EventReceiver", + 0, 0, 0, 10, 10, + NULL, NULL, _pr_hInstance, + NULL); + PR_ASSERT(eventQueue->eventReceiverWindow); + + return; +} /* end _md_CreateEventQueue() */ +#endif /* Winxx */ + +#if defined(XP_OS2) +/* +** _md_CreateEventQueue() -- ModelDependent initializer +*/ +static void _md_CreateEventQueue( PLEventQueue *eventQueue ) +{ + /* Must have HMQ for this & can't assume we already have appshell */ + if( FALSE == WinQueryQueueInfo( HMQ_CURRENT, NULL, 0)) + { + HAB hab = WinInitialize( 0); + WinCreateMsgQueue( hab, 0); + } + + if( !_pr_PostEventMsgId) + { + WinRegisterClass( 0 /* hab_current */, + _pr_eventWindowClass, + _md_EventReceiverProc, + 0, 0); + + _pr_PostEventMsgId = WinAddAtom( WinQuerySystemAtomTable(), + "NSPR_PostEvent"); + } + + eventQueue->eventReceiverWindow = WinCreateWindow( HWND_DESKTOP, + _pr_eventWindowClass, + "", 0, + 0, 0, 0, 0, + HWND_DESKTOP, + HWND_TOP, + 0, + NULL, + NULL); + PR_ASSERT(eventQueue->eventReceiverWindow); + + return; +} /* end _md_CreateEventQueue() */ +#endif /* XP_OS2 */ + +#if defined(XP_UNIX) || defined(XP_MAC) || defined(XP_BEOS) +/* +** _md_CreateEventQueue() -- ModelDependent initializer +*/ +static void _md_CreateEventQueue( PLEventQueue *eventQueue ) +{ +#ifdef XP_MAC +#pragma unused(eventQueue) +#endif + + /* there's really nothing special to do here, + ** the guts of the unix stuff is in the setupnativenotify + ** and related functions. + */ + return; +} /* end _md_CreateEventQueue() */ +#endif /* XP_UNIX */ +/* --- end plevent.c --- */ diff --git a/mozilla/xpcom/threads/plevent.h b/mozilla/xpcom/threads/plevent.h new file mode 100644 index 00000000000..38871055ca1 --- /dev/null +++ b/mozilla/xpcom/threads/plevent.h @@ -0,0 +1,497 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* + * The contents of this file are subject to the Netscape Public License + * Version 1.1 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + +/********************************************************************** +NSPL Events + +Defining Events +--------------- + +Events are essentially structures that represent argument lists for a +function that will run on another thread. All event structures you +define must include a PLEvent struct as their first field: + + typedef struct MyEventType { + PLEvent e; + // arguments follow... + int x; + char* y; + } MyEventType; + +It is also essential that you establish a model of ownership for each +argument passed in an event record, i.e. whether particular arguments +will be deleted by the event destruction callback, or whether they +only loaned to the event handler callback, and guaranteed to persist +until the time at which the handler is called. + +Sending Events +-------------- + +Events are initialized by PL_InitEvent and can be sent via +PL_PostEvent or PL_PostSynchronousEvent. Events can also have an +owner. The owner of an event can revoke all the events in a given +event-queue by calling PL_RevokeEvents. An owner might want +to do this if, for instance, it is being destroyed, and handling the +events after the owner's destruction would cause an error (e.g. an +MWContext). + +Since the act of initializing and posting an event must be coordinated +with it's possible revocation, it is essential that the event-queue's +monitor be entered surrounding the code that constructs, initializes +and posts the event: + + void postMyEvent(MyOwner* owner, int x, char* y) + { + MyEventType* event; + + PL_ENTER_EVENT_QUEUE_MONITOR(myQueue); + + // construct + event = PR_NEW(MyEventType); + if (event == NULL) goto done; + + // initialize + PL_InitEvent(event, owner, + (PLHandleEventProc)handleMyEvent, + (PLDestroyEventProc)destroyMyEvent); + event->x = x; + event->y = strdup(y); + + // post + PL_PostEvent(myQueue, &event->e); + + done: + PL_EXIT_EVENT_QUEUE_MONITOR(myQueue); + } + +If you don't call PL_InitEvent and PL_PostEvent within the +event-queue's monitor, you'll get a big red assert. + +Handling Events +--------------- + +To handle an event you must write a callback that is passed the event +record you defined containing the event's arguments: + + void* handleMyEvent(MyEventType* event) + { + doit(event->x, event->y); + return NULL; // you could return a value for a sync event + } + +Similarly for the destruction callback: + + void destroyMyEvent(MyEventType* event) + { + free(event->y); // created by strdup + free(event); + } + +Processing Events in Your Event Loop +------------------------------------ + +If your main loop only processes events delivered to the event queue, +things are rather simple. You just get the next event (which may +block), and then handle it: + + while (1) { + event = PL_GetEvent(myQueue); + PL_HandleEvent(event); + } + +However, if other things must be waited on, you'll need to obtain a +file-descriptor that represents your event queue, and hand it to select: + + fd = PL_GetEventQueueSelectFD(myQueue); + ...add fd to select set... + while (select(...)) { + if (...fd...) { + PL_ProcessPendingEvents(myQueue); + } + ... + } + +Of course, with Motif and Windows it's more complicated than that, and +on Mac it's completely different, but you get the picture. + +Revoking Events +--------------- +If at any time an owner of events is about to be destroyed, you must +take steps to ensure that no one tries to use the event queue after +the owner is gone (or a crash may result). You can do this by either +processing all the events in the queue before destroying the owner: + + { + ... + PL_ENTER_EVENT_QUEUE_MONITOR(myQueue); + PL_ProcessPendingEvents(myQueue); + DestroyMyOwner(owner); + PL_EXIT_EVENT_QUEUE_MONITOR(myQueue); + ... + } + +or by revoking the events that are in the queue for that owner. This +removes them from the queue and calls their destruction callback: + + { + ... + PL_ENTER_EVENT_QUEUE_MONITOR(myQueue); + PL_RevokeEvents(myQueue, owner); + DestroyMyOwner(owner); + PL_EXIT_EVENT_QUEUE_MONITOR(myQueue); + ... + } + +In either case it is essential that you be in the event-queue's monitor +to ensure that all events are removed from the queue for that owner, +and to ensure that no more events will be delivered for that owner. +**********************************************************************/ + +#ifndef prevent_h___ +#define prevent_h___ + +#include "prtypes.h" +#include "prclist.h" +#include "prthread.h" +#include "prmon.h" + +/* For HWND */ +#if defined(_WIN32) && !defined(__MINGW32__) +#include +#elif defined(WIN16) || defined(__MINGW32__) +#include +#elif defined(XP_OS2) +#define INCL_DOSPROCESS +#include +#endif + +PR_BEGIN_EXTERN_C + +/* Typedefs */ + +typedef struct PLEvent PLEvent; +typedef struct PLEventQueue PLEventQueue; + +/******************************************************************************* + * Event Queue Operations + ******************************************************************************/ + +/* +** Creates a new event queue. Returns NULL on failure. +*/ +PR_EXTERN(PLEventQueue*) +PL_CreateEventQueue(char* name, PRThread* handlerThread); + + +/* ----------------------------------------------------------------------- +** FUNCTION: PL_CreateNativeEventQueue() +** +** DESCRIPTION: +** PL_CreateNativeEventQueue() creates an event queue that +** uses platform specific notify mechanisms. +** +** For Unix, the platform specific notify mechanism provides +** an FD that may be extracted using the function +** PL_GetEventQueueSelectFD(). The FD returned may be used in +** a select() function call. +** +** For Windows, the platform specific notify mechanism +** provides an event receiver window that is called by +** Windows to process the event using the windows message +** pump engine. +** +** INPUTS: +** name: A name, as a diagnostic aid. +** +** handlerThread: A pointer to the PRThread structure for +** the thread that will "handle" events posted to this event +** queue. +** +** RETURNS: +** A pointer to a PLEventQueue structure or NULL. +** +*/ +PR_EXTERN(PLEventQueue *) + PL_CreateNativeEventQueue( + char *name, + PRThread *handlerThread + ); + +/* ----------------------------------------------------------------------- +** FUNCTION: PL_CreateMonitoredEventQueue() +** +** DESCRIPTION: +** PL_CreateMonitoredEventQueue() creates an event queue. No +** platform specific notify mechanism is created with the +** event queue. +** +** Users of this type of event queue must explicitly poll the +** event queue to retreive and process events. +** +** +** INPUTS: +** name: A name, as a diagnostic aid. +** +** handlerThread: A pointer to the PRThread structure for +** the thread that will "handle" events posted to this event +** queue. +** +** RETURNS: +** A pointer to a PLEventQueue structure or NULL. +** +*/ +PR_EXTERN(PLEventQueue *) + PL_CreateMonitoredEventQueue( + char *name, + PRThread *handlerThread + ); + +/* +** Destroys an event queue. +*/ +PR_EXTERN(void) +PL_DestroyEventQueue(PLEventQueue* self); + +/* +** Returns the monitor associated with an event queue. This monitor is +** selectable. The monitor should be entered to protect against anyone +** calling PL_RevokeEvents while the event is trying to be constructed +** and delivered. +*/ +PR_EXTERN(PRMonitor*) +PL_GetEventQueueMonitor(PLEventQueue* self); + +#define PL_ENTER_EVENT_QUEUE_MONITOR(queue) \ + PR_EnterMonitor(PL_GetEventQueueMonitor(queue)) + +#define PL_EXIT_EVENT_QUEUE_MONITOR(queue) \ + PR_ExitMonitor(PL_GetEventQueueMonitor(queue)) + +/* +** Posts an event to an event queue, waking up any threads waiting for an +** event. If event is NULL, notification still occurs, but no event will +** be available. +** +** Any events delivered by this routine will be destroyed by PL_HandleEvent +** when it is called (by the event-handling thread). +*/ +PR_EXTERN(PRStatus) +PL_PostEvent(PLEventQueue* self, PLEvent* event); + +/* +** Like PL_PostEvent, this routine posts an event to the event handling +** thread, but does so synchronously, waiting for the result. The result +** which is the value of the handler routine is returned. +** +** Any events delivered by this routine will be not be destroyed by +** PL_HandleEvent, but instead will be destroyed just before the result is +** returned (by the current thread). +*/ +PR_EXTERN(void*) +PL_PostSynchronousEvent(PLEventQueue* self, PLEvent* event); + +/* +** Gets an event from an event queue. Returns NULL if no event is +** available. +*/ +PR_EXTERN(PLEvent*) +PL_GetEvent(PLEventQueue* self); + +/* +** Returns true if there is an event available for PL_GetEvent. +*/ +PR_EXTERN(PRBool) +PL_EventAvailable(PLEventQueue* self); + +/* +** This is the type of the function that must be passed to PL_MapEvents +** (see description below). +*/ +typedef void +(PR_CALLBACK *PLEventFunProc)(PLEvent* event, void* data, PLEventQueue* queue); + +/* +** Applies a function to every event in the event queue. This can be used +** to selectively handle, filter, or remove events. The data pointer is +** passed to each invocation of the function fun. +*/ +PR_EXTERN(void) +PL_MapEvents(PLEventQueue* self, PLEventFunProc fun, void* data); + +/* +** This routine walks an event queue and destroys any event whose owner is +** the owner specified. The == operation is used to compare owners. +*/ +PR_EXTERN(void) +PL_RevokeEvents(PLEventQueue* self, void* owner); + +/* +** This routine processes all pending events in the event queue. It can be +** called from the thread's main event-processing loop whenever the event +** queue's selectFD is ready (returned by PL_GetEventQueueSelectFD). +*/ +PR_EXTERN(void) +PL_ProcessPendingEvents(PLEventQueue* self); + +/******************************************************************************* + * Pure Event Queues + * + * For when you're only processing PLEvents and there is no native + * select, thread messages, or AppleEvents. + ******************************************************************************/ + +/* +** Blocks until an event can be returned from the event queue. This routine +** may return NULL if the current thread is interrupted. +*/ +PR_EXTERN(PLEvent*) +PL_WaitForEvent(PLEventQueue* self); + +/* +** One stop shopping if all you're going to do is process PLEvents. Just +** call this and it loops forever processing events as they arrive. It will +** terminate when your thread is interrupted or dies. +*/ +PR_EXTERN(void) +PL_EventLoop(PLEventQueue* self); + +/******************************************************************************* + * Native Event Queues + * + * For when you need to call select, or WaitNextEvent, and yet also want + * to handle PLEvents. + ******************************************************************************/ + +/* +** This routine allows you to grab the file descriptor associated with an +** event queue and use it in the readFD set of select. Useful for platforms +** that support select, and must wait on other things besides just PLEvents. +*/ +PR_EXTERN(PRInt32) +PL_GetEventQueueSelectFD(PLEventQueue* self); + +/* +** This routine will allow you to check to see if the given eventQueue in +** on the current thread. It will return PR_TRUE if so, else it will return +** PR_FALSE +*/ +PR_EXTERN(PRBool) + PL_IsQueueOnCurrentThread( PLEventQueue *queue ); + +/******************************************************************************* + * Event Operations + ******************************************************************************/ + +/* +** The type of an event handler function. This function is passed as an +** initialization argument to PL_InitEvent, and called by +** PL_HandleEvent. If the event is called synchronously, a void* result +** may be returned (otherwise any result will be ignored). +*/ +typedef void* +(PR_CALLBACK *PLHandleEventProc)(PLEvent* self); + +/* +** The type of an event destructor function. This function is passed as +** an initialization argument to PL_InitEvent, and called by +** PL_DestroyEvent. +*/ +typedef void +(PR_CALLBACK *PLDestroyEventProc)(PLEvent* self); + +/* +** Initializes an event. Usually events are embedded in a larger event +** structure which holds event-specific data, so this is an initializer +** for that embedded part of the structure. +*/ +PR_EXTERN(void) +PL_InitEvent(PLEvent* self, void* owner, + PLHandleEventProc handler, + PLDestroyEventProc destructor); + +/* +** Returns the owner of an event. +*/ +PR_EXTERN(void*) +PL_GetEventOwner(PLEvent* self); + +/* +** Handles an event, calling the event's handler routine. +*/ +PR_EXTERN(void) +PL_HandleEvent(PLEvent* self); + +/* +** Destroys an event, calling the event's destructor. +*/ +PR_EXTERN(void) +PL_DestroyEvent(PLEvent* self); + +/* +** Removes an event from an event queue. +*/ +PR_EXTERN(void) +PL_DequeueEvent(PLEvent* self, PLEventQueue* queue); + +/******************************************************************************* + * Private Stuff + ******************************************************************************/ + +struct PLEvent { + PRCList link; + PLHandleEventProc handler; + PLDestroyEventProc destructor; + void* owner; + void* synchronousResult; + /* other fields follow... */ +}; + +/******************************************************************************/ + +/* +** Returns the event queue associated with the main thread. +** +*/ +#ifdef XP_PC +/* ----------------------------------------------------------------------- +** FUNCTION: PL_GetNativeEventReceiverWindow() +** +** DESCRIPTION: +** PL_GetNativeEventReceiverWindow() returns the windows +** handle of the event receiver window associated with the +** referenced PLEventQueue argument. +** +** INPUTS: +** PLEventQueue pointer +** +** RETURNS: +** event receiver window handle. +** +** RESTRICTIONS: MS-Windows ONLY. +** +*/ +PR_EXTERN(HWND) + PL_GetNativeEventReceiverWindow( + PLEventQueue *eqp + ); + +#endif /* XP_PC */ + +PR_END_EXTERN_C + +#endif /* prevent_h___ */