blob: a9b891ad772d433865cb25611b4694e0083b0445 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_TASK_SCHEDULER_SCHEDULER_WORKER_H_
#define BASE_TASK_SCHEDULER_SCHEDULER_WORKER_H_
#include <memory>
#include "base/base_export.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/synchronization/atomic_flag.h"
#include "base/synchronization/waitable_event.h"
#include "base/task_scheduler/scheduler_lock.h"
#include "base/task_scheduler/sequence.h"
#include "base/threading/platform_thread.h"
#include "base/time/time.h"
namespace base {
namespace internal {
class TaskTracker;
// A worker that manages a single thread to run Tasks from Sequences returned
// by a delegate.
//
// A SchedulerWorker starts out sleeping. It is woken up by a call to WakeUp().
// After a wake-up, a SchedulerWorker runs Tasks from Sequences returned by the
// GetWork() method of its delegate as long as it doesn't return nullptr. It
// also periodically checks with its TaskTracker whether shutdown has completed
// and exits when it has.
//
// The worker is free to release and reallocate the platform thread with
// guidance from the delegate.
//
// This class is thread-safe.
class BASE_EXPORT SchedulerWorker {
public:
// Delegate interface for SchedulerWorker. The methods are always called from
// the thread managed by the SchedulerWorker instance.
class Delegate {
public:
virtual ~Delegate() = default;
// Called by a thread managed by |worker| when it enters its main function.
// If a thread is recreated after detachment, |detach_duration| is the time
// elapsed since detachment. Otherwise, if this is the first thread created
// for |worker|, |detach_duration| is TimeDelta::Max().
virtual void OnMainEntry(SchedulerWorker* worker) = 0;
// Called by a thread managed by |worker| to get a Sequence from which to
// run a Task.
virtual scoped_refptr<Sequence> GetWork(SchedulerWorker* worker) = 0;
// Called by the SchedulerWorker after it ran a task with |task_priority|.
// |task_latency| is the time elapsed between when the task was posted and
// when it started to run.
virtual void DidRunTaskWithPriority(TaskPriority task_priority,
const TimeDelta& task_latency) = 0;
// Called when |sequence| isn't empty after the SchedulerWorker pops a Task
// from it. |sequence| is the last Sequence returned by GetWork().
virtual void ReEnqueueSequence(scoped_refptr<Sequence> sequence) = 0;
// Called by a thread to determine how long to sleep before the next call to
// GetWork(). GetWork() may be called before this timeout expires if the
// worker's WakeUp() method is called.
virtual TimeDelta GetSleepTimeout() = 0;
// Called by a thread if it is allowed to detach if the last call to
// GetWork() returned nullptr.
//
// It is the responsibility of the delegate to determine if detachment is
// safe. If the delegate is responsible for thread-affine work, detachment
// is generally not safe.
//
// When true is returned:
// - The next WakeUp() could be more costly due to new thread creation.
// - The worker will take this as a signal that it can detach, but it is not
// obligated to do so.
// This MUST return false if SchedulerWorker::JoinForTesting() is in
// progress.
virtual bool CanDetach(SchedulerWorker* worker) = 0;
// Called by a thread before it detaches. This method is not allowed to
// acquire a SchedulerLock because it is called within the scope of another
// SchedulerLock.
virtual void OnDetach() = 0;
};
enum class InitialState { ALIVE, DETACHED };
// Creates a SchedulerWorker that runs Tasks from Sequences returned by
// |delegate|. |priority_hint| is the preferred thread priority; the actual
// thread priority depends on shutdown state and platform capabilities.
// |task_tracker| is used to handle shutdown behavior of Tasks. If
// |worker_state| is DETACHED, the thread will be created upon a WakeUp().
// Returns nullptr if creating the underlying platform thread fails during
// Create().
static std::unique_ptr<SchedulerWorker> Create(
ThreadPriority priority_hint,
std::unique_ptr<Delegate> delegate,
TaskTracker* task_tracker,
InitialState initial_state);
// Destroying a SchedulerWorker in production is not allowed; it is always
// leaked. In tests, it can only be destroyed after JoinForTesting() has
// returned.
~SchedulerWorker();
// Wakes up this SchedulerWorker if it wasn't already awake. After this
// is called, this SchedulerWorker will run Tasks from Sequences
// returned by the GetWork() method of its delegate until it returns nullptr.
// WakeUp() may fail if the worker is detached and it fails to allocate a new
// worker. If this happens, there will be no call to GetWork().
void WakeUp();
SchedulerWorker::Delegate* delegate() { return delegate_.get(); }
// Joins this SchedulerWorker. If a Task is already running, it will be
// allowed to complete its execution. This can only be called once.
void JoinForTesting();
// Returns true if the worker is alive.
bool ThreadAliveForTesting() const;
private:
class Thread;
SchedulerWorker(ThreadPriority thread_priority,
std::unique_ptr<Delegate> delegate,
TaskTracker* task_tracker);
// Returns the thread instance if the detach was successful so that it can be
// freed upon termination of the thread.
// If the detach is not possible, returns nullptr.
std::unique_ptr<SchedulerWorker::Thread> Detach();
void CreateThread();
void CreateThreadAssertSynchronized();
// Synchronizes access to |thread_|.
mutable SchedulerLock thread_lock_;
// The underlying thread for this SchedulerWorker.
std::unique_ptr<Thread> thread_;
const ThreadPriority priority_hint_;
const std::unique_ptr<Delegate> delegate_;
TaskTracker* const task_tracker_;
// Set once JoinForTesting() has been called.
AtomicFlag should_exit_for_testing_;
DISALLOW_COPY_AND_ASSIGN(SchedulerWorker);
};
} // namespace internal
} // namespace base
#endif // BASE_TASK_SCHEDULER_SCHEDULER_WORKER_H_