tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

pending_task_safety_flag.h (6805B)


      1 /*
      2 *  Copyright 2020 The WebRTC Project Authors. All rights reserved.
      3 *
      4 *  Use of this source code is governed by a BSD-style license
      5 *  that can be found in the LICENSE file in the root of the source
      6 *  tree. An additional intellectual property rights grant can be found
      7 *  in the file PATENTS.  All contributing project authors may
      8 *  be found in the AUTHORS file in the root of the source tree.
      9 */
     10 
     11 #ifndef API_TASK_QUEUE_PENDING_TASK_SAFETY_FLAG_H_
     12 #define API_TASK_QUEUE_PENDING_TASK_SAFETY_FLAG_H_
     13 
     14 #include <utility>
     15 
     16 #include "absl/base/nullability.h"
     17 #include "absl/functional/any_invocable.h"
     18 #include "api/ref_counted_base.h"
     19 #include "api/scoped_refptr.h"
     20 #include "api/sequence_checker.h"
     21 #include "api/task_queue/task_queue_base.h"
     22 #include "rtc_base/system/no_unique_address.h"
     23 #include "rtc_base/system/rtc_export.h"
     24 
     25 namespace webrtc {
     26 
     27 // The PendingTaskSafetyFlag and the ScopedTaskSafety are designed to address
     28 // the issue where you have a task to be executed later that has references,
     29 // but cannot guarantee that the referenced object is alive when the task is
     30 // executed.
     31 
     32 // This mechanism can be used with tasks that are created and destroyed
     33 // on a single thread / task queue, and with tasks posted to the same
     34 // thread/task queue, but tasks can be posted from any thread/TQ.
     35 
     36 // Typical usage:
     37 // When posting a task, post a copy (capture by-value in a lambda) of the flag
     38 // reference and before performing the work, check the `alive()` state. Abort if
     39 // alive() returns `false`:
     40 //
     41 // class ExampleClass {
     42 // ....
     43 //    scoped_refptr<PendingTaskSafetyFlag> flag = safety_flag_;
     44 //    my_task_queue_->PostTask(
     45 //        [flag = std::move(flag), this] {
     46 //          // Now running on the main thread.
     47 //          if (!flag->alive())
     48 //            return;
     49 //          MyMethod();
     50 //        });
     51 //   ....
     52 //   ~ExampleClass() {
     53 //     safety_flag_->SetNotAlive();
     54 //   }
     55 //   scoped_refptr<PendingTaskSafetyFlag> safety_flag_
     56 //        = PendingTaskSafetyFlag::Create();
     57 // }
     58 //
     59 // SafeTask makes this check automatic:
     60 //
     61 //   my_task_queue_->PostTask(SafeTask(safety_flag_, [this] { MyMethod(); }));
     62 //
     63 class RTC_EXPORT PendingTaskSafetyFlag final
     64    : public RefCountedNonVirtual<PendingTaskSafetyFlag> {
     65 public:
     66  static scoped_refptr<PendingTaskSafetyFlag> Create();
     67 
     68  // Creates a flag, but with its SequenceChecker initially detached. Hence, it
     69  // may be created on a different thread than the flag will be used on.
     70  static scoped_refptr<PendingTaskSafetyFlag> CreateDetached();
     71 
     72  // Creates a flag, but with its SequenceChecker explicitly initialized for
     73  // a given task queue and the `alive()` flag specified.
     74  static scoped_refptr<PendingTaskSafetyFlag> CreateAttachedToTaskQueue(
     75      bool alive,
     76      TaskQueueBase* absl_nonnull attached_queue);
     77 
     78  // Same as `CreateDetached()` except the initial state of the returned flag
     79  // will be `!alive()`.
     80  static scoped_refptr<PendingTaskSafetyFlag> CreateDetachedInactive();
     81 
     82  ~PendingTaskSafetyFlag() = default;
     83 
     84  void SetNotAlive();
     85  // The SetAlive method is intended to support Start/Stop/Restart usecases.
     86  // When a class has called SetNotAlive on a flag used for posted tasks, and
     87  // decides it wants to post new tasks and have them run, there are two
     88  // reasonable ways to do that:
     89  //
     90  // (i) Use the below SetAlive method. One subtlety is that any task posted
     91  //     prior to SetNotAlive, and still in the queue, is resurrected and will
     92  //     run.
     93  //
     94  // (ii) Create a fresh flag, and just drop the reference to the old one. This
     95  //      avoids the above problem, and ensures that tasks poster prior to
     96  //      SetNotAlive stay cancelled. Instead, there's a potential data race on
     97  //      the flag pointer itself. Some synchronization is required between the
     98  //      thread overwriting the flag pointer, and the threads that want to post
     99  //      tasks and therefore read that same pointer.
    100  void SetAlive();
    101  bool alive() const;
    102 
    103 protected:
    104  explicit PendingTaskSafetyFlag(bool alive) : alive_(alive) {}
    105  PendingTaskSafetyFlag(bool alive, TaskQueueBase* absl_nonnull attached_queue)
    106      : alive_(alive), main_sequence_(attached_queue) {}
    107 
    108 private:
    109  static scoped_refptr<PendingTaskSafetyFlag> CreateInternal(bool alive);
    110 
    111  bool alive_ = true;
    112  RTC_NO_UNIQUE_ADDRESS SequenceChecker main_sequence_;
    113 };
    114 
    115 // The ScopedTaskSafety makes using PendingTaskSafetyFlag very simple.
    116 // It does automatic PTSF creation and signalling of destruction when the
    117 // ScopedTaskSafety instance goes out of scope.
    118 //
    119 // Example usage:
    120 //
    121 //     my_task_queue->PostTask(SafeTask(scoped_task_safety.flag(),
    122 //        [this] {
    123 //             // task goes here
    124 //        }
    125 //
    126 // This should be used by the class that wants tasks dropped after destruction.
    127 // The requirement is that the instance has to be constructed and destructed on
    128 // the same thread as the potentially dropped tasks would be running on.
    129 class RTC_EXPORT ScopedTaskSafety final {
    130 public:
    131  ScopedTaskSafety() = default;
    132  explicit ScopedTaskSafety(scoped_refptr<PendingTaskSafetyFlag> flag)
    133      : flag_(std::move(flag)) {}
    134  ~ScopedTaskSafety() { flag_->SetNotAlive(); }
    135 
    136  // Returns a new reference to the safety flag.
    137  scoped_refptr<PendingTaskSafetyFlag> flag() const { return flag_; }
    138 
    139  // Marks the current flag as not-alive and attaches to a new one.
    140  void reset(scoped_refptr<PendingTaskSafetyFlag> new_flag =
    141                 PendingTaskSafetyFlag::Create()) {
    142    flag_->SetNotAlive();
    143    flag_ = std::move(new_flag);
    144  }
    145 
    146 private:
    147  scoped_refptr<PendingTaskSafetyFlag> flag_ = PendingTaskSafetyFlag::Create();
    148 };
    149 
    150 // Like ScopedTaskSafety, but allows construction on a different thread than
    151 // where the flag will be used.
    152 class RTC_EXPORT ScopedTaskSafetyDetached final {
    153 public:
    154  ScopedTaskSafetyDetached() = default;
    155  ~ScopedTaskSafetyDetached() { flag_->SetNotAlive(); }
    156 
    157  // Returns a new reference to the safety flag.
    158  scoped_refptr<PendingTaskSafetyFlag> flag() const { return flag_; }
    159 
    160 private:
    161  scoped_refptr<PendingTaskSafetyFlag> flag_ =
    162      PendingTaskSafetyFlag::CreateDetached();
    163 };
    164 
    165 inline absl::AnyInvocable<void() &&> SafeTask(
    166    scoped_refptr<PendingTaskSafetyFlag> flag,
    167    absl::AnyInvocable<void() &&> task) {
    168  return [flag = std::move(flag), task = std::move(task)]() mutable {
    169    if (flag->alive()) {
    170      std::move(task)();
    171    }
    172  };
    173 }
    174 
    175 // Safely execute an Invocable that can be used multiple times.
    176 inline absl::AnyInvocable<void()> SafeInvocable(
    177    scoped_refptr<PendingTaskSafetyFlag> flag,
    178    absl::AnyInvocable<void()> task) {
    179  return [flag = std::move(flag), task = std::move(task)]() mutable {
    180    if (flag->alive()) {
    181      task();
    182    }
    183  };
    184 }
    185 
    186 }  // namespace webrtc
    187 
    188 #endif  // API_TASK_QUEUE_PENDING_TASK_SAFETY_FLAG_H_