1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* ***** BEGIN LICENSE BLOCK *****
3
* Version: NPL 1.1/GPL 2.0/LGPL 2.1
5
* The contents of this file are subject to the Netscape Public License
6
* Version 1.1 (the "License"); you may not use this file except in
7
* compliance with the License. You may obtain a copy of the License at
8
* http://www.mozilla.org/NPL/
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
15
* The Original Code is Mozilla Communicator client code.
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.
24
* Alternatively, the contents of this file may be used under the terms of
25
* either the GNU General Public License Version 2 or later (the "GPL"), or
26
* 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 NPL, 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 NPL, the GPL or the LGPL.
36
* ***** END LICENSE BLOCK ***** */
39
#include "nsEventQueue.h"
40
#include "nsIEventQueueService.h"
41
#include "nsIThread.h"
43
#include "nsIServiceManager.h"
44
#include "nsIObserverService.h"
54
#if defined(PR_LOGGING) && defined(DEBUG_danm)
55
/* found these logs useful in conjunction with netlibStreamEvent logging
57
PRLogModuleInfo* gEventQueueLog = 0;
58
PRUint32 gEventQueueLogCount = 0;
59
PRUint32 gEventQueueLogPPCount = 0;
60
static int gEventQueueLogPPLevel = 0;
61
static PLEventQueue *gEventQueueLogQueue = 0;
62
static PRThread *gEventQueueLogThread = 0;
65
// in a real system, these would be members in a header class...
66
static const char gActivatedNotification[] = "nsIEventQueueActivated";
67
static const char gDestroyedNotification[] = "nsIEventQueueDestroyed";
69
nsEventQueueImpl::nsEventQueueImpl()
72
/* The slightly weird ownership model for eventqueues goes like this:
75
There's an addref from the factory generally held by whoever asked for
76
the queue. The queue addrefs itself (right here) and releases itself
77
after someone calls StopAcceptingEvents() on the queue and when it is
78
dark and empty (in CheckForDeactivation()).
83
The eldest queue in a chain is held on to by the EventQueueService
84
in a hash table, so it is possible that the eldest queue may not be
85
released until the EventQueueService is shutdown.
86
You may not call StopAcceptingEvents() on this queue until you have
87
done so on all younger queues.
90
Each queue holds a reference to their immediate elder link and a weak
91
reference to their immediate younger link. Because you must shut down
92
queues from youngest to eldest, all the references will be removed.
94
It happens something like:
95
queue->StopAcceptingEvents()
97
CheckForDeactivation()
99
-- hopefully we are able to shutdown now --
102
-- remove the reference we hold to our elder queue --
103
-- NULL out our elder queues weak reference to us --
105
RELEASE ourself (to balance the ADDREF here in the constructor)
106
-- and we should go away. --
112
A dark queue no longer accepts events. An empty queue simply has no events.
115
#if defined(PR_LOGGING) && defined(DEBUG_danm)
116
PR_LOG(gEventQueueLog, PR_LOG_DEBUG,
117
("EventQueue: Created [queue=%lx]",(long)mEventQueue));
118
++gEventQueueLogCount;
121
mYoungerQueue = nsnull;
122
mEventQueue = nsnull;
123
mAcceptingEvents = PR_TRUE;
124
mCouldHaveEvents = PR_TRUE;
127
nsEventQueueImpl::~nsEventQueueImpl()
131
#if defined(PR_LOGGING) && defined(DEBUG_danm)
132
PR_LOG(gEventQueueLog, PR_LOG_DEBUG,
133
("EventQueue: Destroyed [queue=%lx]",(long)mEventQueue));
134
++gEventQueueLogCount;
138
NotifyObservers(gDestroyedNotification);
139
PL_DestroyEventQueue(mEventQueue);
144
nsEventQueueImpl::Init(PRBool aNative)
146
PRThread *thread = PR_GetCurrentThread();
148
mEventQueue = PL_CreateNativeEventQueue("Thread event queue...", thread);
150
mEventQueue = PL_CreateMonitoredEventQueue("Thread event queue...", thread);
151
NotifyObservers(gActivatedNotification);
156
nsEventQueueImpl::InitFromPRThread(PRThread* thread, PRBool aNative)
158
if (thread == NS_CURRENT_THREAD)
160
thread = PR_GetCurrentThread();
162
else if (thread == NS_UI_THREAD)
164
nsCOMPtr<nsIThread> mainIThread;
167
// Get the primordial thread
168
rv = nsIThread::GetMainThread(getter_AddRefs(mainIThread));
169
if (NS_FAILED(rv)) return rv;
171
rv = mainIThread->GetPRThread(&thread);
172
if (NS_FAILED(rv)) return rv;
176
mEventQueue = PL_CreateNativeEventQueue("Thread event queue...", thread);
178
mEventQueue = PL_CreateMonitoredEventQueue("Thread event queue...", thread);
179
NotifyObservers(gActivatedNotification);
184
nsEventQueueImpl::InitFromPLQueue(PLEventQueue* aQueue)
186
mEventQueue = aQueue;
187
NotifyObservers(gActivatedNotification);
191
/* nsISupports interface implementation... */
192
NS_IMPL_THREADSAFE_ISUPPORTS3(nsEventQueueImpl,
197
/* nsIEventQueue interface implementation... */
200
nsEventQueueImpl::StopAcceptingEvents()
202
// this assertion is bogus. I should be able to shut down the eldest queue,
203
// as long as there are no younger children
206
NS_ASSERTION(mElderQueue || !mYoungerQueue, "attempted to disable eldest queue in chain");
207
mAcceptingEvents = PR_FALSE;
208
CheckForDeactivation();
209
#if defined(PR_LOGGING) && defined(DEBUG_danm)
210
PR_LOG(gEventQueueLog, PR_LOG_DEBUG,
211
("EventQueue: StopAccepting [queue=%lx, accept=%d, could=%d]",
212
(long)mEventQueue,(int)mAcceptingEvents,(int)mCouldHaveEvents));
213
++gEventQueueLogCount;
218
// utility funtion to send observers a notification
220
nsEventQueueImpl::NotifyObservers(const char *aTopic)
224
nsCOMPtr<nsIObserverService> os = do_GetService("@mozilla.org/observer-service;1", &rv);
225
if (NS_SUCCEEDED(rv)) {
226
nsCOMPtr<nsIEventQueue> kungFuDeathGrip(this);
227
nsCOMPtr<nsISupports> us(do_QueryInterface(kungFuDeathGrip));
228
os->NotifyObservers(us, aTopic, NULL);
234
nsEventQueueImpl::InitEvent(PLEvent* aEvent,
236
PLHandleEventProc handler,
237
PLDestroyEventProc destructor)
239
PL_InitEvent(aEvent, owner, handler, destructor);
245
nsEventQueueImpl::PostEvent(PLEvent* aEvent)
247
if (!mAcceptingEvents) {
248
#if defined(PR_LOGGING) && defined(DEBUG_danm)
249
PR_LOG(gEventQueueLog, PR_LOG_DEBUG,
250
("EventQueue: Punt posted event [queue=%lx, accept=%d, could=%d]",
251
(long)mEventQueue,(int)mAcceptingEvents,(int)mCouldHaveEvents));
252
++gEventQueueLogCount;
254
nsresult rv = NS_ERROR_FAILURE;
255
NS_ASSERTION(mElderQueue, "event dropped because event chain is dead");
257
nsCOMPtr<nsIEventQueue> elder(do_QueryInterface(mElderQueue));
259
rv = elder->PostEvent(aEvent);
263
#if defined(PR_LOGGING) && defined(DEBUG_danm)
264
PR_LOG(gEventQueueLog, PR_LOG_DEBUG,
265
("EventQueue: Posting event [queue=%lx]", (long)mEventQueue));
266
++gEventQueueLogCount;
268
return PL_PostEvent(mEventQueue, aEvent) == PR_SUCCESS ? NS_OK : NS_ERROR_FAILURE;
272
nsEventQueueImpl::PostSynchronousEvent(PLEvent* aEvent, void** aResult)
274
if (!mAcceptingEvents) {
275
#if defined(PR_LOGGING) && defined(DEBUG_danm)
276
PR_LOG(gEventQueueLog, PR_LOG_DEBUG,
277
("EventQueue: Punt posted synchronous event [queue=%lx, accept=%d, could=%d]",
278
(long)mEventQueue,(int)mAcceptingEvents,(int)mCouldHaveEvents));
279
++gEventQueueLogCount;
281
nsresult rv = NS_ERROR_NO_INTERFACE;
282
NS_ASSERTION(mElderQueue, "event dropped because event chain is dead");
284
nsCOMPtr<nsIEventQueue> elder(do_QueryInterface(mElderQueue));
286
rv = elder->PostSynchronousEvent(aEvent, aResult);
289
return NS_ERROR_ABORT;
292
#if defined(PR_LOGGING) && defined(DEBUG_danm)
293
PR_LOG(gEventQueueLog, PR_LOG_DEBUG,
294
("EventQueue: Posting synchronous event [queue=%lx]", (long)mEventQueue));
295
++gEventQueueLogCount;
297
void* result = PL_PostSynchronousEvent(mEventQueue, aEvent);
305
nsEventQueueImpl::EnterMonitor()
307
PL_ENTER_EVENT_QUEUE_MONITOR(mEventQueue);
312
nsEventQueueImpl::ExitMonitor()
314
PL_EXIT_EVENT_QUEUE_MONITOR(mEventQueue);
320
nsEventQueueImpl::RevokeEvents(void* owner)
322
PL_RevokeEvents(mEventQueue, owner);
324
nsCOMPtr<nsIEventQueue> elder(do_QueryInterface(mElderQueue));
326
elder->RevokeEvents(owner);
333
nsEventQueueImpl::GetPLEventQueue(PLEventQueue** aEventQueue)
336
return NS_ERROR_NULL_POINTER;
338
*aEventQueue = mEventQueue;
343
nsEventQueueImpl::IsOnCurrentThread(PRBool *aResult)
345
*aResult = PL_IsQueueOnCurrentThread( mEventQueue );
351
nsEventQueueImpl::IsQueueNative(PRBool *aResult)
353
*aResult = PL_IsQueueNative(mEventQueue);
358
nsEventQueueImpl::PendingEvents(PRBool *aResult)
360
*aResult = PL_EventAvailable(mEventQueue);
361
if (!*aResult && mElderQueue) {
362
nsCOMPtr<nsIEventQueue> elder(do_QueryInterface(mElderQueue));
364
return elder->EventAvailable(*aResult);
371
nsEventQueueImpl::ProcessPendingEvents()
373
PRBool correctThread = PL_IsQueueOnCurrentThread(mEventQueue);
375
NS_ASSERTION(correctThread, "attemping to process events on the wrong thread");
378
return NS_ERROR_FAILURE;
379
#if defined(PR_LOGGING) && defined(DEBUG_danm)
380
++gEventQueueLogPPLevel;
381
if ((gEventQueueLogQueue != mEventQueue || gEventQueueLogThread != PR_GetCurrentThread() ||
382
gEventQueueLogCount != gEventQueueLogPPCount) && gEventQueueLogPPLevel == 1) {
383
PR_LOG(gEventQueueLog, PR_LOG_DEBUG,
384
("EventQueue: Process pending [queue=%lx, accept=%d, could=%d]",
385
(long)mEventQueue,(int)mAcceptingEvents,(int)mCouldHaveEvents));
386
gEventQueueLogPPCount = ++gEventQueueLogCount;
387
gEventQueueLogQueue = mEventQueue;
388
gEventQueueLogThread = PR_GetCurrentThread();
391
PL_ProcessPendingEvents(mEventQueue);
393
// if we're no longer accepting events and there are still events in the
394
// queue, then process remaining events.
395
if (!mAcceptingEvents && PL_EventAvailable(mEventQueue))
396
PL_ProcessPendingEvents(mEventQueue);
398
CheckForDeactivation();
401
nsCOMPtr<nsIEventQueue> elder(do_QueryInterface(mElderQueue));
403
elder->ProcessPendingEvents();
405
#if defined(PR_LOGGING) && defined(DEBUG_danm)
406
--gEventQueueLogPPLevel;
412
nsEventQueueImpl::EventLoop()
414
PRBool correctThread = PL_IsQueueOnCurrentThread(mEventQueue);
416
NS_ASSERTION(correctThread, "attemping to process events on the wrong thread");
419
return NS_ERROR_FAILURE;
421
PL_EventLoop(mEventQueue);
426
nsEventQueueImpl::EventAvailable(PRBool& aResult)
428
aResult = PL_EventAvailable(mEventQueue);
433
nsEventQueueImpl::GetEvent(PLEvent** aResult)
435
*aResult = PL_GetEvent(mEventQueue);
436
CheckForDeactivation();
441
nsEventQueueImpl::HandleEvent(PLEvent* aEvent)
443
PRBool correctThread = PL_IsQueueOnCurrentThread(mEventQueue);
444
NS_ASSERTION(correctThread, "attemping to process events on the wrong thread");
446
return NS_ERROR_FAILURE;
448
#if defined(PR_LOGGING) && defined(DEBUG_danm)
449
PR_LOG(gEventQueueLog, PR_LOG_DEBUG,
450
("EventQueue: handle event [queue=%lx, accept=%d, could=%d]",
451
(long)mEventQueue,(int)mAcceptingEvents,(int)mCouldHaveEvents));
452
++gEventQueueLogCount;
454
PL_HandleEvent(aEvent);
459
nsEventQueueImpl::WaitForEvent(PLEvent** aResult)
461
PRBool correctThread = PL_IsQueueOnCurrentThread(mEventQueue);
462
NS_ASSERTION(correctThread, "attemping to process events on the wrong thread");
464
return NS_ERROR_FAILURE;
466
#if defined(PR_LOGGING) && defined(DEBUG_danm)
467
PR_LOG(gEventQueueLog, PR_LOG_DEBUG,
468
("EventQueue: wait for event [queue=%lx, accept=%d, could=%d]",
469
(long)mEventQueue,(int)mAcceptingEvents,(int)mCouldHaveEvents));
470
++gEventQueueLogCount;
472
*aResult = PL_WaitForEvent(mEventQueue);
473
CheckForDeactivation();
477
NS_IMETHODIMP_(PRInt32)
478
nsEventQueueImpl::GetEventQueueSelectFD()
480
return PL_GetEventQueueSelectFD(mEventQueue);
484
nsEventQueueImpl::Create(nsISupports *aOuter,
488
nsEventQueueImpl* evt = new nsEventQueueImpl();
490
return NS_ERROR_OUT_OF_MEMORY;
491
nsresult rv = evt->QueryInterface(aIID, aResult);
498
// ---------------- nsPIEventQueueChain -----------------
501
nsEventQueueImpl::AppendQueue(nsIEventQueue *aQueue)
504
nsCOMPtr<nsIEventQueue> end;
505
nsCOMPtr<nsPIEventQueueChain> queueChain(do_QueryInterface(aQueue));
508
return NS_ERROR_NO_INTERFACE;
510
/* this would be nice
511
NS_ASSERTION(aQueue->mYoungerQueue == NULL && aQueue->mElderQueue == NULL,
512
"event queue repeatedly appended to queue chain");
514
rv = NS_ERROR_NO_INTERFACE;
518
nsEventQueueImpl *next = this;
519
while (next && depth < 100) {
520
next = NS_STATIC_CAST(nsEventQueueImpl *, next->mYoungerQueue);
525
PR_snprintf(warning, sizeof(warning),
526
"event queue chain length is %d. this is almost certainly a leak.", depth);
531
// (be careful doing this outside nsEventQueueService's mEventQMonitor)
533
GetYoungest(getter_AddRefs(end));
534
nsCOMPtr<nsPIEventQueueChain> endChain(do_QueryInterface(end));
536
endChain->SetYounger(queueChain);
537
queueChain->SetElder(endChain);
544
nsEventQueueImpl::Unlink()
546
nsCOMPtr<nsPIEventQueueChain> young = mYoungerQueue,
549
#if defined(PR_LOGGING) && defined(DEBUG_danm)
550
PR_LOG(gEventQueueLog, PR_LOG_DEBUG,
551
("EventQueue: unlink [queue=%lx, younger=%lx, elder=%lx]",
552
(long)mEventQueue,(long)mYoungerQueue, (long)mElderQueue.get()));
553
++gEventQueueLogCount;
556
// this is probably OK, but shouldn't happen by design, so tell me if it does
557
NS_ASSERTION(!mYoungerQueue, "event queue chain broken in middle");
559
// break links early in case the Release cascades back onto us
560
mYoungerQueue = nsnull;
561
mElderQueue = nsnull;
564
young->SetElder(old);
566
old->SetYounger(young);
572
nsEventQueueImpl::GetYoungest(nsIEventQueue **aQueue)
575
return mYoungerQueue->GetYoungest(aQueue);
577
nsIEventQueue *answer = NS_STATIC_CAST(nsIEventQueue *, this);
584
nsEventQueueImpl::GetYoungestActive(nsIEventQueue **aQueue)
586
nsCOMPtr<nsIEventQueue> answer;
589
mYoungerQueue->GetYoungestActive(getter_AddRefs(answer));
591
if (mAcceptingEvents && mCouldHaveEvents)
592
answer = NS_STATIC_CAST(nsIEventQueue *, this);
595
NS_IF_ADDREF(*aQueue);
600
nsEventQueueImpl::SetYounger(nsPIEventQueueChain *aQueue)
602
mYoungerQueue = aQueue;
607
nsEventQueueImpl::SetElder(nsPIEventQueueChain *aQueue)
609
mElderQueue = aQueue;
614
nsEventQueueImpl::GetYounger(nsIEventQueue **aQueue)
616
if (!mYoungerQueue) {
620
return mYoungerQueue->QueryInterface(NS_GET_IID(nsIEventQueue), (void**)&aQueue);
624
nsEventQueueImpl::GetElder(nsIEventQueue **aQueue)
630
return mElderQueue->QueryInterface(NS_GET_IID(nsIEventQueue), (void**)&aQueue);