mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-23 02:05:42 +00:00
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:
parent
b5d5d0a876
commit
bdb1f426ad
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user