340032 nsIThreadObserver needs a method called after processing an event to support Cocoa autorelease pools. r=darin sr=bryner

This commit is contained in:
mark%moxienet.com 2006-06-07 00:06:11 +00:00
parent 3c9961813f
commit a6b04ae8c0
6 changed files with 104 additions and 8 deletions

View File

@ -477,6 +477,13 @@ nsSocketTransportService::OnProcessNextEvent(nsIThreadInternal *thread,
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransportService::AfterProcessNextEvent(nsIThreadInternal* thread,
PRUint32 depth)
{
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransportService::Run()
{

View File

@ -55,6 +55,10 @@ public:
nsresult Init();
NS_IMETHOD Run(void);
NS_IMETHOD OnProcessNextEvent(nsIThreadInternal *aThread, PRBool aMayWait,
PRUint32 aRecursionDepth);
NS_IMETHOD AfterProcessNextEvent(nsIThreadInternal *aThread,
PRUint32 aRecursionDepth);
// public only to be visible to Objective-C code that must call it
void ProcessGeckoEvents();
@ -67,6 +71,7 @@ protected:
protected:
NSAutoreleasePool* mMainPool;
CFMutableArrayRef mAutoreleasePools;
NSPort* mPort;
AppShellDelegate* mDelegate;

View File

@ -70,7 +70,8 @@
// nsAppShell implementation
nsAppShell::nsAppShell()
: mPort(nil)
: mAutoreleasePools(nsnull)
, mPort(nil)
, mDelegate(nil)
, mRunningEventLoop(PR_FALSE)
{
@ -85,6 +86,12 @@ nsAppShell::nsAppShell()
nsAppShell::~nsAppShell()
{
if (mAutoreleasePools) {
NS_ASSERTION(::CFArrayGetCount(mAutoreleasePools) == 0,
"nsAppShell destroyed without popping all autorelease pools");
::CFRelease(mAutoreleasePools);
}
if (mPort) {
[[NSRunLoop currentRunLoop] removePort:mPort forMode:NSDefaultRunLoopMode];
[mPort release];
@ -108,6 +115,13 @@ nsAppShell::Init()
// and will release them as appropriate.
NSAutoreleasePool* localPool = [[NSAutoreleasePool alloc] init];
// mAutoreleasePools is used as a stack of NSAutoreleasePool objects created
// by |this|. CFArray is used instead of NSArray because NSArray wants to
// retain each object you add to it, and you can't retain an
// NSAutoreleasePool.
mAutoreleasePools = ::CFArrayCreateMutable(nsnull, 0, nsnull);
NS_ENSURE_STATE(mAutoreleasePools);
// Get the path of the nib file, which lives in the GRE location
nsCOMPtr<nsIFile> nibFile;
nsresult rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(nibFile));
@ -230,10 +244,10 @@ nsAppShell::ProcessNextNativeEvent(PRBool aMayWait)
waitUntil = [NSDate distantFuture];
do {
// Handle the event on its own autorelease pool.
// Ordinarily, each event gets a new pool when dispatched by
// [NSApp run].
NSAutoreleasePool* localPool = [[NSAutoreleasePool alloc] init];
// No autorelease pool is provided here, because OnProcessNextEvent
// and AfterProcessNextEvent are responsible for maintaining it.
NS_ASSERTION(mAutoreleasePools && ::CFArrayGetCount(mAutoreleasePools),
"No autorelease pool for native event");
if (NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask
untilDate:waitUntil
@ -253,8 +267,6 @@ nsAppShell::ProcessNextNativeEvent(PRBool aMayWait)
eventProcessed = PR_TRUE;
}
[localPool release];
} while (mRunningEventLoop);
mRunningEventLoop = wasRunningEventLoop;
@ -289,6 +301,54 @@ nsAppShell::Run(void)
return nsBaseAppShell::Run();
}
// OnProcessNextEvent
//
// This nsIThreadObserver method is called prior to processing an event.
// Set up an autorelease pool that will service any autoreleased Cocoa
// objects during this event. This includes native events processed by
// ProcessNextNativeEvent. The autorelease pool will be popped by
// AfterProcessNextEvent, it is important for these two methods to be
// tightly coupled.
//
// public
NS_IMETHODIMP
nsAppShell::OnProcessNextEvent(nsIThreadInternal *aThread, PRBool aMayWait,
PRUint32 aRecursionDepth)
{
NS_ASSERTION(mAutoreleasePools,
"No stack on which to store autorelease pool");
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
::CFArrayAppendValue(mAutoreleasePools, pool);
return nsBaseAppShell::OnProcessNextEvent(aThread, aMayWait, aRecursionDepth);
}
// AfterProcessNextEvent
//
// This nsIThreadObserver method is called after event processing is complete.
// The Cocoa implementation cleans up the autorelease pool create by the
// previous OnProcessNextEvent call.
//
// public
NS_IMETHODIMP
nsAppShell::AfterProcessNextEvent(nsIThreadInternal *aThread,
PRUint32 aRecursionDepth)
{
CFIndex count = ::CFArrayGetCount(mAutoreleasePools);
NS_ASSERTION(mAutoreleasePools && count,
"Processed an event, but there's no autorelease pool?");
NSAutoreleasePool* pool = NS_STATIC_CAST(const NSAutoreleasePool*,
::CFArrayGetValueAtIndex(mAutoreleasePools,
count - 1));
::CFArrayRemoveValueAtIndex(mAutoreleasePools, count - 1);
[pool release];
return nsBaseAppShell::AfterProcessNextEvent(aThread, aRecursionDepth);
}
// AppShellDelegate implementation
@implementation AppShellDelegate

View File

@ -244,6 +244,14 @@ nsBaseAppShell::OnProcessNextEvent(nsIThreadInternal *thr, PRBool mayWait,
return NS_OK;
}
// Called from the main thread
NS_IMETHODIMP
nsBaseAppShell::AfterProcessNextEvent(nsIThreadInternal *thr,
PRUint32 recursionDepth)
{
return NS_OK;
}
NS_IMETHODIMP
nsBaseAppShell::Observe(nsISupports *subject, const char *topic,
const PRUnichar *data)

View File

@ -101,7 +101,7 @@ interface nsIThreadInternal : nsIThread
* NOTE: It is valid to change the thread's observer during a call to an
* observer method.
*/
[scriptable, uuid(719d2fb9-54ce-4847-b15b-fa9960e22f9f)]
[scriptable, uuid(81D0B509-F198-4417-8020-08EB4271491F)]
interface nsIThreadObserver : nsISupports
{
/**
@ -128,6 +128,19 @@ interface nsIThreadObserver : nsISupports
*/
void onProcessNextEvent(in nsIThreadInternal thread, in boolean mayWait,
in unsigned long recursionDepth);
/**
* This method is called (from nsIThread::ProcessNextEvent) after an event
* is processed. This method is only called on the target thread.
*
* @param thread
* The thread that processed another event.
* @param recursionDepth
* Indicates the number of calls to ProcessNextEvent on the call stack in
* addition to the current call.
*/
void afterProcessNextEvent(in nsIThreadInternal thread,
in unsigned long recursionDepth);
};
/**

View File

@ -486,6 +486,9 @@ nsThread::ProcessNextEvent(PRBool mayWait, PRBool *result)
rv = NS_ERROR_UNEXPECTED;
}
if (obs)
obs->AfterProcessNextEvent(this, mRunningEvent);
return rv;
}