Bug 805451: Remove lock (mostly) and ensure other singleton refs are MainThread r=derf

This commit is contained in:
Randell Jesup 2012-10-25 20:14:47 -04:00
parent e289cb5b07
commit b993796c62
2 changed files with 123 additions and 134 deletions

View File

@ -62,13 +62,12 @@ public:
nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> success(mSuccess);
nsCOMPtr<nsIDOMGetUserMediaErrorCallback> error(mError);
{
MutexAutoLock lock(MediaManager::Get()->GetMutex());
WindowTable* activeWindows = MediaManager::Get()->GetActiveWindows();
if (activeWindows->Get(mWindowID)) {
error->OnError(mErrorMsg);
}
if (!(MediaManager::Get()->IsWindowStillActive(mWindowID))) {
return NS_OK;
}
// This is safe since we're on main-thread, and the windowlist can only
// be invalidated from the main-thread (see OnNavigation)
error->OnError(mErrorMsg);
return NS_OK;
}
@ -106,14 +105,12 @@ public:
nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> success(mSuccess);
nsCOMPtr<nsIDOMGetUserMediaErrorCallback> error(mError);
{
MutexAutoLock lock(MediaManager::Get()->GetMutex());
WindowTable* activeWindows = MediaManager::Get()->GetActiveWindows();
if (activeWindows->Get(mWindowID)) {
// XPConnect is a magical unicorn.
success->OnSuccess(mFile);
}
if (!(MediaManager::Get()->IsWindowStillActive(mWindowID))) {
return NS_OK;
}
// This is safe since we're on main-thread, and the windowlist can only
// be invalidated from the main-thread (see OnNavigation)
success->OnSuccess(mFile);
return NS_OK;
}
@ -252,19 +249,20 @@ public:
nsPIDOMWindow *window = static_cast<nsPIDOMWindow*>
(nsGlobalWindow::GetInnerWindowWithId(mWindowID));
WindowTable* activeWindows = MediaManager::Get()->GetActiveWindows();
{
MutexAutoLock lock(MediaManager::Get()->GetMutex());
if (!stream) {
if (activeWindows->Get(mWindowID)) {
nsCOMPtr<nsIDOMGetUserMediaErrorCallback> error(mError);
LOG(("Returning error for getUserMedia() - no stream"));
error->OnError(NS_LITERAL_STRING("NO_STREAM"));
if (!(MediaManager::Get()->IsWindowStillActive(mWindowID))) {
return NS_OK;
}
// This is safe since we're on main-thread, and the windowlist can only
// be invalidated from the main-thread (see OnNavigation)
nsCOMPtr<nsIDOMGetUserMediaErrorCallback> error(mError);
LOG(("Returning error for getUserMedia() - no stream"));
error->OnError(NS_LITERAL_STRING("NO_STREAM"));
return NS_OK;
}
}
if (window && window->GetExtantDoc()) {
stream->CombineWithPrincipal(window->GetExtantDoc()->NodePrincipal());
}
@ -295,13 +293,13 @@ public:
nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> success(mSuccess);
nsCOMPtr<nsIDOMGetUserMediaErrorCallback> error(mError);
{
MutexAutoLock lock(MediaManager::Get()->GetMutex());
if (activeWindows->Get(mWindowID)) {
LOG(("Returning success for getUserMedia()"));
success->OnSuccess(static_cast<nsIDOMLocalMediaStream*>(stream));
}
if (!(MediaManager::Get()->IsWindowStillActive(mWindowID))) {
return NS_OK;
}
// This is safe since we're on main-thread, and the windowlist can only
// be invalidated from the main-thread (see OnNavigation)
LOG(("Returning success for getUserMedia()"));
success->OnSuccess(static_cast<nsIDOMLocalMediaStream*>(stream));
return NS_OK;
}
@ -435,9 +433,12 @@ public:
Denied()
{
if (NS_IsMainThread()) {
// This is safe since we're on main-thread, and the window can only
// be invalidated from the main-thread (see OnNavigation)
nsCOMPtr<nsIDOMGetUserMediaErrorCallback> error(mError);
error->OnError(NS_LITERAL_STRING("PERMISSION_DENIED"));
} else {
// This will re-check the window being alive on main-thread
NS_DispatchToMainThread(new ErrorCallbackRunnable(
mSuccess, mError, NS_LITERAL_STRING("PERMISSION_DENIED"), mWindowID
));
@ -789,51 +790,49 @@ MediaManager::GetUserMedia(bool aPrivileged, nsPIDOMWindow* aWindow,
// when this window is closed or navigated away from.
uint64_t windowID = aWindow->WindowID();
nsRefPtr<GetUserMediaRunnable> gUMRunnable;
{
MutexAutoLock lock(mMutex);
StreamListeners* listeners = mActiveWindows.Get(windowID);
if (!listeners) {
listeners = new StreamListeners;
mActiveWindows.Put(windowID, listeners);
}
// Developer preference for turning off permission check.
if (Preferences::GetBool("media.navigator.permission.disabled", false)) {
aPrivileged = true;
}
/**
* Pass runnables along to GetUserMediaRunnable so it can add the
* MediaStreamListener to the runnable list. The last argument can
* optionally be a MediaDevice object, which should provided if one was
* selected by the user via the UI, or was provided by privileged code
* via the device: attribute via nsIMediaStreamOptions.
*
* If a fake stream was requested, we force the use of the default backend.
*/
if (fake) {
// Fake stream from default backend.
gUMRunnable = new GetUserMediaRunnable(
audio, video, onSuccess.forget(), onError.forget(), listeners,
windowID, new MediaEngineDefault()
);
} else if (audiodevice || videodevice) {
// Stream from provided device.
gUMRunnable = new GetUserMediaRunnable(
audio, video, picture, onSuccess.forget(), onError.forget(), listeners,
windowID,
static_cast<MediaDevice*>(audiodevice.get()),
static_cast<MediaDevice*>(videodevice.get())
);
} else {
// Stream from default device from WebRTC backend.
gUMRunnable = new GetUserMediaRunnable(
audio, video, picture, onSuccess.forget(), onError.forget(), listeners,
windowID
);
}
// This is safe since we're on main-thread, and the windowlist can only
// be invalidated from the main-thread (see OnNavigation)
StreamListeners* listeners = GetActiveWindows()->Get(windowID);
if (!listeners) {
listeners = new StreamListeners;
GetActiveWindows()->Put(windowID, listeners);
}
// Developer preference for turning off permission check.
if (Preferences::GetBool("media.navigator.permission.disabled", false)) {
aPrivileged = true;
}
/**
* Pass runnables along to GetUserMediaRunnable so it can add the
* MediaStreamListener to the runnable list. The last argument can
* optionally be a MediaDevice object, which should provided if one was
* selected by the user via the UI, or was provided by privileged code
* via the device: attribute via nsIMediaStreamOptions.
*
* If a fake stream was requested, we force the use of the default backend.
*/
if (fake) {
// Fake stream from default backend.
gUMRunnable = new GetUserMediaRunnable(
audio, video, onSuccess.forget(), onError.forget(), listeners,
windowID, new MediaEngineDefault()
);
} else if (audiodevice || videodevice) {
// Stream from provided device.
gUMRunnable = new GetUserMediaRunnable(
audio, video, picture, onSuccess.forget(), onError.forget(), listeners,
windowID,
static_cast<MediaDevice*>(audiodevice.get()),
static_cast<MediaDevice*>(videodevice.get())
);
} else {
// Stream from default device from WebRTC backend.
gUMRunnable = new GetUserMediaRunnable(
audio, video, picture, onSuccess.forget(), onError.forget(), listeners,
windowID
);
}
#ifdef ANDROID
if (picture) {
@ -844,7 +843,6 @@ MediaManager::GetUserMedia(bool aPrivileged, nsPIDOMWindow* aWindow,
#else
// XXX No full support for picture in Desktop yet (needs proper UI)
if (aPrivileged || fake) {
(void) MediaManager::GetThread();
mMediaThread->Dispatch(gUMRunnable, NS_DISPATCH_NORMAL);
} else {
// Ask for user permission, and dispatch runnable (or not) when a response
@ -865,10 +863,7 @@ MediaManager::GetUserMedia(bool aPrivileged, nsPIDOMWindow* aWindow,
NS_ConvertUTF8toUTF16 callID(buffer);
// Store the current callback.
{
MutexAutoLock lock(mMutex);
mActiveCallbacks.Put(callID, gUMRunnable);
}
mActiveCallbacks.Put(callID, gUMRunnable);
// Construct JSON structure with both the windowID and the callID.
nsAutoString data;
@ -922,6 +917,8 @@ MediaManager::GetBackend()
{
// Plugin backends as appropriate. The default engine also currently
// includes picture support for Android.
// This IS called off main-thread.
MutexAutoLock lock(mMutex);
if (!mBackend) {
#if defined(MOZ_WEBRTC)
mBackend = new MediaEngineWebRTC();
@ -929,39 +926,33 @@ MediaManager::GetBackend()
mBackend = new MediaEngineDefault();
#endif
}
return mBackend;
}
WindowTable*
MediaManager::GetActiveWindows()
{
return &mActiveWindows;
}
void
MediaManager::OnNavigation(uint64_t aWindowID)
{
NS_ASSERTION(NS_IsMainThread(), "OnNavigation called off main thread");
// Invalidate this window. The runnables check this value before making
// a call to content.
{
MutexAutoLock lock(mMutex);
StreamListeners* listeners = mActiveWindows.Get(aWindowID);
if (!listeners) {
return;
}
uint32_t length = listeners->Length();
for (uint32_t i = 0; i < length; i++) {
nsRefPtr<GetUserMediaCallbackMediaStreamListener> listener =
listeners->ElementAt(i);
listener->Invalidate();
listener = nullptr;
}
listeners->Clear();
mActiveWindows.Remove(aWindowID);
// This is safe since we're on main-thread, and the windowlist can only
// be added to from the main-thread (see OnNavigation)
StreamListeners* listeners = GetActiveWindows()->Get(aWindowID);
if (!listeners) {
return;
}
uint32_t length = listeners->Length();
for (uint32_t i = 0; i < length; i++) {
nsRefPtr<GetUserMediaCallbackMediaStreamListener> listener =
listeners->ElementAt(i);
listener->Invalidate();
}
listeners->Clear();
GetActiveWindows()->Remove(aWindowID);
}
nsresult
@ -979,7 +970,7 @@ MediaManager::Observe(nsISupports* aSubject, const char* aTopic,
// Close off any remaining active windows.
{
MutexAutoLock lock(mMutex);
mActiveWindows.Clear();
GetActiveWindows()->Clear();
mActiveCallbacks.Clear();
sSingleton = nullptr;
}
@ -990,15 +981,10 @@ MediaManager::Observe(nsISupports* aSubject, const char* aTopic,
if (!strcmp(aTopic, "getUserMedia:response:allow")) {
nsString key(aData);
nsRefPtr<nsRunnable> runnable;
{
MutexAutoLock lock(mMutex);
if (!mActiveCallbacks.Get(key, getter_AddRefs(runnable))) {
return NS_OK;
}
if (!mActiveCallbacks.Get(key, getter_AddRefs(runnable))) {
return NS_OK;
}
// Reuse the same thread to save memory.
(void) MediaManager::GetThread();
mActiveCallbacks.Remove(key);
if (aSubject) {
// A particular device was chosen by the user.
@ -1019,26 +1005,22 @@ MediaManager::Observe(nsISupports* aSubject, const char* aTopic,
}
}
// Reuse the same thread to save memory.
mMediaThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
{
MutexAutoLock lock(mMutex);
mActiveCallbacks.Remove(key);
}
return NS_OK;
}
if (!strcmp(aTopic, "getUserMedia:response:deny")) {
nsString key(aData);
nsRefPtr<nsRunnable> runnable;
{
MutexAutoLock lock(mMutex);
if (mActiveCallbacks.Get(key, getter_AddRefs(runnable))) {
GetUserMediaRunnable* gUMRunnable =
static_cast<GetUserMediaRunnable*>(runnable.get());
gUMRunnable->Denied();
mActiveCallbacks.Remove(key);
}
if (!mActiveCallbacks.Get(key, getter_AddRefs(runnable))) {
return NS_OK;
}
mActiveCallbacks.Remove(key);
GetUserMediaRunnable* gUMRunnable =
static_cast<GetUserMediaRunnable*>(runnable.get());
gUMRunnable->Denied();
return NS_OK;
}

View File

@ -275,6 +275,9 @@ public:
if (!sSingleton) {
sSingleton = new MediaManager();
NS_NewThread(getter_AddRefs(sSingleton->mMediaThread));
MM_LOG(("New Media thread for gum"));
NS_ASSERTION(NS_IsMainThread(), "Only create MediaManager on main thread");
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
obs->AddObserver(sSingleton, "xpcom-shutdown", false);
@ -283,23 +286,19 @@ public:
}
return sSingleton;
}
static Mutex& GetMutex() {
return Get()->mMutex;
}
static nsIThread* GetThread() {
MutexAutoLock lock(Get()->mMutex); // only need to call Get() once
if (!sSingleton->mMediaThread) {
NS_NewThread(getter_AddRefs(sSingleton->mMediaThread));
MM_LOG(("New Media thread for gum"));
}
return sSingleton->mMediaThread;
return Get()->mMediaThread;
}
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
MediaEngine* GetBackend();
WindowTable* GetActiveWindows();
bool IsWindowStillActive(uint64_t aWindowId) {
NS_ASSERTION(NS_IsMainThread(), "Only access windowlist on main thread");
return !!mActiveWindows.Get(aWindowId);
}
nsresult GetUserMedia(bool aPrivileged, nsPIDOMWindow* aWindow,
nsIMediaStreamOptions* aParams,
@ -311,11 +310,16 @@ public:
void OnNavigation(uint64_t aWindowID);
private:
WindowTable *GetActiveWindows() {
NS_ASSERTION(NS_IsMainThread(), "Only access windowlist on main thread");
return &mActiveWindows;
};
// Make private because we want only one instance of this class
MediaManager()
: mMutex("mozilla::MediaManager")
, mBackend(nullptr)
, mMediaThread(nullptr) {
: mMediaThread(nullptr)
, mMutex("mozilla::MediaManager")
, mBackend(nullptr) {
mActiveWindows.Init();
mActiveCallbacks.Init();
};
@ -324,12 +328,15 @@ private:
delete mBackend;
};
// ONLY access from MainThread so we don't need to lock
WindowTable mActiveWindows;
nsRefPtrHashtable<nsStringHashKey, nsRunnable> mActiveCallbacks;
// Always exists
nsCOMPtr<nsIThread> mMediaThread;
Mutex mMutex;
// protected with mMutex:
MediaEngine* mBackend;
nsCOMPtr<nsIThread> mMediaThread;
WindowTable mActiveWindows;
nsRefPtrHashtable<nsStringHashKey, nsRunnable> mActiveCallbacks;
static nsRefPtr<MediaManager> sSingleton;
};