blob: 71d4cbcc91667b27be3537c8519aafc802bd4221 [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/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
// a 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, this call will occur again.
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 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;
};
enum class InitialState { ALIVE, DETACHED };
// Creates a SchedulerWorker with priority |thread_priority| that runs Tasks
// from Sequences returned by |delegate|. |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 thread_priority,
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();
bool ShouldExitForTesting() const;
// Synchronizes access to |thread_|
mutable SchedulerLock thread_lock_;
// The underlying thread for this SchedulerWorker.
std::unique_ptr<Thread> thread_;
const ThreadPriority thread_priority_;
const std::unique_ptr<Delegate> delegate_;
TaskTracker* const task_tracker_;
// Synchronizes access to |should_exit_for_testing_|.
mutable SchedulerLock should_exit_for_testing_lock_;
// True once JoinForTesting() has been called.
bool should_exit_for_testing_ = false;
DISALLOW_COPY_AND_ASSIGN(SchedulerWorker);
};
} // namespace internal
} // namespace base
#endif // BASE_TASK_SCHEDULER_SCHEDULER_WORKER_H_