~ubuntu-branches/ubuntu/gutsy/virtualbox-ose/gutsy

« back to all changes in this revision

Viewing changes to src/libs/xpcom18a4/xpcom/threads/plevent.c

  • Committer: Bazaar Package Importer
  • Author(s): Steve Kowalik
  • Date: 2007-09-08 16:44:58 UTC
  • Revision ID: james.westby@ubuntu.com-20070908164458-wao29470vqtr8ksy
Tags: upstream-1.5.0-dfsg2
ImportĀ upstreamĀ versionĀ 1.5.0-dfsg2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 
2
/* ***** BEGIN LICENSE BLOCK *****
 
3
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 
4
 *
 
5
 * The contents of this file are subject to the Mozilla Public License Version
 
6
 * 1.1 (the "License"); you may not use this file except in compliance with
 
7
 * the License. You may obtain a copy of the License at
 
8
 * http://www.mozilla.org/MPL/
 
9
 *
 
10
 * Software distributed under the License is distributed on an "AS IS" basis,
 
11
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 
12
 * for the specific language governing rights and limitations under the
 
13
 * License.
 
14
 *
 
15
 * The Original Code is mozilla.org Code.
 
16
 *
 
17
 * The Initial Developer of the Original Code is
 
18
 * Netscape Communications Corporation.
 
19
 * Portions created by the Initial Developer are Copyright (C) 1998
 
20
 * the Initial Developer. All Rights Reserved.
 
21
 *
 
22
 * Contributor(s):
 
23
 *
 
24
 * Alternatively, the contents of this file may be used under the terms of
 
25
 * either of the GNU General Public License Version 2 or later (the "GPL"),
 
26
 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 
27
 * in which case the provisions of the GPL or the LGPL are applicable instead
 
28
 * of those above. If you wish to allow use of your version of this file only
 
29
 * under the terms of either the GPL or the LGPL, and not to allow others to
 
30
 * use your version of this file under the terms of the MPL, indicate your
 
31
 * decision by deleting the provisions above and replace them with the notice
 
32
 * and other provisions required by the GPL or the LGPL. If you do not delete
 
33
 * the provisions above, a recipient may use your version of this file under
 
34
 * the terms of any one of the MPL, the GPL or the LGPL.
 
35
 *
 
36
 * ***** END LICENSE BLOCK ***** */
 
37
 
 
38
#if defined(_WIN32)
 
39
#include <windows.h>
 
40
#endif
 
41
 
 
42
#if defined(XP_OS2)
 
43
#define INCL_DOS
 
44
#define INCL_DOSERRORS
 
45
#define INCL_WIN
 
46
#include <os2.h>
 
47
#define DefWindowProc WinDefWindowProc
 
48
#endif /* XP_OS2 */
 
49
 
 
50
#include "nspr.h"
 
51
#include "plevent.h"
 
52
 
 
53
#if !defined(WIN32)
 
54
#include <errno.h>
 
55
#include <stddef.h>
 
56
#if !defined(XP_OS2)
 
57
#include <unistd.h>
 
58
#endif /* !XP_OS2 */
 
59
#endif /* !Win32 */
 
60
 
 
61
#if defined(XP_UNIX)
 
62
/* for fcntl */
 
63
#include <sys/types.h>
 
64
#include <fcntl.h>
 
65
#endif
 
66
 
 
67
#if defined(XP_BEOS)
 
68
#include <kernel/OS.h>
 
69
#endif
 
70
 
 
71
#if defined(XP_MAC) || defined(XP_MACOSX)
 
72
#if !defined(MOZ_WIDGET_COCOA) && TARGET_CARBON
 
73
#include <CarbonEvents.h>
 
74
#define MAC_USE_CARBON_EVENT
 
75
#else
 
76
#include <Processes.h>
 
77
#define MAC_USE_WAKEUPPROCESS
 
78
#endif
 
79
#endif
 
80
 
 
81
#if defined(XP_MAC)
 
82
#include "pprthred.h"
 
83
#else
 
84
#include "private/pprthred.h"
 
85
#endif /* defined(XP_MAC) */
 
86
 
 
87
#if defined(VMS)
 
88
/*
 
89
** On OpenVMS, XtAppAddInput doesn't want a regular fd, instead it
 
90
** wants an event flag. So, we don't create and use a pipe for
 
91
** notification of when an event queue has something ready, instead
 
92
** we use an event flag. Shouldn't be a problem if we only have
 
93
** a few event queues.
 
94
*/
 
95
#include <lib$routines.h>
 
96
#include <starlet.h>
 
97
#include <stsdef.h>
 
98
#endif /* VMS */
 
99
 
 
100
#if defined(_WIN32)
 
101
/* Comment out the following USE_TIMER define to prevent
 
102
 * WIN32 from using a WIN32 native timer for PLEvent notification.
 
103
 * With USE_TIMER defined we will use a timer when pending input
 
104
 * or paint events are starved, otherwise it will use a posted
 
105
 * WM_APP msg for PLEvent notification.
 
106
 */
 
107
#define USE_TIMER
 
108
 
 
109
/* Threshold defined in milliseconds for determining when the input
 
110
 * and paint events have been held in the WIN32 msg queue too long
 
111
 */
 
112
#define INPUT_STARVATION_LIMIT    50
 
113
/* The paint starvation limit is set to the smallest value which
 
114
 * does not cause performance degradation while running page load tests
 
115
 */
 
116
#define PAINT_STARVATION_LIMIT   750
 
117
/* The WIN9X paint starvation limit is larger because it was
 
118
 * determined that the following value was required to prevent performance
 
119
 * degradation on page load tests for WIN98/95 only.
 
120
 */
 
121
#define WIN9X_PAINT_STARVATION_LIMIT 3000
 
122
 
 
123
#define TIMER_ID 0
 
124
/* If _md_PerformanceSetting <=0 then no event starvation otherwise events will be starved */
 
125
static PRInt32  _md_PerformanceSetting = 0;
 
126
static PRUint32 _md_StarvationDelay    = 0;
 
127
static PRUint32 _md_SwitchTime         = 0;
 
128
#endif
 
129
 
 
130
static PRLogModuleInfo *event_lm = NULL;
 
131
 
 
132
/*******************************************************************************
 
133
 * Private Stuff
 
134
 ******************************************************************************/
 
135
 
 
136
/*
 
137
** EventQueueType -- Defines notification type for an event queue
 
138
**
 
139
*/
 
140
typedef enum {
 
141
    EventQueueIsNative = 1,
 
142
    EventQueueIsMonitored = 2
 
143
} EventQueueType;
 
144
 
 
145
 
 
146
struct PLEventQueue {
 
147
    const char*         name;
 
148
    PRCList             queue;
 
149
    PRMonitor*          monitor;
 
150
    PRThread*           handlerThread;
 
151
    EventQueueType      type;
 
152
    PRPackedBool        processingEvents;
 
153
    PRPackedBool        notified;
 
154
#if defined(_WIN32) 
 
155
    PRPackedBool        timerSet;
 
156
#endif
 
157
 
 
158
#if defined(XP_UNIX) && !defined(XP_MACOSX)
 
159
#if defined(VMS)
 
160
    int                 efn;
 
161
#else
 
162
    PRInt32             eventPipe[2];
 
163
#endif
 
164
    PLGetEventIDFunc    idFunc;
 
165
    void*               idFuncClosure;
 
166
#elif defined(_WIN32) || defined(XP_OS2)
 
167
    HWND                eventReceiverWindow;
 
168
    PRBool              removeMsg;
 
169
#elif defined(XP_BEOS)
 
170
    port_id             eventport;
 
171
#elif defined(XP_MAC) || defined(XP_MACOSX)
 
172
#if defined(MAC_USE_CARBON_EVENT)
 
173
    EventHandlerUPP     eventHandlerUPP;
 
174
    EventHandlerRef     eventHandlerRef;
 
175
#elif defined(MAC_USE_WAKEUPPROCESS)
 
176
    ProcessSerialNumber psn;
 
177
#endif
 
178
#endif
 
179
};
 
180
 
 
181
#define PR_EVENT_PTR(_qp) \
 
182
    ((PLEvent*) ((char*) (_qp) - offsetof(PLEvent, link)))
 
183
 
 
184
static PRStatus    _pl_SetupNativeNotifier(PLEventQueue* self);
 
185
static void        _pl_CleanupNativeNotifier(PLEventQueue* self);
 
186
static PRStatus    _pl_NativeNotify(PLEventQueue* self);
 
187
static PRStatus    _pl_AcknowledgeNativeNotify(PLEventQueue* self);
 
188
static void        _md_CreateEventQueue( PLEventQueue *eventQueue );
 
189
static PRInt32     _pl_GetEventCount(PLEventQueue* self);
 
190
 
 
191
 
 
192
#if defined(_WIN32) || defined(XP_OS2)
 
193
#if defined(XP_OS2)
 
194
ULONG _pr_PostEventMsgId;
 
195
#else
 
196
UINT _pr_PostEventMsgId;
 
197
#endif /* OS2 */
 
198
static char *_pr_eventWindowClass = "XPCOM:EventWindow";
 
199
#endif /* Win32, OS2 */
 
200
 
 
201
#if defined(_WIN32)
 
202
 
 
203
static LPCTSTR _md_GetEventQueuePropName() {
 
204
    static ATOM atom = 0;
 
205
    if (!atom) {
 
206
        atom = GlobalAddAtom("XPCOM_EventQueue");
 
207
    }
 
208
    return MAKEINTATOM(atom);
 
209
}
 
210
#endif
 
211
 
 
212
#if defined(MAC_USE_CARBON_EVENT)
 
213
enum {
 
214
  kEventClassPL         = FOUR_CHAR_CODE('PLEC'),
 
215
 
 
216
  kEventProcessPLEvents = 1,
 
217
 
 
218
  kEventParamPLEventQueue = FOUR_CHAR_CODE('OWNQ')
 
219
};
 
220
 
 
221
static pascal Boolean _md_CarbonEventComparator(EventRef inEvent, void *inCompareData);
 
222
#endif
 
223
 
 
224
/*******************************************************************************
 
225
 * Event Queue Operations
 
226
 ******************************************************************************/
 
227
 
 
228
/*
 
229
** _pl_CreateEventQueue() -- Create the event queue
 
230
**
 
231
**
 
232
*/
 
233
static PLEventQueue * _pl_CreateEventQueue(const char *name,
 
234
                                           PRThread *handlerThread,
 
235
                                           EventQueueType  qtype)
 
236
{
 
237
    PRStatus err;
 
238
    PLEventQueue* self = NULL;
 
239
    PRMonitor* mon = NULL;
 
240
 
 
241
    if (event_lm == NULL)
 
242
        event_lm = PR_NewLogModule("event");
 
243
 
 
244
    self = PR_NEWZAP(PLEventQueue);
 
245
    if (self == NULL) return NULL;
 
246
 
 
247
    mon = PR_NewNamedMonitor(name);
 
248
    if (mon == NULL) goto error;
 
249
 
 
250
    self->name = name;
 
251
    self->monitor = mon;
 
252
    self->handlerThread = handlerThread;
 
253
    self->processingEvents = PR_FALSE;
 
254
    self->type = qtype;
 
255
#if defined(_WIN32)
 
256
    self->timerSet = PR_FALSE;
 
257
#endif
 
258
#if defined(_WIN32) || defined(XP_OS2)
 
259
    self->removeMsg = PR_TRUE;
 
260
#endif
 
261
#if defined(MAC_USE_WAKEUPPROCESS)
 
262
    self->psn.lowLongOfPSN = kNoProcess;
 
263
#endif
 
264
 
 
265
    self->notified = PR_FALSE;
 
266
 
 
267
    PR_INIT_CLIST(&self->queue);
 
268
    if ( qtype == EventQueueIsNative ) {
 
269
        err = _pl_SetupNativeNotifier(self);
 
270
        if (err) goto error;
 
271
        _md_CreateEventQueue( self );
 
272
    }
 
273
    return self;
 
274
 
 
275
  error:
 
276
    if (mon != NULL)
 
277
        PR_DestroyMonitor(mon);
 
278
    PR_DELETE(self);
 
279
    return NULL;
 
280
}
 
281
 
 
282
PR_IMPLEMENT(PLEventQueue*)
 
283
PL_CreateEventQueue(const char* name, PRThread* handlerThread)
 
284
{
 
285
    return( _pl_CreateEventQueue( name, handlerThread, EventQueueIsNative ));
 
286
}
 
287
 
 
288
PR_EXTERN(PLEventQueue *) 
 
289
PL_CreateNativeEventQueue(const char *name, PRThread *handlerThread)
 
290
{
 
291
    return( _pl_CreateEventQueue( name, handlerThread, EventQueueIsNative ));
 
292
}
 
293
 
 
294
PR_EXTERN(PLEventQueue *) 
 
295
PL_CreateMonitoredEventQueue(const char *name, PRThread *handlerThread)
 
296
{
 
297
    return( _pl_CreateEventQueue( name, handlerThread, EventQueueIsMonitored ));
 
298
}
 
299
 
 
300
PR_IMPLEMENT(PRMonitor*)
 
301
PL_GetEventQueueMonitor(PLEventQueue* self)
 
302
{
 
303
    return self->monitor;
 
304
}
 
305
 
 
306
static void PR_CALLBACK
 
307
_pl_destroyEvent(PLEvent* event, void* data, PLEventQueue* queue)
 
308
{
 
309
#ifdef XP_MAC
 
310
#pragma unused (data, queue)
 
311
#endif
 
312
    PL_DequeueEvent(event, queue);
 
313
    PL_DestroyEvent(event);
 
314
}
 
315
 
 
316
PR_IMPLEMENT(void)
 
317
PL_DestroyEventQueue(PLEventQueue* self)
 
318
{
 
319
    PR_EnterMonitor(self->monitor);
 
320
 
 
321
    /* destroy undelivered events */
 
322
    PL_MapEvents(self, _pl_destroyEvent, NULL);
 
323
 
 
324
    if ( self->type == EventQueueIsNative )
 
325
        _pl_CleanupNativeNotifier(self);
 
326
 
 
327
    /* destroying the monitor also destroys the name */
 
328
    PR_ExitMonitor(self->monitor);
 
329
    PR_DestroyMonitor(self->monitor);
 
330
    PR_DELETE(self);
 
331
 
 
332
}
 
333
 
 
334
PR_IMPLEMENT(PRStatus)
 
335
PL_PostEvent(PLEventQueue* self, PLEvent* event)
 
336
{
 
337
    PRStatus err = PR_SUCCESS;
 
338
    PRMonitor* mon;
 
339
 
 
340
    if (self == NULL)
 
341
        return PR_FAILURE;
 
342
 
 
343
    mon = self->monitor;
 
344
    PR_EnterMonitor(mon);
 
345
 
 
346
#if defined(XP_UNIX) && !defined(XP_MACOSX)
 
347
    if (self->idFunc && event)
 
348
        event->id = self->idFunc(self->idFuncClosure);
 
349
#endif
 
350
 
 
351
    /* insert event into thread's event queue: */
 
352
    if (event != NULL) {
 
353
        PR_APPEND_LINK(&event->link, &self->queue);
 
354
    }
 
355
 
 
356
    if (self->type == EventQueueIsNative && !self->notified) {
 
357
        err = _pl_NativeNotify(self);
 
358
 
 
359
        if (err != PR_SUCCESS)
 
360
            goto error;
 
361
 
 
362
        self->notified = PR_TRUE;
 
363
    }
 
364
 
 
365
    /*
 
366
     * This may fall on deaf ears if we're really notifying the native
 
367
     * thread, and no one has called PL_WaitForEvent (or PL_EventLoop):
 
368
     */
 
369
    err = PR_Notify(mon);
 
370
 
 
371
error:
 
372
    PR_ExitMonitor(mon);
 
373
    return err;
 
374
}
 
375
 
 
376
PR_IMPLEMENT(void*)
 
377
PL_PostSynchronousEvent(PLEventQueue* self, PLEvent* event)
 
378
{
 
379
    void* result;
 
380
 
 
381
    if (self == NULL)
 
382
        return NULL;
 
383
 
 
384
    PR_ASSERT(event != NULL);
 
385
 
 
386
    if (PR_GetCurrentThread() == self->handlerThread) {
 
387
        /* Handle the case where the thread requesting the event handling
 
388
         * is also the thread that's supposed to do the handling. */
 
389
        result = event->handler(event);
 
390
    }
 
391
    else {
 
392
        int i, entryCount;
 
393
 
 
394
        event->lock = PR_NewLock();
 
395
        if (!event->lock) {
 
396
          return NULL;
 
397
        }
 
398
        event->condVar = PR_NewCondVar(event->lock);
 
399
        if(!event->condVar) {
 
400
          PR_DestroyLock(event->lock);
 
401
          event->lock = NULL;
 
402
          return NULL;
 
403
        }
 
404
 
 
405
        PR_Lock(event->lock);
 
406
 
 
407
        entryCount = PR_GetMonitorEntryCount(self->monitor);
 
408
 
 
409
        event->synchronousResult = (void*)PR_TRUE;
 
410
 
 
411
        PL_PostEvent(self, event);
 
412
 
 
413
        /* We need temporarily to give up our event queue monitor if
 
414
           we're holding it, otherwise, the thread we're going to wait
 
415
           for notification from won't be able to enter it to process
 
416
           the event. */
 
417
        if (entryCount) {
 
418
            for (i = 0; i < entryCount; i++)
 
419
                PR_ExitMonitor(self->monitor);
 
420
        }
 
421
 
 
422
        event->handled = PR_FALSE;
 
423
 
 
424
        while (!event->handled) {
 
425
            /* wait for event to be handled or destroyed */
 
426
            PR_WaitCondVar(event->condVar, PR_INTERVAL_NO_TIMEOUT);
 
427
        }
 
428
 
 
429
        if (entryCount) {
 
430
            for (i = 0; i < entryCount; i++)
 
431
                PR_EnterMonitor(self->monitor);
 
432
        }
 
433
 
 
434
        result = event->synchronousResult;
 
435
        event->synchronousResult = NULL;
 
436
        PR_Unlock(event->lock);
 
437
    }
 
438
 
 
439
    /* For synchronous events, they're destroyed here on the caller's
 
440
       thread before the result is returned. See PL_HandleEvent. */
 
441
    PL_DestroyEvent(event);
 
442
 
 
443
    return result;
 
444
}
 
445
 
 
446
PR_IMPLEMENT(PLEvent*)
 
447
PL_GetEvent(PLEventQueue* self)
 
448
{
 
449
    PLEvent* event = NULL;
 
450
    PRStatus err = PR_SUCCESS;
 
451
 
 
452
    if (self == NULL)
 
453
        return NULL;
 
454
 
 
455
    PR_EnterMonitor(self->monitor);
 
456
 
 
457
    if (!PR_CLIST_IS_EMPTY(&self->queue)) {
 
458
        if ( self->type == EventQueueIsNative &&
 
459
             self->notified                   &&
 
460
             !self->processingEvents          &&
 
461
             0 == _pl_GetEventCount(self)     )
 
462
        {
 
463
            err = _pl_AcknowledgeNativeNotify(self);
 
464
            self->notified = PR_FALSE;
 
465
        }
 
466
        if (err)
 
467
            goto done;
 
468
 
 
469
        /* then grab the event and return it: */
 
470
        event = PR_EVENT_PTR(self->queue.next);
 
471
        PR_REMOVE_AND_INIT_LINK(&event->link);
 
472
    }
 
473
 
 
474
  done:
 
475
    PR_ExitMonitor(self->monitor);
 
476
    return event;
 
477
}
 
478
 
 
479
PR_IMPLEMENT(PRBool)
 
480
PL_EventAvailable(PLEventQueue* self)
 
481
{
 
482
    PRBool result = PR_FALSE;
 
483
 
 
484
    if (self == NULL)
 
485
        return PR_FALSE;
 
486
 
 
487
    PR_EnterMonitor(self->monitor);
 
488
 
 
489
    if (!PR_CLIST_IS_EMPTY(&self->queue))
 
490
        result = PR_TRUE;
 
491
 
 
492
    PR_ExitMonitor(self->monitor);
 
493
    return result;
 
494
}
 
495
 
 
496
PR_IMPLEMENT(void)
 
497
PL_MapEvents(PLEventQueue* self, PLEventFunProc fun, void* data)
 
498
{
 
499
    PRCList* qp;
 
500
 
 
501
    if (self == NULL)
 
502
        return;
 
503
 
 
504
    PR_EnterMonitor(self->monitor);
 
505
    qp = self->queue.next;
 
506
    while (qp != &self->queue) {
 
507
        PLEvent* event = PR_EVENT_PTR(qp);
 
508
        qp = qp->next;
 
509
        (*fun)(event, data, self);
 
510
    }
 
511
    PR_ExitMonitor(self->monitor);
 
512
}
 
513
 
 
514
static void PR_CALLBACK
 
515
_pl_DestroyEventForOwner(PLEvent* event, void* owner, PLEventQueue* queue)
 
516
{
 
517
    PR_ASSERT(PR_GetMonitorEntryCount(queue->monitor) > 0);
 
518
    if (event->owner == owner) {
 
519
        PR_LOG(event_lm, PR_LOG_DEBUG,
 
520
               ("$$$ \tdestroying event %0x for owner %0x", event, owner));
 
521
        PL_DequeueEvent(event, queue);
 
522
 
 
523
        if (event->synchronousResult == (void*)PR_TRUE) {
 
524
            PR_Lock(event->lock);
 
525
            event->synchronousResult = NULL;
 
526
            event->handled = PR_TRUE;
 
527
            PR_NotifyCondVar(event->condVar);
 
528
            PR_Unlock(event->lock);
 
529
        }
 
530
        else {
 
531
            PL_DestroyEvent(event);
 
532
        }
 
533
    }
 
534
    else {
 
535
        PR_LOG(event_lm, PR_LOG_DEBUG,
 
536
               ("$$$ \tskipping event %0x for owner %0x", event, owner));
 
537
    }
 
538
}
 
539
 
 
540
PR_IMPLEMENT(void)
 
541
PL_RevokeEvents(PLEventQueue* self, void* owner)
 
542
{
 
543
    if (self == NULL)
 
544
        return;
 
545
 
 
546
    PR_LOG(event_lm, PR_LOG_DEBUG,
 
547
         ("$$$ revoking events for owner %0x", owner));
 
548
 
 
549
    /*
 
550
    ** First we enter the monitor so that no one else can post any events
 
551
    ** to the queue:
 
552
    */
 
553
    PR_EnterMonitor(self->monitor);
 
554
    PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ owner %0x, entered monitor", owner));
 
555
 
 
556
    /*
 
557
    ** Discard any pending events for this owner:
 
558
    */
 
559
    PL_MapEvents(self, _pl_DestroyEventForOwner, owner);
 
560
 
 
561
#ifdef DEBUG
 
562
    {
 
563
        PRCList* qp = self->queue.next;
 
564
        while (qp != &self->queue) {
 
565
            PLEvent* event = PR_EVENT_PTR(qp);
 
566
            qp = qp->next;
 
567
            PR_ASSERT(event->owner != owner);
 
568
        }
 
569
    }
 
570
#endif /* DEBUG */
 
571
 
 
572
    PR_ExitMonitor(self->monitor);
 
573
 
 
574
    PR_LOG(event_lm, PR_LOG_DEBUG,
 
575
           ("$$$ revoking events for owner %0x", owner));
 
576
}
 
577
 
 
578
static PRInt32
 
579
_pl_GetEventCount(PLEventQueue* self)
 
580
{
 
581
    PRCList* node;
 
582
    PRInt32  count = 0;
 
583
 
 
584
    PR_EnterMonitor(self->monitor);
 
585
    node = PR_LIST_HEAD(&self->queue);
 
586
    while (node != &self->queue) {
 
587
        count++;
 
588
        node = PR_NEXT_LINK(node);
 
589
    }
 
590
    PR_ExitMonitor(self->monitor);
 
591
 
 
592
    return count;
 
593
}
 
594
 
 
595
PR_IMPLEMENT(void)
 
596
PL_ProcessPendingEvents(PLEventQueue* self)
 
597
{
 
598
    PRInt32 count;
 
599
 
 
600
    if (self == NULL)
 
601
        return;
 
602
 
 
603
 
 
604
    PR_EnterMonitor(self->monitor);
 
605
 
 
606
    if (self->processingEvents) {
 
607
        _pl_AcknowledgeNativeNotify(self);
 
608
        self->notified = PR_FALSE;
 
609
        PR_ExitMonitor(self->monitor);
 
610
        return;
 
611
    }
 
612
    self->processingEvents = PR_TRUE;
 
613
 
 
614
    /* Only process the events that are already in the queue, and
 
615
     * not any new events that get added. Do this by counting the
 
616
     * number of events currently in the queue
 
617
     */
 
618
    count = _pl_GetEventCount(self);
 
619
    PR_ExitMonitor(self->monitor);
 
620
 
 
621
    while (count-- > 0) {
 
622
        PLEvent* event = PL_GetEvent(self);
 
623
        if (event == NULL)
 
624
            break;
 
625
 
 
626
        PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ processing event"));
 
627
        PL_HandleEvent(event);
 
628
        PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ done processing event"));
 
629
    }
 
630
 
 
631
    PR_EnterMonitor(self->monitor);
 
632
 
 
633
    if (self->type == EventQueueIsNative) {
 
634
        count = _pl_GetEventCount(self);
 
635
 
 
636
        if (count <= 0) {
 
637
            _pl_AcknowledgeNativeNotify(self);
 
638
            self->notified = PR_FALSE;
 
639
        }
 
640
        else {
 
641
            _pl_NativeNotify(self);
 
642
            self->notified = PR_TRUE;
 
643
        }
 
644
 
 
645
    }
 
646
    self->processingEvents = PR_FALSE;
 
647
 
 
648
    PR_ExitMonitor(self->monitor);
 
649
}
 
650
 
 
651
/*******************************************************************************
 
652
 * Event Operations
 
653
 ******************************************************************************/
 
654
 
 
655
PR_IMPLEMENT(void)
 
656
PL_InitEvent(PLEvent* self, void* owner,
 
657
             PLHandleEventProc handler,
 
658
             PLDestroyEventProc destructor)
 
659
{
 
660
#ifdef PL_POST_TIMINGS
 
661
    self->postTime = PR_IntervalNow();
 
662
#endif
 
663
    PR_INIT_CLIST(&self->link);
 
664
    self->handler = handler;
 
665
    self->destructor = destructor;
 
666
    self->owner = owner;
 
667
    self->synchronousResult = NULL;
 
668
    self->handled = PR_FALSE;
 
669
    self->lock = NULL;
 
670
    self->condVar = NULL;
 
671
#if defined(XP_UNIX) && !defined(XP_MACOSX)
 
672
    self->id = 0;
 
673
#endif
 
674
}
 
675
 
 
676
PR_IMPLEMENT(void*)
 
677
PL_GetEventOwner(PLEvent* self)
 
678
{
 
679
    return self->owner;
 
680
}
 
681
 
 
682
PR_IMPLEMENT(void)
 
683
PL_HandleEvent(PLEvent* self)
 
684
{
 
685
    void* result;
 
686
    if (self == NULL)
 
687
        return;
 
688
 
 
689
    /* This event better not be on an event queue anymore. */
 
690
    PR_ASSERT(PR_CLIST_IS_EMPTY(&self->link));
 
691
 
 
692
    result = self->handler(self);
 
693
    if (NULL != self->synchronousResult) {
 
694
        PR_Lock(self->lock);
 
695
        self->synchronousResult = result;
 
696
        self->handled = PR_TRUE;
 
697
        PR_NotifyCondVar(self->condVar);
 
698
        PR_Unlock(self->lock);
 
699
    }
 
700
    else {
 
701
        /* For asynchronous events, they're destroyed by the event-handler
 
702
           thread. See PR_PostSynchronousEvent. */
 
703
        PL_DestroyEvent(self);
 
704
    }
 
705
}
 
706
#ifdef PL_POST_TIMINGS
 
707
static long s_eventCount = 0;
 
708
static long s_totalTime  = 0;
 
709
#endif
 
710
 
 
711
PR_IMPLEMENT(void)
 
712
PL_DestroyEvent(PLEvent* self)
 
713
{
 
714
    if (self == NULL)
 
715
        return;
 
716
 
 
717
    /* This event better not be on an event queue anymore. */
 
718
    PR_ASSERT(PR_CLIST_IS_EMPTY(&self->link));
 
719
 
 
720
    if(self->condVar)
 
721
      PR_DestroyCondVar(self->condVar);
 
722
    if(self->lock)
 
723
      PR_DestroyLock(self->lock);
 
724
 
 
725
#ifdef PL_POST_TIMINGS
 
726
    s_totalTime += PR_IntervalNow() - self->postTime;
 
727
    s_eventCount++;
 
728
    printf("$$$ running avg (%d) \n", PR_IntervalToMilliseconds(s_totalTime/s_eventCount));
 
729
#endif
 
730
 
 
731
    self->destructor(self);
 
732
}
 
733
 
 
734
PR_IMPLEMENT(void)
 
735
PL_DequeueEvent(PLEvent* self, PLEventQueue* queue)
 
736
{
 
737
#ifdef XP_MAC
 
738
#pragma unused (queue)
 
739
#endif
 
740
    if (self == NULL)
 
741
        return;
 
742
 
 
743
    /* Only the owner is allowed to dequeue events because once the
 
744
       client has put it in the queue, they have no idea whether it's
 
745
       been processed and destroyed or not. */
 
746
 
 
747
    PR_ASSERT(queue->handlerThread == PR_GetCurrentThread());
 
748
 
 
749
    PR_EnterMonitor(queue->monitor);
 
750
 
 
751
    PR_ASSERT(!PR_CLIST_IS_EMPTY(&self->link));
 
752
 
 
753
#if 0
 
754
    /*  I do not think that we need to do this anymore.
 
755
        if we do not acknowledge and this is the only
 
756
        only event in the queue, any calls to process
 
757
        the eventQ will be effective noop.
 
758
    */
 
759
    if (queue->type == EventQueueIsNative)
 
760
      _pl_AcknowledgeNativeNotify(queue);
 
761
#endif
 
762
 
 
763
    PR_REMOVE_AND_INIT_LINK(&self->link);
 
764
 
 
765
    PR_ExitMonitor(queue->monitor);
 
766
}
 
767
 
 
768
PR_IMPLEMENT(void)
 
769
PL_FavorPerformanceHint(PRBool favorPerformanceOverEventStarvation,
 
770
                        PRUint32 starvationDelay)
 
771
{
 
772
#if defined(_WIN32)
 
773
 
 
774
    _md_StarvationDelay = starvationDelay;
 
775
 
 
776
    if (favorPerformanceOverEventStarvation) {
 
777
        _md_PerformanceSetting++;
 
778
        return;
 
779
    }
 
780
 
 
781
    _md_PerformanceSetting--;
 
782
 
 
783
    if (_md_PerformanceSetting == 0) {
 
784
      /* Switched from allowing event starvation to no event starvation so grab
 
785
         the current time to determine when to actually switch to using timers
 
786
         instead of posted WM_APP messages. */
 
787
      _md_SwitchTime = PR_IntervalToMilliseconds(PR_IntervalNow());
 
788
    }
 
789
 
 
790
#endif
 
791
}
 
792
 
 
793
/*******************************************************************************
 
794
 * Pure Event Queues
 
795
 *
 
796
 * For when you're only processing PLEvents and there is no native
 
797
 * select, thread messages, or AppleEvents.
 
798
 ******************************************************************************/
 
799
 
 
800
PR_IMPLEMENT(PLEvent*)
 
801
PL_WaitForEvent(PLEventQueue* self)
 
802
{
 
803
    PLEvent* event;
 
804
    PRMonitor* mon;
 
805
 
 
806
    if (self == NULL)
 
807
        return NULL;
 
808
 
 
809
    mon = self->monitor;
 
810
    PR_EnterMonitor(mon);
 
811
 
 
812
    while ((event = PL_GetEvent(self)) == NULL) {
 
813
        PRStatus err;
 
814
        PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ waiting for event"));
 
815
        err = PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT);
 
816
        if ((err == PR_FAILURE)
 
817
            && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) break;
 
818
    }
 
819
 
 
820
    PR_ExitMonitor(mon);
 
821
    return event;
 
822
}
 
823
 
 
824
PR_IMPLEMENT(void)
 
825
PL_EventLoop(PLEventQueue* self)
 
826
{
 
827
    if (self == NULL)
 
828
        return;
 
829
 
 
830
    while (PR_TRUE) {
 
831
        PLEvent* event = PL_WaitForEvent(self);
 
832
        if (event == NULL) {
 
833
            /* This can only happen if the current thread is interrupted */
 
834
            return;
 
835
        }
 
836
 
 
837
        PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ processing event"));
 
838
        PL_HandleEvent(event);
 
839
        PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ done processing event"));
 
840
    }
 
841
}
 
842
 
 
843
/*******************************************************************************
 
844
 * Native Event Queues
 
845
 *
 
846
 * For when you need to call select, or WaitNextEvent, and yet also want
 
847
 * to handle PLEvents.
 
848
 ******************************************************************************/
 
849
 
 
850
static PRStatus
 
851
_pl_SetupNativeNotifier(PLEventQueue* self)
 
852
{
 
853
#if defined(XP_MAC)
 
854
#pragma unused (self)
 
855
#endif
 
856
 
 
857
#if defined(VMS)
 
858
    unsigned int status;
 
859
    self->idFunc = 0;
 
860
    self->idFuncClosure = 0;
 
861
    status = LIB$GET_EF(&self->efn);
 
862
    if (!$VMS_STATUS_SUCCESS(status))
 
863
        return PR_FAILURE;
 
864
    PR_LOG(event_lm, PR_LOG_DEBUG,
 
865
           ("$$$ Allocated event flag %d", self->efn));
 
866
    return PR_SUCCESS;
 
867
#elif defined(XP_UNIX) && !defined(XP_MACOSX)
 
868
    int err;
 
869
    int flags;
 
870
 
 
871
    self->idFunc = 0;
 
872
    self->idFuncClosure = 0;
 
873
 
 
874
    err = pipe(self->eventPipe);
 
875
    if (err != 0) {
 
876
        return PR_FAILURE;
 
877
    }
 
878
 
 
879
    /* make the pipe nonblocking */
 
880
    flags = fcntl(self->eventPipe[0], F_GETFL, 0);
 
881
    if (flags == -1) {
 
882
        goto failed;
 
883
    }
 
884
    err = fcntl(self->eventPipe[0], F_SETFL, flags | O_NONBLOCK);
 
885
    if (err == -1) {
 
886
        goto failed;
 
887
    }
 
888
    flags = fcntl(self->eventPipe[1], F_GETFL, 0);
 
889
    if (flags == -1) {
 
890
        goto failed;
 
891
    }
 
892
    err = fcntl(self->eventPipe[1], F_SETFL, flags | O_NONBLOCK);
 
893
    if (err == -1) {
 
894
        goto failed;
 
895
    }
 
896
    return PR_SUCCESS;
 
897
 
 
898
failed:
 
899
    close(self->eventPipe[0]);
 
900
    close(self->eventPipe[1]);
 
901
    return PR_FAILURE;
 
902
#elif defined(XP_BEOS)
 
903
    /* hook up to the nsToolkit queue, however the appshell
 
904
     * isn't necessairly started, so we might have to create
 
905
     * the queue ourselves
 
906
     */
 
907
    char portname[64];
 
908
    char semname[64];
 
909
    PR_snprintf(portname, sizeof(portname), "event%lx", 
 
910
                (long unsigned) self->handlerThread);
 
911
    PR_snprintf(semname, sizeof(semname), "sync%lx", 
 
912
                (long unsigned) self->handlerThread);
 
913
 
 
914
    if((self->eventport = find_port(portname)) < 0)
 
915
    {
 
916
        /* create port
 
917
         */
 
918
        self->eventport = create_port(500, portname);
 
919
 
 
920
        /* We don't use the sem, but it has to be there
 
921
         */
 
922
        create_sem(0, semname);
 
923
    }
 
924
 
 
925
    return PR_SUCCESS;
 
926
#else
 
927
    return PR_SUCCESS;
 
928
#endif
 
929
}
 
930
 
 
931
static void
 
932
_pl_CleanupNativeNotifier(PLEventQueue* self)
 
933
{
 
934
#if defined(XP_MAC)
 
935
#pragma unused (self)
 
936
#endif
 
937
 
 
938
#if defined(VMS)
 
939
    {
 
940
        unsigned int status;
 
941
        PR_LOG(event_lm, PR_LOG_DEBUG,
 
942
           ("$$$ Freeing event flag %d", self->efn));
 
943
        status = LIB$FREE_EF(&self->efn);
 
944
    }
 
945
#elif defined(XP_UNIX) && !defined(XP_MACOSX)
 
946
    close(self->eventPipe[0]);
 
947
    close(self->eventPipe[1]);
 
948
#elif defined(_WIN32)
 
949
    if (self->timerSet) {
 
950
        KillTimer(self->eventReceiverWindow, TIMER_ID);
 
951
        self->timerSet = PR_FALSE;
 
952
    }
 
953
    RemoveProp(self->eventReceiverWindow, _md_GetEventQueuePropName());
 
954
 
 
955
    /* DestroyWindow doesn't do anything when called from a non ui thread.  Since 
 
956
     * self->eventReceiverWindow was created on the ui thread, it must be destroyed
 
957
     * on the ui thread.
 
958
     */
 
959
    SendMessage(self->eventReceiverWindow, WM_CLOSE, 0, 0);
 
960
 
 
961
#elif defined(XP_OS2)
 
962
    WinDestroyWindow(self->eventReceiverWindow);
 
963
#elif defined(MAC_USE_CARBON_EVENT)
 
964
    EventComparatorUPP comparator = NewEventComparatorUPP(_md_CarbonEventComparator);
 
965
    PR_ASSERT(comparator != NULL);
 
966
    if (comparator) {
 
967
      FlushSpecificEventsFromQueue(GetMainEventQueue(), comparator, self);
 
968
      DisposeEventComparatorUPP(comparator);
 
969
    }
 
970
    DisposeEventHandlerUPP(self->eventHandlerUPP);
 
971
    RemoveEventHandler(self->eventHandlerRef);
 
972
#endif
 
973
}
 
974
 
 
975
#if defined(_WIN32)
 
976
 
 
977
static PRBool   _md_WasInputPending = PR_FALSE;
 
978
static PRUint32 _md_InputTime = 0;
 
979
static PRBool   _md_WasPaintPending = PR_FALSE;
 
980
static PRUint32 _md_PaintTime = 0;
 
981
/* last mouse location */
 
982
static POINT    _md_LastMousePos;
 
983
 
 
984
/*******************************************************************************
 
985
 * Timer callback function. Timers are used on WIN32 instead of APP events
 
986
 * when there are pending UI events because APP events can cause the GUI to lockup
 
987
 * because posted messages are processed before other messages.
 
988
 ******************************************************************************/
 
989
 
 
990
static void CALLBACK _md_TimerProc( HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime )
 
991
{
 
992
    PREventQueue* queue =  (PREventQueue  *) GetProp(hwnd, _md_GetEventQueuePropName());
 
993
    PR_ASSERT(queue != NULL);
 
994
 
 
995
    KillTimer(hwnd, TIMER_ID);
 
996
    queue->timerSet = PR_FALSE;
 
997
    queue->removeMsg = PR_FALSE;
 
998
    PL_ProcessPendingEvents( queue );
 
999
    queue->removeMsg = PR_TRUE;
 
1000
}
 
1001
 
 
1002
static PRBool _md_IsWIN9X = PR_FALSE;
 
1003
static PRBool _md_IsOSSet = PR_FALSE;
 
1004
 
 
1005
static void _md_DetermineOSType()
 
1006
{
 
1007
    OSVERSIONINFO os;
 
1008
    os.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
 
1009
    GetVersionEx(&os);
 
1010
    if (VER_PLATFORM_WIN32_WINDOWS == os.dwPlatformId) {
 
1011
        _md_IsWIN9X = PR_TRUE;
 
1012
    }
 
1013
}
 
1014
 
 
1015
static PRUint32 _md_GetPaintStarvationLimit()
 
1016
{
 
1017
    if (! _md_IsOSSet) {
 
1018
        _md_DetermineOSType();
 
1019
        _md_IsOSSet = PR_TRUE;
 
1020
    }
 
1021
 
 
1022
    if (_md_IsWIN9X) {
 
1023
        return WIN9X_PAINT_STARVATION_LIMIT;
 
1024
    }
 
1025
 
 
1026
    return PAINT_STARVATION_LIMIT;
 
1027
}
 
1028
 
 
1029
 
 
1030
/*
 
1031
 * Determine if an event is being starved (i.e the starvation limit has
 
1032
 * been exceeded.
 
1033
 * Note: this function uses the current setting and updates the contents
 
1034
 * of the wasPending and lastTime arguments
 
1035
 *
 
1036
 * ispending:       PR_TRUE if the event is currently pending
 
1037
 * starvationLimit: Threshold defined in milliseconds for determining when
 
1038
 *                  the event has been held in the queue too long
 
1039
 * wasPending:      PR_TRUE if the last time _md_EventIsStarved was called
 
1040
 *                  the event was pending.  This value is updated within
 
1041
 *                  this function.
 
1042
 * lastTime:        Holds the last time the event was in the queue.
 
1043
 *                  This value is updated within this function
 
1044
 * returns:         PR_TRUE if the event is starved, PR_FALSE otherwise
 
1045
 */
 
1046
 
 
1047
static PRBool _md_EventIsStarved(PRBool isPending, PRUint32 starvationLimit,
 
1048
                                 PRBool *wasPending, PRUint32 *lastTime,
 
1049
                                 PRUint32 currentTime)
 
1050
{
 
1051
    if (*wasPending && isPending) {
 
1052
        /*
 
1053
         * It was pending previously and the event is still
 
1054
         * pending so check to see if the elapsed time is
 
1055
         * over the limit which indicates the event was starved
 
1056
         */
 
1057
        if ((currentTime - *lastTime) > starvationLimit) {
 
1058
            return PR_TRUE; /* pending and over the limit */
 
1059
        }
 
1060
 
 
1061
        return PR_FALSE; /* pending but within the limit */
 
1062
    }
 
1063
 
 
1064
    if (isPending) {
 
1065
        /*
 
1066
         * was_pending must be false so record the current time
 
1067
         * so the elapsed time can be computed the next time this
 
1068
         * function is called
 
1069
         */
 
1070
        *lastTime = currentTime;
 
1071
        *wasPending = PR_TRUE;
 
1072
        return PR_FALSE;
 
1073
    }
 
1074
 
 
1075
    /* Event is no longer pending */
 
1076
    *wasPending = PR_FALSE;
 
1077
    return PR_FALSE;
 
1078
}
 
1079
 
 
1080
/* Determines if the there is a pending Mouse or input event */
 
1081
 
 
1082
static PRBool _md_IsInputPending(WORD qstatus)
 
1083
{
 
1084
    /* Return immediately there aren't any pending input or paints. */
 
1085
    if (qstatus == 0) {
 
1086
        return PR_FALSE;
 
1087
    }
 
1088
 
 
1089
    /* Is there anything other than a QS_MOUSEMOVE pending? */
 
1090
    if ((qstatus & QS_MOUSEBUTTON) ||
 
1091
        (qstatus & QS_KEY) ||
 
1092
        (qstatus & QS_HOTKEY)) {
 
1093
        return PR_TRUE;
 
1094
    }
 
1095
 
 
1096
    /*
 
1097
     * Mouse moves need extra processing to determine if the mouse
 
1098
     * pointer actually changed location because Windows automatically
 
1099
     * generates WM_MOVEMOVE events when a new window is created which
 
1100
     * we need to filter out.
 
1101
     */
 
1102
    if (qstatus & QS_MOUSEMOVE) {
 
1103
        POINT cursorPos;
 
1104
        GetCursorPos(&cursorPos);
 
1105
        if ((_md_LastMousePos.x == cursorPos.x) &&
 
1106
            (_md_LastMousePos.y == cursorPos.y)) {
 
1107
            return PR_FALSE; /* This is a fake mouse move */
 
1108
        }
 
1109
 
 
1110
        /* Real mouse move */
 
1111
        _md_LastMousePos.x = cursorPos.x;
 
1112
        _md_LastMousePos.y = cursorPos.y;
 
1113
        return PR_TRUE;
 
1114
    }
 
1115
 
 
1116
    return PR_FALSE;
 
1117
}
 
1118
 
 
1119
static PRStatus
 
1120
_pl_NativeNotify(PLEventQueue* self)
 
1121
{
 
1122
#ifdef USE_TIMER
 
1123
    WORD qstatus;
 
1124
 
 
1125
    PRUint32 now = PR_IntervalToMilliseconds(PR_IntervalNow());
 
1126
 
 
1127
    /* Since calls to set the _md_PerformanceSetting can be nested
 
1128
     * only performance setting values <= 0 will potentially trigger
 
1129
     * the use of a timer.
 
1130
     */
 
1131
    if ((_md_PerformanceSetting <= 0) &&
 
1132
        ((now - _md_SwitchTime) > _md_StarvationDelay)) {
 
1133
        SetTimer(self->eventReceiverWindow, TIMER_ID, 0 ,_md_TimerProc);
 
1134
        self->timerSet = PR_TRUE;
 
1135
        _md_WasInputPending = PR_FALSE;
 
1136
        _md_WasPaintPending = PR_FALSE;
 
1137
        return PR_SUCCESS;
 
1138
    }
 
1139
 
 
1140
    qstatus = HIWORD(GetQueueStatus(QS_INPUT | QS_PAINT));
 
1141
 
 
1142
    /* Check for starved input */
 
1143
    if (_md_EventIsStarved( _md_IsInputPending(qstatus),
 
1144
                            INPUT_STARVATION_LIMIT,
 
1145
                            &_md_WasInputPending,
 
1146
                            &_md_InputTime,
 
1147
                            now )) {
 
1148
        /*
 
1149
         * Use a timer for notification. Timers have the lowest priority.
 
1150
         * They are not processed until all other events have been processed.
 
1151
         * This allows any starved paints and input to be processed.
 
1152
         */
 
1153
        SetTimer(self->eventReceiverWindow, TIMER_ID, 0 ,_md_TimerProc);
 
1154
        self->timerSet = PR_TRUE;
 
1155
 
 
1156
        /*
 
1157
         * Clear any pending paint.  _md_WasInputPending was cleared in
 
1158
         * _md_EventIsStarved.
 
1159
         */
 
1160
        _md_WasPaintPending = PR_FALSE;
 
1161
        return PR_SUCCESS;
 
1162
    }
 
1163
 
 
1164
    if (_md_EventIsStarved( (qstatus & QS_PAINT),
 
1165
                            _md_GetPaintStarvationLimit(),
 
1166
                            &_md_WasPaintPending,
 
1167
                            &_md_PaintTime,
 
1168
                            now) ) {
 
1169
        /*
 
1170
         * Use a timer for notification. Timers have the lowest priority.
 
1171
         * They are not processed until all other events have been processed.
 
1172
         * This allows any starved paints and input to be processed
 
1173
         */
 
1174
        SetTimer(self->eventReceiverWindow, TIMER_ID, 0 ,_md_TimerProc);
 
1175
        self->timerSet = PR_TRUE;
 
1176
 
 
1177
        /*
 
1178
         * Clear any pending input.  _md_WasPaintPending was cleared in
 
1179
         * _md_EventIsStarved.
 
1180
         */
 
1181
        _md_WasInputPending = PR_FALSE;
 
1182
        return PR_SUCCESS;
 
1183
    }
 
1184
 
 
1185
    /*
 
1186
     * Nothing is being starved so post a message instead of using a timer.
 
1187
     * Posted messages are processed before other messages so they have the
 
1188
     * highest priority.
 
1189
     */
 
1190
#endif
 
1191
    PostMessage( self->eventReceiverWindow, _pr_PostEventMsgId,
 
1192
                (WPARAM)0, (LPARAM)self );
 
1193
 
 
1194
    return PR_SUCCESS;
 
1195
}/* --- end _pl_NativeNotify() --- */
 
1196
#endif
 
1197
 
 
1198
 
 
1199
#if defined(XP_OS2)
 
1200
static PRStatus
 
1201
_pl_NativeNotify(PLEventQueue* self)
 
1202
{
 
1203
    BOOL rc = WinPostMsg( self->eventReceiverWindow, _pr_PostEventMsgId,
 
1204
                          0, MPFROMP(self));
 
1205
    return (rc == TRUE) ? PR_SUCCESS : PR_FAILURE;
 
1206
}/* --- end _pl_NativeNotify() --- */
 
1207
#endif /* XP_OS2 */
 
1208
 
 
1209
#if defined(VMS)
 
1210
/* Just set the event flag */
 
1211
static PRStatus
 
1212
_pl_NativeNotify(PLEventQueue* self)
 
1213
{
 
1214
    unsigned int status;
 
1215
    PR_LOG(event_lm, PR_LOG_DEBUG,
 
1216
           ("_pl_NativeNotify: self=%p efn=%d",
 
1217
            self, self->efn));
 
1218
    status = SYS$SETEF(self->efn);
 
1219
    return ($VMS_STATUS_SUCCESS(status)) ? PR_SUCCESS : PR_FAILURE;
 
1220
}/* --- end _pl_NativeNotify() --- */
 
1221
#elif defined(XP_UNIX) && !defined(XP_MACOSX)
 
1222
 
 
1223
static PRStatus
 
1224
_pl_NativeNotify(PLEventQueue* self)
 
1225
{
 
1226
#define NOTIFY_TOKEN    0xFA
 
1227
    PRInt32 count;
 
1228
    unsigned char buf[] = { NOTIFY_TOKEN };
 
1229
 
 
1230
    PR_LOG(event_lm, PR_LOG_DEBUG,
 
1231
           ("_pl_NativeNotify: self=%p",
 
1232
            self));
 
1233
    count = write(self->eventPipe[1], buf, 1);
 
1234
    if (count == 1)
 
1235
        return PR_SUCCESS;
 
1236
    if (count == -1 && (errno == EAGAIN || errno == EWOULDBLOCK))
 
1237
        return PR_SUCCESS;
 
1238
    return PR_FAILURE;
 
1239
}/* --- end _pl_NativeNotify() --- */
 
1240
#endif /* defined(XP_UNIX) && !defined(XP_MACOSX) */
 
1241
 
 
1242
#if defined(XP_BEOS)
 
1243
struct ThreadInterfaceData
 
1244
{
 
1245
    void  *data;
 
1246
    int32 sync;
 
1247
};
 
1248
 
 
1249
static PRStatus
 
1250
_pl_NativeNotify(PLEventQueue* self)
 
1251
{
 
1252
    struct ThreadInterfaceData id;
 
1253
    id.data = self;
 
1254
    id.sync = false;
 
1255
    write_port(self->eventport, 'natv', &id, sizeof(id));
 
1256
 
 
1257
    return PR_SUCCESS;    /* Is this correct? */
 
1258
}
 
1259
#endif /* XP_BEOS */
 
1260
 
 
1261
#if defined(XP_MAC) || defined(XP_MACOSX)
 
1262
static PRStatus
 
1263
_pl_NativeNotify(PLEventQueue* self)
 
1264
{
 
1265
#if defined(MAC_USE_CARBON_EVENT)
 
1266
    OSErr err;
 
1267
    EventRef newEvent;
 
1268
    if (CreateEvent(NULL, kEventClassPL, kEventProcessPLEvents,
 
1269
                    0, kEventAttributeNone, &newEvent) != noErr)
 
1270
        return PR_FAILURE;
 
1271
    err = SetEventParameter(newEvent, kEventParamPLEventQueue,
 
1272
                            typeUInt32, sizeof(PREventQueue*), &self);
 
1273
    if (err == noErr) {
 
1274
        err = PostEventToQueue(GetMainEventQueue(), newEvent, kEventPriorityLow);
 
1275
        ReleaseEvent(newEvent);
 
1276
    }
 
1277
    if (err != noErr)
 
1278
        return PR_FAILURE;
 
1279
#elif defined(MAC_USE_WAKEUPPROCESS)
 
1280
    WakeUpProcess(&self->psn);
 
1281
#endif
 
1282
    return PR_SUCCESS;
 
1283
}
 
1284
#endif /* defined(XP_MAC) || defined(XP_MACOSX) */
 
1285
 
 
1286
static PRStatus
 
1287
_pl_AcknowledgeNativeNotify(PLEventQueue* self)
 
1288
{
 
1289
#if defined(_WIN32) || defined(XP_OS2)
 
1290
#ifdef XP_OS2
 
1291
    QMSG aMsg;
 
1292
#else
 
1293
    MSG aMsg;
 
1294
#endif
 
1295
    /*
 
1296
     * only remove msg when we've been called directly by
 
1297
     * PL_ProcessPendingEvents, not when we've been called by
 
1298
     * the window proc because the window proc will remove the
 
1299
     * msg for us.
 
1300
     */
 
1301
    if (self->removeMsg) {
 
1302
        PR_LOG(event_lm, PR_LOG_DEBUG,
 
1303
               ("_pl_AcknowledgeNativeNotify: self=%p", self));
 
1304
#ifdef XP_OS2
 
1305
        WinPeekMsg((HAB)0, &aMsg, self->eventReceiverWindow,
 
1306
                   _pr_PostEventMsgId, _pr_PostEventMsgId, PM_REMOVE);
 
1307
#else
 
1308
        PeekMessage(&aMsg, self->eventReceiverWindow,
 
1309
                    _pr_PostEventMsgId, _pr_PostEventMsgId, PM_REMOVE);
 
1310
        if (self->timerSet) {
 
1311
            KillTimer(self->eventReceiverWindow, TIMER_ID);
 
1312
            self->timerSet = PR_FALSE;
 
1313
        }
 
1314
#endif
 
1315
    }
 
1316
    return PR_SUCCESS;
 
1317
#elif defined(VMS)
 
1318
    PR_LOG(event_lm, PR_LOG_DEBUG,
 
1319
            ("_pl_AcknowledgeNativeNotify: self=%p efn=%d",
 
1320
             self, self->efn));
 
1321
    /*
 
1322
    ** If this is the last entry, then clear the event flag. Also make sure
 
1323
    ** the flag is cleared on any spurious wakeups.
 
1324
    */
 
1325
    sys$clref(self->efn);
 
1326
    return PR_SUCCESS;
 
1327
#elif defined(XP_UNIX) && !defined(XP_MACOSX)
 
1328
 
 
1329
    PRInt32 count;
 
1330
    unsigned char c;
 
1331
    PR_LOG(event_lm, PR_LOG_DEBUG,
 
1332
            ("_pl_AcknowledgeNativeNotify: self=%p",
 
1333
             self));
 
1334
    /* consume the byte NativeNotify put in our pipe: */
 
1335
    count = read(self->eventPipe[0], &c, 1);
 
1336
    if ((count == 1) && (c == NOTIFY_TOKEN))
 
1337
        return PR_SUCCESS;
 
1338
    if ((count == -1) && ((errno == EAGAIN) || (errno == EWOULDBLOCK)))
 
1339
        return PR_SUCCESS;
 
1340
    return PR_FAILURE;
 
1341
#else
 
1342
 
 
1343
#if defined(XP_MAC)
 
1344
#pragma unused (self)
 
1345
#endif
 
1346
 
 
1347
    /* nothing to do on the other platforms */
 
1348
    return PR_SUCCESS;
 
1349
#endif
 
1350
}
 
1351
 
 
1352
PR_IMPLEMENT(PRInt32)
 
1353
PL_GetEventQueueSelectFD(PLEventQueue* self)
 
1354
{
 
1355
    if (self == NULL)
 
1356
    return -1;
 
1357
 
 
1358
#if defined(VMS)
 
1359
    return -(self->efn);
 
1360
#elif defined(XP_UNIX) && !defined(XP_MACOSX)
 
1361
    return self->eventPipe[0];
 
1362
#else
 
1363
    return -1;    /* other platforms don't handle this (yet) */
 
1364
#endif
 
1365
}
 
1366
 
 
1367
PR_IMPLEMENT(PRBool)
 
1368
PL_IsQueueOnCurrentThread( PLEventQueue *queue )
 
1369
{
 
1370
    PRThread *me = PR_GetCurrentThread();
 
1371
    return me == queue->handlerThread;
 
1372
}
 
1373
 
 
1374
PR_EXTERN(PRBool)
 
1375
PL_IsQueueNative(PLEventQueue *queue)
 
1376
{
 
1377
    return queue->type == EventQueueIsNative ? PR_TRUE : PR_FALSE;
 
1378
}
 
1379
 
 
1380
#if defined(_WIN32)
 
1381
/*
 
1382
** Global Instance handle...
 
1383
** In Win32 this is the module handle of the DLL.
 
1384
**
 
1385
*/
 
1386
static HINSTANCE _pr_hInstance;
 
1387
#endif
 
1388
 
 
1389
 
 
1390
#if defined(_WIN32)
 
1391
 
 
1392
/*
 
1393
** Initialization routine for the DLL...
 
1394
*/
 
1395
 
 
1396
BOOL WINAPI DllMain (HINSTANCE hDLL, DWORD dwReason, LPVOID lpReserved)
 
1397
{
 
1398
    switch (dwReason)
 
1399
    {
 
1400
      case DLL_PROCESS_ATTACH:
 
1401
        _pr_hInstance = hDLL;
 
1402
        break;
 
1403
 
 
1404
      case DLL_THREAD_ATTACH:
 
1405
        break;
 
1406
 
 
1407
      case DLL_THREAD_DETACH:
 
1408
        break;
 
1409
 
 
1410
      case DLL_PROCESS_DETACH:
 
1411
        _pr_hInstance = NULL;
 
1412
        break;
 
1413
    }
 
1414
 
 
1415
    return TRUE;
 
1416
}
 
1417
#endif
 
1418
 
 
1419
 
 
1420
#if defined(_WIN32) || defined(XP_OS2)
 
1421
#ifdef XP_OS2
 
1422
MRESULT EXPENTRY
 
1423
_md_EventReceiverProc(HWND hwnd, ULONG uMsg, MPARAM wParam, MPARAM lParam)
 
1424
#else
 
1425
LRESULT CALLBACK
 
1426
_md_EventReceiverProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
 
1427
#endif
 
1428
{
 
1429
    if (_pr_PostEventMsgId == uMsg )
 
1430
    {
 
1431
        PREventQueue *queue = (PREventQueue *)lParam;
 
1432
        queue->removeMsg = PR_FALSE;
 
1433
        PL_ProcessPendingEvents(queue);
 
1434
        queue->removeMsg = PR_TRUE;
 
1435
#ifdef XP_OS2
 
1436
        return MRFROMLONG(TRUE);
 
1437
#else
 
1438
        return TRUE;
 
1439
#endif
 
1440
    }
 
1441
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
 
1442
}
 
1443
 
 
1444
static PRBool   isInitialized;
 
1445
static PRCallOnceType once;
 
1446
static PRLock   *initLock;
 
1447
 
 
1448
/*
 
1449
** InitWinEventLib() -- Create the Windows initialization lock
 
1450
**
 
1451
*/
 
1452
static PRStatus InitEventLib( void )
 
1453
{
 
1454
    PR_ASSERT( initLock == NULL );
 
1455
 
 
1456
    initLock = PR_NewLock();
 
1457
    return initLock ? PR_SUCCESS : PR_FAILURE;
 
1458
}
 
1459
 
 
1460
#endif /* Win32, OS2 */
 
1461
 
 
1462
#if defined(_WIN32)
 
1463
 
 
1464
/*
 
1465
** _md_CreateEventQueue() -- ModelDependent initializer
 
1466
*/
 
1467
static void _md_CreateEventQueue( PLEventQueue *eventQueue )
 
1468
{
 
1469
    WNDCLASS wc;
 
1470
 
 
1471
    /*
 
1472
    ** If this is the first call to PL_InitializeEventsLib(),
 
1473
    ** make the call to InitWinEventLib() to create the initLock.
 
1474
    **
 
1475
    ** Then lock the initializer lock to insure that
 
1476
    ** we have exclusive control over the initialization sequence.
 
1477
    **
 
1478
    */
 
1479
 
 
1480
 
 
1481
    /* Register the windows message for XPCOM Event notification */
 
1482
    _pr_PostEventMsgId = RegisterWindowMessage("XPCOM_PostEvent");
 
1483
 
 
1484
    /* Register the class for the event receiver window */
 
1485
    if (!GetClassInfo(_pr_hInstance, _pr_eventWindowClass, &wc)) {
 
1486
        wc.style         = 0;
 
1487
        wc.lpfnWndProc   = _md_EventReceiverProc;
 
1488
        wc.cbClsExtra    = 0;
 
1489
        wc.cbWndExtra    = 0;
 
1490
        wc.hInstance     = _pr_hInstance;
 
1491
        wc.hIcon         = NULL;
 
1492
        wc.hCursor       = NULL;
 
1493
        wc.hbrBackground = (HBRUSH) NULL;
 
1494
        wc.lpszMenuName  = (LPCSTR) NULL;
 
1495
        wc.lpszClassName = _pr_eventWindowClass;
 
1496
        RegisterClass(&wc);
 
1497
    }
 
1498
 
 
1499
    /* Create the event receiver window */
 
1500
    eventQueue->eventReceiverWindow = CreateWindow(_pr_eventWindowClass,
 
1501
                                        "XPCOM:EventReceiver",
 
1502
                                            0, 0, 0, 10, 10,
 
1503
                                            NULL, NULL, _pr_hInstance,
 
1504
                                            NULL);
 
1505
    PR_ASSERT(eventQueue->eventReceiverWindow);
 
1506
    /* Set a property which can be used to retrieve the event queue
 
1507
     * within the _md_TimerProc callback
 
1508
     */
 
1509
    SetProp(eventQueue->eventReceiverWindow,
 
1510
            _md_GetEventQueuePropName(), (HANDLE)eventQueue);
 
1511
 
 
1512
    return;
 
1513
} /* end _md_CreateEventQueue() */
 
1514
#endif /* Winxx */
 
1515
 
 
1516
#if defined(XP_OS2)
 
1517
/*
 
1518
** _md_CreateEventQueue() -- ModelDependent initializer
 
1519
*/
 
1520
static void _md_CreateEventQueue( PLEventQueue *eventQueue )
 
1521
{
 
1522
    /* Must have HMQ for this & can't assume we already have appshell */
 
1523
    if( FALSE == WinQueryQueueInfo( HMQ_CURRENT, NULL, 0))
 
1524
    {
 
1525
       PPIB ppib;
 
1526
       PTIB ptib;
 
1527
       HAB hab;
 
1528
       HMQ hmq;
 
1529
 
 
1530
       /* Set our app to be a PM app before attempting Win calls */
 
1531
       DosGetInfoBlocks(&ptib, &ppib);
 
1532
       ppib->pib_ultype = 3;
 
1533
 
 
1534
       hab = WinInitialize(0);
 
1535
       hmq = WinCreateMsgQueue(hab, 0);
 
1536
       PR_ASSERT(hmq);
 
1537
    }
 
1538
 
 
1539
    if( !_pr_PostEventMsgId)
 
1540
    {
 
1541
        WinRegisterClass( 0 /* hab_current */,
 
1542
                         _pr_eventWindowClass,
 
1543
                         _md_EventReceiverProc,
 
1544
                         0, 0);
 
1545
 
 
1546
        _pr_PostEventMsgId = WinAddAtom( WinQuerySystemAtomTable(),
 
1547
                                        "XPCOM_PostEvent");
 
1548
    }
 
1549
 
 
1550
    eventQueue->eventReceiverWindow = WinCreateWindow( HWND_DESKTOP,
 
1551
                                                       _pr_eventWindowClass,
 
1552
                                                       "", 0,
 
1553
                                                       0, 0, 0, 0,
 
1554
                                                       HWND_DESKTOP,
 
1555
                                                       HWND_TOP,
 
1556
                                                       0,
 
1557
                                                       NULL,
 
1558
                                                       NULL);
 
1559
    PR_ASSERT(eventQueue->eventReceiverWindow);
 
1560
 
 
1561
    return;
 
1562
} /* end _md_CreateEventQueue() */
 
1563
#endif /* XP_OS2 */
 
1564
 
 
1565
#if (defined(XP_UNIX) && !defined(XP_MACOSX)) || defined(XP_BEOS)
 
1566
/*
 
1567
** _md_CreateEventQueue() -- ModelDependent initializer
 
1568
*/
 
1569
static void _md_CreateEventQueue( PLEventQueue *eventQueue )
 
1570
{
 
1571
    /* there's really nothing special to do here,
 
1572
    ** the guts of the unix stuff is in the setupnativenotify
 
1573
    ** and related functions.
 
1574
    */
 
1575
    return;
 
1576
} /* end _md_CreateEventQueue() */
 
1577
#endif /* (defined(XP_UNIX) && !defined(XP_MACOSX)) || defined(XP_BEOS) */
 
1578
 
 
1579
#if defined(MAC_USE_CARBON_EVENT)
 
1580
/*
 
1581
** _md_CreateEventQueue() -- ModelDependent initializer
 
1582
*/
 
1583
 
 
1584
static pascal OSStatus _md_EventReceiverProc(EventHandlerCallRef nextHandler,
 
1585
                                             EventRef inEvent,
 
1586
                                             void* userData)
 
1587
{
 
1588
    if (GetEventClass(inEvent) == kEventClassPL &&
 
1589
        GetEventKind(inEvent) == kEventProcessPLEvents)
 
1590
    {
 
1591
        PREventQueue *queue;
 
1592
        if (GetEventParameter(inEvent, kEventParamPLEventQueue,
 
1593
                              typeUInt32, NULL, sizeof(PREventQueue*), NULL,
 
1594
                              &queue) == noErr)
 
1595
        {
 
1596
            PL_ProcessPendingEvents(queue);
 
1597
            return noErr;
 
1598
        }
 
1599
    }
 
1600
    return eventNotHandledErr;
 
1601
}
 
1602
 
 
1603
static pascal Boolean _md_CarbonEventComparator(EventRef inEvent,
 
1604
                                                void *inCompareData)
 
1605
{
 
1606
    Boolean match = false;
 
1607
 
 
1608
    if (GetEventClass(inEvent) == kEventClassPL &&
 
1609
        GetEventKind(inEvent) == kEventProcessPLEvents)
 
1610
    {
 
1611
        PREventQueue *queue;
 
1612
        match = ((GetEventParameter(inEvent, kEventParamPLEventQueue,
 
1613
                                    typeUInt32, NULL, sizeof(PREventQueue*), NULL,
 
1614
                                    &queue) == noErr) && (queue == inCompareData));
 
1615
    }
 
1616
    return match;
 
1617
}
 
1618
 
 
1619
#endif /* defined(MAC_USE_CARBON_EVENT) */
 
1620
 
 
1621
#if defined(XP_MAC) || defined(XP_MACOSX)
 
1622
static void _md_CreateEventQueue( PLEventQueue *eventQueue )
 
1623
{
 
1624
#if defined(MAC_USE_CARBON_EVENT)
 
1625
    eventQueue->eventHandlerUPP = NewEventHandlerUPP(_md_EventReceiverProc);
 
1626
    PR_ASSERT(eventQueue->eventHandlerUPP);
 
1627
    if (eventQueue->eventHandlerUPP)
 
1628
    {
 
1629
      EventTypeSpec     eventType;
 
1630
 
 
1631
      eventType.eventClass = kEventClassPL;
 
1632
      eventType.eventKind  = kEventProcessPLEvents;
 
1633
 
 
1634
      InstallApplicationEventHandler(eventQueue->eventHandlerUPP, 1, &eventType,
 
1635
                                     eventQueue, &eventQueue->eventHandlerRef);
 
1636
      PR_ASSERT(eventQueue->eventHandlerRef);
 
1637
    }
 
1638
#elif defined(MAC_USE_WAKEUPPROCESS)
 
1639
    OSErr err = GetCurrentProcess(&eventQueue->psn);
 
1640
    PR_ASSERT(err == noErr);
 
1641
#endif
 
1642
} /* end _md_CreateEventQueue() */
 
1643
#endif /* defined(XP_MAC) || defined(XP_MACOSX) */
 
1644
 
 
1645
/* extra functions for unix */
 
1646
 
 
1647
#if defined(XP_UNIX) && !defined(XP_MACOSX)
 
1648
 
 
1649
PR_IMPLEMENT(PRInt32)
 
1650
PL_ProcessEventsBeforeID(PLEventQueue *aSelf, unsigned long aID)
 
1651
{
 
1652
    PRInt32 count = 0;
 
1653
    PRInt32 fullCount;
 
1654
 
 
1655
    if (aSelf == NULL)
 
1656
        return -1;
 
1657
 
 
1658
    PR_EnterMonitor(aSelf->monitor);
 
1659
 
 
1660
    if (aSelf->processingEvents) {
 
1661
        PR_ExitMonitor(aSelf->monitor);
 
1662
        return 0;
 
1663
    }
 
1664
 
 
1665
    aSelf->processingEvents = PR_TRUE;
 
1666
 
 
1667
    /* Only process the events that are already in the queue, and
 
1668
     * not any new events that get added. Do this by counting the
 
1669
     * number of events currently in the queue
 
1670
     */
 
1671
    fullCount = _pl_GetEventCount(aSelf);
 
1672
    PR_LOG(event_lm, PR_LOG_DEBUG,
 
1673
           ("$$$ fullCount is %d id is %ld\n", fullCount, aID));
 
1674
 
 
1675
    if (fullCount == 0) {
 
1676
        aSelf->processingEvents = PR_FALSE;
 
1677
        PR_ExitMonitor(aSelf->monitor);
 
1678
        return 0;
 
1679
    }
 
1680
 
 
1681
    PR_ExitMonitor(aSelf->monitor);
 
1682
 
 
1683
    while (fullCount-- > 0) {
 
1684
        /* peek at the next event */
 
1685
        PLEvent *event;
 
1686
        event = PR_EVENT_PTR(aSelf->queue.next);
 
1687
        if (event == NULL)
 
1688
            break;
 
1689
        PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ processing event %ld\n",
 
1690
                                        event->id));
 
1691
        if (event->id >= aID) {
 
1692
            PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ skipping event and breaking"));
 
1693
            break;
 
1694
        }
 
1695
 
 
1696
        event = PL_GetEvent(aSelf);
 
1697
        PL_HandleEvent(event);
 
1698
        PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ done processing event"));
 
1699
        count++;
 
1700
    }
 
1701
 
 
1702
    PR_EnterMonitor(aSelf->monitor);
 
1703
 
 
1704
    /* if full count still had items left then there's still items left
 
1705
       in the queue.  Let the native notify token stay. */
 
1706
 
 
1707
    if (aSelf->type == EventQueueIsNative) {
 
1708
        fullCount = _pl_GetEventCount(aSelf);
 
1709
 
 
1710
        if (fullCount <= 0) {
 
1711
            _pl_AcknowledgeNativeNotify(aSelf);
 
1712
            aSelf->notified = PR_FALSE;
 
1713
        }
 
1714
    }
 
1715
 
 
1716
    aSelf->processingEvents = PR_FALSE;
 
1717
 
 
1718
    PR_ExitMonitor(aSelf->monitor);
 
1719
 
 
1720
    return count;
 
1721
}
 
1722
 
 
1723
PR_IMPLEMENT(void)
 
1724
PL_RegisterEventIDFunc(PLEventQueue *aSelf, PLGetEventIDFunc aFunc,
 
1725
                       void *aClosure)
 
1726
{
 
1727
    aSelf->idFunc = aFunc;
 
1728
    aSelf->idFuncClosure = aClosure;
 
1729
}
 
1730
 
 
1731
PR_IMPLEMENT(void)
 
1732
PL_UnregisterEventIDFunc(PLEventQueue *aSelf)
 
1733
{
 
1734
    aSelf->idFunc = 0;
 
1735
    aSelf->idFuncClosure = 0;
 
1736
}
 
1737
 
 
1738
#endif /* defined(XP_UNIX) && !defined(XP_MACOSX) */
 
1739
 
 
1740
/* --- end plevent.c --- */