1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131

content / browser / cache_storage / cache_storage_scheduler.h [blame]

// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CONTENT_BROWSER_CACHE_STORAGE_CACHE_STORAGE_SCHEDULER_H_
#define CONTENT_BROWSER_CACHE_STORAGE_CACHE_STORAGE_SCHEDULER_H_

#include <map>
#include <vector>

#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/memory/weak_ptr.h"
#include "base/task/sequenced_task_runner.h"
#include "content/browser/cache_storage/cache_storage_scheduler_types.h"
#include "content/common/content_export.h"

namespace content {

class CacheStorageOperation;

CONTENT_EXPORT BASE_DECLARE_FEATURE(kCacheStorageParallelOps);

// TODO(jkarlin): Support operation identification so that ops can be checked in
// DCHECKs.

// CacheStorageScheduler runs the scheduled callbacks sequentially. Add an
// operation by calling ScheduleOperation() with your callback. Once your
// operation is done be sure to call CompleteOperationAndRunNext() to schedule
// the next operation.
class CONTENT_EXPORT CacheStorageScheduler {
 public:
  // TODO(estade): remove `task_runner` which is invariably the same one that
  // `CacheStorageScheduler` is constructed and operated on (i.e. the "current
  // default").
  CacheStorageScheduler(CacheStorageSchedulerClient client_type,
                        scoped_refptr<base::SequencedTaskRunner> task_runner);

  CacheStorageScheduler(const CacheStorageScheduler&) = delete;
  CacheStorageScheduler& operator=(const CacheStorageScheduler&) = delete;

  virtual ~CacheStorageScheduler();

  // Create a scheduler-unique identifier for an operation to be scheduled.
  // This value must be passed to the ScheduleOperation(),
  // CompleteOperationAndRunNext(), and WrapCallbackToRunNext() methods.
  CacheStorageSchedulerId CreateId();

  // Adds the operation to the tail of the queue and starts it if possible.
  // A unique identifier must be provided via the CreateId() method.  The
  // mode determines whether the operation should run exclusively by itself
  // or can safely run in parallel with other shared operations. Note that the
  // operation may be executed either synchronously or asynchronously.
  void ScheduleOperation(CacheStorageSchedulerId id,
                         CacheStorageSchedulerMode mode,
                         CacheStorageSchedulerOp op_type,
                         CacheStorageSchedulerPriority priority,
                         base::OnceClosure closure);

  // Call this after each operation completes. It cleans up the operation
  // associated with the given id.  If may also start the next set of
  // operations.
  void CompleteOperationAndRunNext(CacheStorageSchedulerId id);

  // Returns true if there are any running or pending operations.
  bool ScheduledOperations() const;

  // Returns true if the scheduler is currently running an exclusive operation.
  bool IsRunningExclusiveOperation() const;

  // Wraps |callback| to also call CompleteOperationAndRunNext.
  template <typename... Args>
  base::OnceCallback<void(Args...)> WrapCallbackToRunNext(
      CacheStorageSchedulerId id,
      base::OnceCallback<void(Args...)> callback) {
    return base::BindOnce(&CacheStorageScheduler::RunNextContinuation<Args...>,
                          weak_ptr_factory_.GetWeakPtr(), id,
                          std::move(callback));
  }

 protected:
  // virtual for testing
  virtual void DispatchOperationTask(base::OnceClosure task);

 private:
  // Maybe start running the next operation depending on the current
  // set of running operations and the mode of the next operation.
  void MaybeRunOperation();

  template <typename... Args>
  void RunNextContinuation(CacheStorageSchedulerId id,
                           base::OnceCallback<void(Args...)> callback,
                           Args... args) {
    // Grab a weak ptr to guard against the scheduler being deleted during the
    // callback.
    base::WeakPtr<CacheStorageScheduler> scheduler =
        weak_ptr_factory_.GetWeakPtr();

    std::move(callback).Run(std::forward<Args>(args)...);
    if (scheduler)
      CompleteOperationAndRunNext(id);
  }

  scoped_refptr<base::SequencedTaskRunner> task_runner_;

  // Managed as a heap using std::push_heap and std::pop_heap.  We do not
  // use std::priority_queue since it does not support moving the contained
  // unique_ptr out when the operation begins execution.
  std::vector<std::unique_ptr<CacheStorageOperation>> pending_operations_;

  std::map<CacheStorageSchedulerId, std::unique_ptr<CacheStorageOperation>>
      running_operations_;
  const CacheStorageSchedulerClient client_type_;
  CacheStorageSchedulerId next_id_ = 0;

  // Number of shared/exclusive operations currently running.
  int num_running_shared_ = 0;
  int num_running_exclusive_ = 0;

  // Whether the control flow is already inside `MaybeRunOperation()`. Used to
  // prevent recursion.
  bool in_maybe_run_ = false;

  SEQUENCE_CHECKER(sequence_checker_);
  base::WeakPtrFactory<CacheStorageScheduler> weak_ptr_factory_{this};
};

}  // namespace content

#endif  // CONTENT_BROWSER_CACHE_STORAGE_CACHE_STORAGE_SCHEDULER_H_