summaryrefslogtreecommitdiffstats
path: root/js/src/threading/ConditionVariable.h
blob: e2b8865cbf606430cabcc9931074dbac46346fa5 (plain)
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
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef threading_ConditionVariable_h
#define threading_ConditionVariable_h

#include "mozilla/PlatformConditionVariable.h"
#include "mozilla/TimeStamp.h"

#include <stdint.h>
#include <utility>
#if !defined(XP_WIN) && !defined(__wasi__)
#  include <pthread.h>
#endif

#include "threading/LockGuard.h"
#include "threading/Mutex.h"

namespace js {

template <class T>
class ExclusiveData;

enum class CVStatus { NoTimeout, Timeout };

template <typename T>
using UniqueLock = LockGuard<T>;

// A poly-fill for std::condition_variable.
class ConditionVariable {
 public:
  struct PlatformData;

  ConditionVariable() = default;
  ~ConditionVariable() = default;

  // Wake one thread that is waiting on this condition.
  void notify_one() { impl_.notify_one(); }

  // Wake all threads that are waiting on this condition.
  void notify_all() { impl_.notify_all(); }

  // Block the current thread of execution until this condition variable is
  // woken from another thread via notify_one or notify_all.
  void wait(Mutex& lock) {
#ifdef DEBUG
    lock.preUnlockChecks();
#endif
    impl_.wait(lock.impl_);
#ifdef DEBUG
    lock.preLockChecks();
    lock.postLockChecks();
#endif
  }
  void wait(UniqueLock<Mutex>& lock) { wait(lock.lock); }

  // As with |wait|, block the current thread of execution until woken from
  // another thread. This method will resume waiting once woken until the given
  // Predicate |pred| evaluates to true.
  template <typename Predicate>
  void wait(UniqueLock<Mutex>& lock, Predicate pred) {
    while (!pred()) {
      wait(lock);
    }
  }

  // Block the current thread of execution until woken from another thread, or
  // the given absolute time is reached. The given absolute time is evaluated
  // when this method is called, so will wake up after (abs_time - now),
  // independent of system clock changes. While insulated from clock changes,
  // this API is succeptible to the issues discussed above wait_for.
  CVStatus wait_until(UniqueLock<Mutex>& lock,
                      const mozilla::TimeStamp& abs_time) {
    return wait_for(lock, abs_time - mozilla::TimeStamp::Now());
  }

  // As with |wait_until|, block the current thread of execution until woken
  // from another thread, or the given absolute time is reached. This method
  // will resume waiting once woken until the given Predicate |pred| evaluates
  // to true.
  template <typename Predicate>
  bool wait_until(UniqueLock<Mutex>& lock, const mozilla::TimeStamp& abs_time,
                  Predicate pred) {
    while (!pred()) {
      if (wait_until(lock, abs_time) == CVStatus::Timeout) {
        return pred();
      }
    }
    return true;
  }

  // Block the current thread of execution until woken from another thread, or
  // the given time duration has elapsed. Given that the system may be
  // interrupted between the callee and the actual wait beginning, this call
  // has a minimum granularity of the system's scheduling interval, and may
  // encounter substantially longer delays, depending on system load.
  CVStatus wait_for(UniqueLock<Mutex>& lock,
                    const mozilla::TimeDuration& rel_time) {
#ifdef DEBUG
    lock.lock.preUnlockChecks();
#endif
    CVStatus res =
        impl_.wait_for(lock.lock.impl_, rel_time) == mozilla::CVStatus::Timeout
            ? CVStatus::Timeout
            : CVStatus::NoTimeout;
#ifdef DEBUG
    lock.lock.preLockChecks();
    lock.lock.postLockChecks();
#endif
    return res;
  }

  // As with |wait_for|, block the current thread of execution until woken from
  // another thread or the given time duration has elapsed. This method will
  // resume waiting once woken until the given Predicate |pred| evaluates to
  // true.
  template <typename Predicate>
  bool wait_for(UniqueLock<Mutex>& lock, const mozilla::TimeDuration& rel_time,
                Predicate pred) {
    return wait_until(lock, mozilla::TimeStamp::Now() + rel_time,
                      std::move(pred));
  }

 private:
  ConditionVariable(const ConditionVariable&) = delete;
  ConditionVariable& operator=(const ConditionVariable&) = delete;
  template <class T>
  friend class ExclusiveWaitableData;

  mozilla::detail::ConditionVariableImpl impl_;
};

}  // namespace js

#endif  // threading_ConditionVariable_h