Bug 1208973 - Ensure all code in TaskThrottler is threadsafe since it gets called from various threads. r=botond

Hat tip to :froydnj for describing the proof-of-lock technique used in this patch
in his blog at https://blog.mozilla.org/nfroyd/2015/09/17/compiler-enforced-locked-accesses/

--HG--
extra : commitid : 4jVEAYGsCvY
This commit is contained in:
Kartikaya Gupta 2015-10-05 16:18:56 -04:00
parent b5d5d0a876
commit bdb1f426ad
2 changed files with 44 additions and 11 deletions

View File

@ -17,7 +17,8 @@ namespace mozilla {
namespace layers {
TaskThrottler::TaskThrottler(const TimeStamp& aTimeStamp, const TimeDuration& aMaxWait)
: mOutstanding(false)
: mMonitor("TaskThrottler")
, mOutstanding(false)
, mQueuedTask(nullptr)
, mStartTime(aTimeStamp)
, mMaxWait(aMaxWait)
@ -39,24 +40,27 @@ void
TaskThrottler::PostTask(const tracked_objects::Location& aLocation,
UniquePtr<CancelableTask> aTask, const TimeStamp& aTimeStamp)
{
MonitorAutoLock lock(mMonitor);
TASK_LOG("%p got a task posted; mOutstanding=%d\n", this, mOutstanding);
aTask->SetBirthPlace(aLocation);
if (mOutstanding) {
CancelPendingTask();
if (TimeSinceLastRequest(aTimeStamp) < mMaxWait) {
CancelPendingTask(lock);
if (TimeSinceLastRequest(aTimeStamp, lock) < mMaxWait) {
mQueuedTask = Move(aTask);
TASK_LOG("%p queued task %p\n", this, mQueuedTask.get());
// Make sure the queued task is sent after mMaxWait time elapses,
// even if we don't get a TaskComplete() until then.
TimeDuration timeout = mMaxWait - TimeSinceLastRequest(aTimeStamp);
TimeDuration timeout = mMaxWait - TimeSinceLastRequest(aTimeStamp, lock);
TimeStamp timeoutTime = mStartTime + mMaxWait;
nsRefPtr<TaskThrottler> refPtrThis = this;
mTimer->InitWithCallback(NewTimerCallback(
[refPtrThis, timeoutTime]()
{
MonitorAutoLock lock(refPtrThis->mMonitor);
if (refPtrThis->mQueuedTask) {
refPtrThis->RunQueuedTask(timeoutTime);
refPtrThis->RunQueuedTask(timeoutTime, lock);
}
}),
timeout.ToMilliseconds(), nsITimer::TYPE_ONE_SHOT);
@ -74,6 +78,8 @@ TaskThrottler::PostTask(const tracked_objects::Location& aLocation,
void
TaskThrottler::TaskComplete(const TimeStamp& aTimeStamp)
{
MonitorAutoLock lock(mMonitor);
if (!mOutstanding) {
return;
}
@ -81,7 +87,7 @@ TaskThrottler::TaskComplete(const TimeStamp& aTimeStamp)
mMean.insert(aTimeStamp - mStartTime);
if (mQueuedTask) {
RunQueuedTask(aTimeStamp);
RunQueuedTask(aTimeStamp, lock);
mTimer->Cancel();
} else {
mOutstanding = false;
@ -91,21 +97,30 @@ TaskThrottler::TaskComplete(const TimeStamp& aTimeStamp)
TimeDuration
TaskThrottler::AverageDuration()
{
MonitorAutoLock lock(mMonitor);
return mMean.empty() ? TimeDuration() : mMean.mean();
}
void
TaskThrottler::RunQueuedTask(const TimeStamp& aTimeStamp)
TaskThrottler::RunQueuedTask(const TimeStamp& aTimeStamp,
const MonitorAutoLock& aProofOfLock)
{
TASK_LOG("%p running task %p\n", this, mQueuedTask.get());
mStartTime = aTimeStamp;
mQueuedTask->Run();
mQueuedTask = nullptr;
}
void
TaskThrottler::CancelPendingTask()
{
MonitorAutoLock lock(mMonitor);
CancelPendingTask(lock);
}
void
TaskThrottler::CancelPendingTask(const MonitorAutoLock& aProofOfLock)
{
if (mQueuedTask) {
TASK_LOG("%p cancelling task %p\n", this, mQueuedTask.get());
@ -117,6 +132,14 @@ TaskThrottler::CancelPendingTask()
TimeDuration
TaskThrottler::TimeSinceLastRequest(const TimeStamp& aTimeStamp)
{
MonitorAutoLock lock(mMonitor);
return TimeSinceLastRequest(aTimeStamp, lock);
}
TimeDuration
TaskThrottler::TimeSinceLastRequest(const TimeStamp& aTimeStamp,
const MonitorAutoLock& aProofOfLock)
{
return aTimeStamp - mStartTime;
}
@ -124,12 +147,16 @@ TaskThrottler::TimeSinceLastRequest(const TimeStamp& aTimeStamp)
void
TaskThrottler::ClearHistory()
{
MonitorAutoLock lock(mMonitor);
mMean.clear();
}
void
TaskThrottler::SetMaxDurations(uint32_t aMaxDurations)
{
MonitorAutoLock lock(mMonitor);
if (aMaxDurations != mMean.maxValues()) {
mMean = RollingMean<TimeDuration, TimeDuration>(aMaxDurations);
}

View File

@ -9,9 +9,10 @@
#include <stdint.h> // for uint32_t
#include "base/task.h" // for CancelableTask
#include "mozilla/TimeStamp.h" // for TimeDuration, TimeStamp
#include "mozilla/RollingMean.h" // for RollingMean
#include "mozilla/Monitor.h" // for Monitor
#include "mozilla/mozalloc.h" // for operator delete
#include "mozilla/RollingMean.h" // for RollingMean
#include "mozilla/TimeStamp.h" // for TimeDuration, TimeStamp
#include "mozilla/UniquePtr.h" // for UniquePtr
#include "nsCOMPtr.h" // for nsCOMPtr
#include "nsISupportsImpl.h" // for NS_INLINE_DECL_THREADSAFE_REFCOUNTING
@ -95,6 +96,7 @@ public:
void SetMaxDurations(uint32_t aMaxDurations);
private:
mutable Monitor mMonitor;
bool mOutstanding;
UniquePtr<CancelableTask> mQueuedTask;
TimeStamp mStartTime;
@ -103,7 +105,11 @@ private:
nsCOMPtr<nsITimer> mTimer;
~TaskThrottler();
void RunQueuedTask(const TimeStamp& aTimeStamp);
void RunQueuedTask(const TimeStamp& aTimeStamp,
const MonitorAutoLock& aProofOfLock);
void CancelPendingTask(const MonitorAutoLock& aProofOfLock);
TimeDuration TimeSinceLastRequest(const TimeStamp& aTimeStamp,
const MonitorAutoLock& aProofOfLock);
};
} // namespace layers