• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2018 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "base/task/common/operations_controller.h"
6 #include "base/check_op.h"
7 #include "base/synchronization/waitable_event.h"
8 
9 #include <ostream>
10 
11 namespace base {
12 namespace internal {
13 
14 OperationsController::OperationsController() = default;
15 
~OperationsController()16 OperationsController::~OperationsController() {
17 #if DCHECK_IS_ON()
18   // An OperationsController may only be deleted when it was either not
19   // accepting operations or after it was shutdown and there are no in flight
20   // attempts to perform operations.
21   auto value = state_and_count_.load();
22   DCHECK(
23       ExtractState(value) == State::kRejectingOperations ||
24       (ExtractState(value) == State::kShuttingDown && ExtractCount(value) == 0))
25       << value;
26 #endif
27 }
28 
StartAcceptingOperations()29 bool OperationsController::StartAcceptingOperations() {
30   // Release semantics are required to ensure that all memory accesses made on
31   // this thread happen-before any others done on a thread which is later
32   // allowed to perform an operation.
33   auto prev_value = state_and_count_.fetch_or(kAcceptingOperationsBitMask,
34                                               std::memory_order_release);
35 
36   DCHECK_EQ(ExtractState(prev_value), State::kRejectingOperations);
37   // The count is the number of rejected operations, unwind them now.
38   auto num_rejected = ExtractCount(prev_value);
39   DecrementBy(num_rejected);
40   return num_rejected != 0;
41 }
42 
TryBeginOperation()43 OperationsController::OperationToken OperationsController::TryBeginOperation() {
44   // Acquire semantics are required to ensure that a thread which is allowed to
45   // perform an operation sees all the memory side-effects that happened-before
46   // StartAcceptingOperations(). They're also required so that no operations on
47   // this thread (e.g. the operation itself) can be reordered before this one.
48   auto prev_value = state_and_count_.fetch_add(1, std::memory_order_acquire);
49 
50   switch (ExtractState(prev_value)) {
51     case State::kRejectingOperations:
52       return OperationToken(nullptr);
53     case State::kAcceptingOperations:
54       return OperationToken(this);
55     case State::kShuttingDown:
56       DecrementBy(1);
57       return OperationToken(nullptr);
58   }
59 }
60 
ShutdownAndWaitForZeroOperations()61 void OperationsController::ShutdownAndWaitForZeroOperations() {
62   // Acquire semantics are required to guarantee that all memory side-effects
63   // made by other threads that were allowed to perform operations are
64   // synchronized with this thread before it returns from this method.
65   auto prev_value = state_and_count_.fetch_or(kShuttingDownBitMask,
66                                               std::memory_order_acquire);
67 
68   switch (ExtractState(prev_value)) {
69     case State::kRejectingOperations:
70       // The count is the number of rejected operations, unwind them now.
71       DecrementBy(ExtractCount(prev_value));
72       break;
73     case State::kAcceptingOperations:
74       if (ExtractCount(prev_value) != 0) {
75         shutdown_complete_.Wait();
76       }
77       break;
78     case State::kShuttingDown:
79       DCHECK(false) << "Multiple calls to ShutdownAndWaitForZeroOperations()";
80       break;
81   }
82 }
83 
84 // static
ExtractState(uint32_t value)85 OperationsController::State OperationsController::ExtractState(uint32_t value) {
86   if (value & kShuttingDownBitMask) {
87     return State::kShuttingDown;
88   } else if (value & kAcceptingOperationsBitMask) {
89     return State::kAcceptingOperations;
90   } else {
91     return State::kRejectingOperations;
92   }
93 }
94 
DecrementBy(uint32_t n)95 void OperationsController::DecrementBy(uint32_t n) {
96   // Release semantics are required to ensure that no operation on the current
97   // thread (e.g. the operation itself) can be reordered after this one.
98   auto prev_value = state_and_count_.fetch_sub(n, std::memory_order_release);
99   DCHECK_LE(n, ExtractCount(prev_value)) << "Decrement underflow";
100 
101   if (ExtractState(prev_value) == State::kShuttingDown &&
102       ExtractCount(prev_value) == n) {
103     shutdown_complete_.Signal();
104   }
105 }
106 
107 }  // namespace internal
108 }  // namespace base
109