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
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156

base / task / common / operations_controller.h [blame]

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

#ifndef BASE_TASK_COMMON_OPERATIONS_CONTROLLER_H_
#define BASE_TASK_COMMON_OPERATIONS_CONTROLLER_H_

#include <atomic>
#include <cstdint>

#include "base/base_export.h"
#include "base/memory/stack_allocated.h"
#include "base/synchronization/waitable_event.h"

namespace base {
namespace internal {

// A lock-free thread-safe controller to manage critical multi-threaded
// operations without locks.
//
// The controller is used to determine if operations are allowed, and to keep
// track of how many are currently active. Users will call TryBeginOperation()
// before starting such operations. If the call succeeds the user can run the
// operation and the controller will keep track of it until the user signals
// that the operation is completed. No operations are allowed before
// StartAcceptingOperations() is called, or after
// ShutdownAndWaitForZeroOperations() is called.
//
// There is no explicit way of telling the controller when an operation is
// completed, instead for convenience TryBeginOperation() will return a RAII
// like object that will do so on destruction.
//
// For example:
//
// OperationsController controller_;
//
// void SetUp() {
//   controller_.StartAcceptingOperations();
// }
//
// void TearDown() {
//   controller_.ShutdownAndWaitForZeroOperations();
// }
//
// void MaybeRunOperation() {
//   auto operation_token = controller_.TryBeginOperation();
//   if (operation_token) {
//     Process();
//   }
// }
//
// This class is thread-safe.
// But note that StartAcceptingOperations can never be called after
// ShutdownAndWaitForZeroOperations.
class BASE_EXPORT OperationsController {
 public:
  // The owner of an OperationToken which evaluates to true can safely perform
  // an operation while being certain it happens-after
  // StartAcceptingOperations() and happens-before
  // ShutdownAndWaitForZeroOperations(). Releasing this OperationToken
  // relinquishes this right.
  //
  // This class is thread-safe
  class OperationToken {
    STACK_ALLOCATED();

   public:
    ~OperationToken() {
      if (outer_)
        outer_->DecrementBy(1);
    }
    OperationToken(const OperationToken&) = delete;
    OperationToken(OperationToken&& other) {
      this->outer_ = other.outer_;
      other.outer_ = nullptr;
    }

    operator bool() const { return !!outer_; }

   private:
    friend class OperationsController;
    explicit OperationToken(OperationsController* outer) : outer_(outer) {}

    OperationsController* outer_;
  };

  OperationsController();

  // Users must call ShutdownAndWaitForZeroOperations() before destroying an
  // instance of this class if StartAcceptingOperations() was called.
  ~OperationsController();

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

  // Starts to accept operations (before this point TryBeginOperation() returns
  // an invalid token). Returns true if an attempt to perform an operation was
  // made and denied before StartAcceptingOperations() was called. Can be called
  // at most once, never after ShutdownAndWaitForZeroOperations().
  bool StartAcceptingOperations();

  // Returns a RAII like object that implicitly converts to true if operations
  // are allowed i.e. if this call happens-after StartAcceptingOperations() and
  // happens-before Shutdown(), otherwise the object will convert to false. On
  // successful return, this OperationsController will keep track of the
  // operation until the returned object goes out of scope.
  OperationToken TryBeginOperation();

  // Prevents further calls to TryBeginOperation() from succeeding and waits for
  // all the ongoing operations to complete.
  //
  // Attention: Can only be called once.
  void ShutdownAndWaitForZeroOperations();

 private:
  // Atomic representation of the state of this class. We use the upper 2 bits
  // to keep track of flag like values and the remainder bits are used as a
  // counter. The 2 flags are used to represent 3 different states:
  //
  // State                   | AcceptOperations Bit | ShuttingDown Bit
  // --------------------------------------------------------------
  // kRejectingOperations    | 0                    | 0
  // kAcceptingOperations    | 1                    | 0
  // kShuttingDown           | *                    | 1
  //
  // The counter keeps track of the rejected operations when we are in
  // the kRejectingOperations state, the number of inflight operations
  // otherwise. If the count reaches zero and we are in the shutting down state
  // |shutdown_complete_| will be signaled.
  static constexpr uint32_t kShuttingDownBitMask = uint32_t{1} << 31;
  static constexpr uint32_t kAcceptingOperationsBitMask = uint32_t{1} << 30;
  static constexpr uint32_t kFlagsBitMask =
      (kShuttingDownBitMask | kAcceptingOperationsBitMask);
  static constexpr uint32_t kCountBitMask = ~kFlagsBitMask;
  enum class State {
    kRejectingOperations,
    kAcceptingOperations,
    kShuttingDown,
  };

  // Helper methods for the bit fiddling. Pass a |state_and_count_| value to
  // extract state or count out of it.
  static uint32_t ExtractCount(uint32_t value) { return value & kCountBitMask; }
  static State ExtractState(uint32_t value);

  // Decrements the counter by |n| and signals |shutdown_complete_| if needed.
  void DecrementBy(uint32_t n);

  std::atomic<uint32_t> state_and_count_{0};
  WaitableEvent shutdown_complete_;
};

}  // namespace internal
}  // namespace base

#endif  // BASE_TASK_COMMON_OPERATIONS_CONTROLLER_H_