Bug 1280507 - Simplify context loss handler. - r=jrmuizel

Use a self-referential RefPtr instead of manual AddRef/Release.
Reuse DisableTimer for when a worker is dead.

MozReview-Commit-ID: E1Cv9M7rbe2
This commit is contained in:
Jeff Gilbert 2016-06-16 10:01:44 -07:00
Родитель 04356f1b32
Коммит cb4677e853
4 изменённых файлов: 65 добавлений и 218 удалений

Просмотреть файл

@ -113,6 +113,7 @@ WebGLContext::WebGLContext()
, mMaxFetchedVertices(0)
, mMaxFetchedInstances(0)
, mBypassShaderValidation(false)
, mContextLossHandler(this)
, mNeedsFakeNoAlpha(false)
, mNeedsFakeNoDepth(false)
, mNeedsFakeNoStencil(false)
@ -174,7 +175,6 @@ WebGLContext::WebGLContext()
mAllowContextRestore = true;
mLastLossWasSimulated = false;
mContextLossHandler = new WebGLContextLossHandler(this);
mContextStatus = ContextNotLost;
mLoseContextOnMemoryPressure = false;
mCanLoseContextInForeground = true;
@ -210,9 +210,6 @@ WebGLContext::~WebGLContext()
// XXX mtseng: bug 709490, not thread safe
WebGLMemoryTracker::RemoveWebGLContext(this);
}
mContextLossHandler->DisableTimer();
mContextLossHandler = nullptr;
}
template<typename T>
@ -1623,7 +1620,7 @@ WebGLContext::TryToRestoreContext()
void
WebGLContext::RunContextLossTimer()
{
mContextLossHandler->RunTimer();
mContextLossHandler.RunTimer();
}
class UpdateContextLossStatusTask : public CancelableRunnable
@ -1765,7 +1762,7 @@ WebGLContext::UpdateContextLossStatus()
if (!TryToRestoreContext()) {
// Failed to restore. Try again later.
mContextLossHandler->RunTimer();
mContextLossHandler.RunTimer();
return;
}

Просмотреть файл

@ -19,7 +19,6 @@
#include "mozilla/gfx/2D.h"
#include "mozilla/LinkedList.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/WeakPtr.h"
#include "nsCycleCollectionNoteChild.h"
#include "nsICanvasRenderingContextInternal.h"
#include "nsLayoutUtils.h"
@ -34,6 +33,7 @@
#endif
// Local
#include "WebGLContextLossHandler.h"
#include "WebGLContextUnchecked.h"
#include "WebGLFormats.h"
#include "WebGLObjectModel.h"
@ -90,7 +90,6 @@ class ScopedCopyTexImageSource;
class ScopedResolveTexturesForDraw;
class ScopedUnpackReset;
class WebGLActiveInfo;
class WebGLContextLossHandler;
class WebGLBuffer;
class WebGLExtensionBase;
class WebGLFramebuffer;
@ -187,7 +186,6 @@ class WebGLContext
, public WebGLContextUnchecked
, public WebGLRectangleObject
, public nsWrapperCache
, public SupportsWeakPtr<WebGLContext>
{
friend class WebGL2Context;
friend class WebGLContextUserData;
@ -223,8 +221,6 @@ protected:
virtual ~WebGLContext();
public:
MOZ_DECLARE_WEAKREFERENCE_TYPENAME(WebGLContext)
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(WebGLContext,
@ -1491,7 +1487,7 @@ protected:
GLsizei mViewportHeight;
bool mAlreadyWarnedAboutViewportLargerThanDest;
RefPtr<WebGLContextLossHandler> mContextLossHandler;
WebGLContextLossHandler mContextLossHandler;
bool mAllowContextRestore;
bool mLastLossWasSimulated;
ContextStatus mContextStatus;

Просмотреть файл

@ -5,237 +5,99 @@
#include "WebGLContextLossHandler.h"
#include "mozilla/DebugOnly.h"
#include "nsISupportsImpl.h"
#include "nsITimer.h"
#include "nsThreadUtils.h"
#include "WebGLContext.h"
#include "mozilla/dom/WorkerPrivate.h"
namespace mozilla {
// -------------------------------------------------------------------
// Begin worker specific code
// -------------------------------------------------------------------
// On workers we can only dispatch CancelableRunnables, so we have to wrap the
// timer's EventTarget to use our own cancelable runnable
class ContextLossWorkerEventTarget final : public nsIEventTarget
class WatchdogTimerEvent final : public nsITimerCallback
{
const WeakPtr<WebGLContextLossHandler> mHandler;
public:
explicit ContextLossWorkerEventTarget(nsIEventTarget* aEventTarget)
: mEventTarget(aEventTarget)
{
MOZ_ASSERT(aEventTarget);
}
NS_DECL_ISUPPORTS
NS_DECL_NSIEVENTTARGET
NS_DECL_THREADSAFE_ISUPPORTS
protected:
~ContextLossWorkerEventTarget() {}
explicit WatchdogTimerEvent(WebGLContextLossHandler* handler)
: mHandler(handler)
{ }
private:
nsCOMPtr<nsIEventTarget> mEventTarget;
};
virtual ~WatchdogTimerEvent() { }
class ContextLossWorkerRunnable final : public CancelableRunnable
{
public:
explicit ContextLossWorkerRunnable(nsIRunnable* aRunnable)
: mRunnable(aRunnable)
{
NS_IMETHOD Notify(nsITimer*) override {
if (mHandler) {
mHandler->TimerCallback();
}
nsresult Cancel() override;
NS_FORWARD_NSIRUNNABLE(mRunnable->)
protected:
~ContextLossWorkerRunnable() {}
private:
nsCOMPtr<nsIRunnable> mRunnable;
};
NS_IMPL_ISUPPORTS(ContextLossWorkerEventTarget, nsIEventTarget,
nsISupports)
NS_IMETHODIMP
ContextLossWorkerEventTarget::DispatchFromScript(nsIRunnable* aEvent, uint32_t aFlags)
{
nsCOMPtr<nsIRunnable> event(aEvent);
return Dispatch(event.forget(), aFlags);
}
NS_IMETHODIMP
ContextLossWorkerEventTarget::Dispatch(already_AddRefed<nsIRunnable> aEvent,
uint32_t aFlags)
{
nsCOMPtr<nsIRunnable> eventRef(aEvent);
RefPtr<ContextLossWorkerRunnable> wrappedEvent =
new ContextLossWorkerRunnable(eventRef);
return mEventTarget->Dispatch(wrappedEvent, aFlags);
}
NS_IMETHODIMP
ContextLossWorkerEventTarget::DelayedDispatch(already_AddRefed<nsIRunnable>,
uint32_t)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
ContextLossWorkerEventTarget::IsOnCurrentThread(bool* aResult)
{
return mEventTarget->IsOnCurrentThread(aResult);
}
nsresult
ContextLossWorkerRunnable::Cancel()
{
mRunnable = nullptr;
return NS_OK;
}
};
// -------------------------------------------------------------------
// End worker-specific code
// -------------------------------------------------------------------
NS_IMPL_ISUPPORTS(WatchdogTimerEvent, nsITimerCallback, nsISupports)
////////////////////////////////////////
WebGLContextLossHandler::WebGLContextLossHandler(WebGLContext* webgl)
: mWeakWebGL(webgl)
: mWebGL(webgl)
, mTimer(do_CreateInstance(NS_TIMER_CONTRACTID))
, mIsTimerRunning(false)
, mTimerPending(false)
, mShouldRunTimerAgain(false)
, mIsDisabled(false)
, mWorkerHolderAdded(false)
#ifdef DEBUG
, mThread(NS_GetCurrentThread())
#endif
{
MOZ_ASSERT(mThread);
}
WebGLContextLossHandler::~WebGLContextLossHandler()
{
MOZ_ASSERT(!mIsTimerRunning);
// NS_GetCurrentThread() returns null during shutdown.
const DebugOnly<nsIThread*> callingThread = NS_GetCurrentThread();
MOZ_ASSERT(callingThread == mThread || !callingThread);
}
////////////////////
void
WebGLContextLossHandler::StartTimer(unsigned long delayMS)
WebGLContextLossHandler::RunTimer()
{
// We can't pass an already_AddRefed through InitWithFuncCallback, so we
// should do the AddRef/Release manually.
this->AddRef();
MOZ_ASSERT(NS_GetCurrentThread() == mThread);
mTimer->InitWithFuncCallback(StaticTimerCallback,
static_cast<void*>(this),
delayMS,
nsITimer::TYPE_ONE_SHOT);
// If the timer was already running, don't restart it here. Instead,
// wait until the previous call is done, then fire it one more time.
// This is also an optimization to prevent unnecessary
// cross-communication between threads.
if (mTimerPending) {
mShouldRunTimerAgain = true;
return;
}
/*static*/ void
WebGLContextLossHandler::StaticTimerCallback(nsITimer*, void* voidHandler)
{
typedef WebGLContextLossHandler T;
T* handler = static_cast<T*>(voidHandler);
const RefPtr<WatchdogTimerEvent> event = new WatchdogTimerEvent(this);
const uint32_t kDelayMS = 1000;
mTimer->InitWithCallback(event, kDelayMS, nsITimer::TYPE_ONE_SHOT);
handler->TimerCallback();
// Release the AddRef from StartTimer.
handler->Release();
mTimerPending = true;
}
////////////////////
void
WebGLContextLossHandler::TimerCallback()
{
MOZ_ASSERT(NS_GetCurrentThread() == mThread);
MOZ_ASSERT(mIsTimerRunning);
mIsTimerRunning = false;
if (mIsDisabled)
return;
mTimerPending = false;
// If we need to run the timer again, restart it immediately.
// Otherwise, the code we call into below might *also* try to
// restart it.
if (mShouldRunTimerAgain) {
RunTimer();
MOZ_ASSERT(mIsTimerRunning);
}
if (mWeakWebGL) {
mWeakWebGL->UpdateContextLossStatus();
}
}
void
WebGLContextLossHandler::RunTimer()
{
MOZ_ASSERT(!mIsDisabled);
// If the timer was already running, don't restart it here. Instead,
// wait until the previous call is done, then fire it one more time.
// This is an optimization to prevent unnecessary
// cross-communication between threads.
if (mIsTimerRunning) {
mShouldRunTimerAgain = true;
return;
}
if (!NS_IsMainThread()) {
dom::workers::WorkerPrivate* workerPrivate =
dom::workers::GetCurrentThreadWorkerPrivate();
nsCOMPtr<nsIEventTarget> target = workerPrivate->GetEventTarget();
mTimer->SetTarget(new ContextLossWorkerEventTarget(target));
if (!mWorkerHolderAdded) {
HoldWorker(workerPrivate);
mWorkerHolderAdded = true;
}
}
StartTimer(1000);
mIsTimerRunning = true;
const bool runOnceMore = mShouldRunTimerAgain;
mShouldRunTimerAgain = false;
mWebGL->UpdateContextLossStatus();
if (runOnceMore && !mTimerPending) {
RunTimer();
}
void
WebGLContextLossHandler::DisableTimer()
{
if (mIsDisabled)
return;
mIsDisabled = true;
if (mWorkerHolderAdded) {
dom::workers::WorkerPrivate* workerPrivate =
dom::workers::GetCurrentThreadWorkerPrivate();
MOZ_RELEASE_ASSERT(workerPrivate, "GFX: No private worker created.");
ReleaseWorker();
mWorkerHolderAdded = false;
}
// We can't just Cancel() the timer, as sometimes we end up
// receiving a callback after calling Cancel(). This could cause us
// to receive the callback after object destruction.
// Instead, we let the timer finish, but ignore it.
if (!mIsTimerRunning)
return;
mTimer->SetDelay(0);
}
bool
WebGLContextLossHandler::Notify(dom::workers::Status aStatus)
{
bool isWorkerRunning = aStatus < dom::workers::Closing;
if (!isWorkerRunning && mIsTimerRunning) {
mIsTimerRunning = false;
this->Release();
}
return true;
}
} // namespace mozilla

Просмотреть файл

@ -6,11 +6,8 @@
#ifndef WEBGL_CONTEXT_LOSS_HANDLER_H_
#define WEBGL_CONTEXT_LOSS_HANDLER_H_
#include "mozilla/DebugOnly.h"
#include "mozilla/WeakPtr.h"
#include "nsCOMPtr.h"
#include "nsISupportsImpl.h"
#include "WorkerHolder.h"
class nsIThread;
class nsITimer;
@ -18,35 +15,30 @@ class nsITimer;
namespace mozilla {
class WebGLContext;
class WebGLContextLossHandler : public dom::workers::WorkerHolder
class WebGLContextLossHandler final : public SupportsWeakPtr<WebGLContextLossHandler>
{
WeakPtr<WebGLContext> mWeakWebGL;
nsCOMPtr<nsITimer> mTimer;
bool mIsTimerRunning;
bool mShouldRunTimerAgain;
bool mIsDisabled;
bool mWorkerHolderAdded;
WebGLContext* const mWebGL;
const nsCOMPtr<nsITimer> mTimer; // If we don't hold a ref to the timer, it will think
bool mTimerPending; // that it's been discarded, and be canceled 'for our
bool mShouldRunTimerAgain; // convenience'.
#ifdef DEBUG
nsIThread* mThread;
nsIThread* const mThread;
#endif
friend class WatchdogTimerEvent;
public:
NS_INLINE_DECL_REFCOUNTING(WebGLContextLossHandler)
MOZ_DECLARE_WEAKREFERENCE_TYPENAME(WebGLContextLossHandler)
explicit WebGLContextLossHandler(WebGLContext* webgl);
void RunTimer();
void DisableTimer();
bool Notify(dom::workers::Status aStatus) override;
protected:
~WebGLContextLossHandler();
void StartTimer(unsigned long delayMS);
static void StaticTimerCallback(nsITimer*, void* tempRefForTimer);
void RunTimer();
private:
void TimerCallback();
};
} // namespace mozilla
#endif
#endif // WEBGL_CONTEXT_LOSS_HANDLER_H_