Replace all remaining uses of WTF::Mutex with WTF::Lock
[WebKit-https.git] / Source / WTF / wtf / Condition.h
1 /*
2  * Copyright (C) 2015 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #ifndef WTF_Condition_h
27 #define WTF_Condition_h
28
29 #include <chrono>
30 #include <functional>
31 #include <mutex>
32 #include <wtf/CurrentTime.h>
33 #include <wtf/ParkingLot.h>
34
35 namespace WTF {
36
37 class Condition {
38 public:
39     typedef ParkingLot::Clock Clock;
40     
41     Condition() { }
42
43     // Wait on a parking queue while releasing the given lock. It will unlock the lock just before
44     // parking, and relock it upon wakeup. Returns true if we woke up due to some call to
45     // notifyOne() or notifyAll(). Returns false if we woke up due to a timeout. Note that this form
46     // of waitUntil() has some quirks:
47     //
48     // No spurious wake-up: in order for this to return before the timeout, some notifyOne() or
49     // notifyAll() call must have happened. No scenario other than timeout or notify can lead to this
50     // method returning. This means, for example, that you can't use pthread cancelation or signals to
51     // cause early return.
52     //
53     // Past timeout: it's possible for waitUntil() to be called with a timeout in the past. In that
54     // case, waitUntil() will still release the lock and reacquire it. waitUntil() will always return
55     // false in that case. This is subtly different from some pthread_cond_timedwait() implementations,
56     // which may not release the lock for past timeout. But, this behavior is consistent with OpenGroup
57     // documentation for timedwait().
58     template<typename LockType>
59     bool waitUntil(LockType& lock, Clock::time_point timeout)
60     {
61         bool result;
62         if (timeout < Clock::now()) {
63             lock.unlock();
64             result = false;
65         } else {
66             result = ParkingLot::parkConditionally(
67                 &m_dummy,
68                 [] () -> bool { return true; },
69                 [&lock] () { lock.unlock(); },
70                 timeout);
71         }
72         lock.lock();
73         return result;
74     }
75
76     // Wait until the given predicate is satisfied. Returns true if it is satisfied in the end.
77     // May return early due to timeout.
78     template<typename LockType, typename Functor>
79     bool waitUntil(LockType& lock, Clock::time_point timeout, const Functor& predicate)
80     {
81         while (!predicate()) {
82             if (!waitUntil(lock, timeout))
83                 return predicate();
84         }
85         return true;
86     }
87
88     // Wait until the given predicate is satisfied. Returns true if it is satisfied in the end.
89     // May return early due to timeout.
90     template<typename LockType, typename DurationType, typename Functor>
91     bool waitFor(
92         LockType& lock, const DurationType& relativeTimeout, const Functor& predicate)
93     {
94         return waitUntil(lock, absoluteFromRelative(relativeTimeout), predicate);
95     }
96
97     template<typename LockType>
98     void wait(LockType& lock)
99     {
100         waitUntil(lock, Clock::time_point::max());
101     }
102
103     template<typename LockType, typename Functor>
104     void wait(LockType& lock, const Functor& predicate)
105     {
106         while (!predicate())
107             wait(lock);
108     }
109
110     template<typename LockType, typename TimeType>
111     bool waitUntil(LockType& lock, const TimeType& timeout)
112     {
113         if (timeout == TimeType::max()) {
114             wait(lock);
115             return true;
116         }
117         return waitForImpl(lock, timeout - TimeType::clock::now());
118     }
119
120     template<typename LockType>
121     bool waitUntilWallClockSeconds(LockType& lock, double absoluteTimeoutSeconds)
122     {
123         return waitForSecondsImpl(lock, absoluteTimeoutSeconds - currentTime());
124     }
125
126     template<typename LockType>
127     bool waitUntilMonotonicClockSeconds(LockType& lock, double absoluteTimeoutSeconds)
128     {
129         return waitForSecondsImpl(lock, absoluteTimeoutSeconds - monotonicallyIncreasingTime());
130     }
131
132     // FIXME: We could replace the dummy byte with a boolean to tell us if there is anyone waiting
133     // right now. This could be used to implement a fast path for notifyOne() and notifyAll().
134     // https://bugs.webkit.org/show_bug.cgi?id=148090
135
136     void notifyOne()
137     {
138         ParkingLot::unparkOne(&m_dummy);
139     }
140     
141     void notifyAll()
142     {
143         ParkingLot::unparkAll(&m_dummy);
144     }
145     
146 private:
147     template<typename LockType>
148     bool waitForSecondsImpl(LockType& lock, double relativeTimeoutSeconds)
149     {
150         double relativeTimeoutNanoseconds = relativeTimeoutSeconds * (1000.0 * 1000.0 * 1000.0);
151         
152         if (!(relativeTimeoutNanoseconds > 0)) {
153             // This handles insta-timeouts as well as NaN.
154             lock.unlock();
155             lock.lock();
156             return false;
157         }
158
159         if (relativeTimeoutNanoseconds > static_cast<double>(std::numeric_limits<int64_t>::max())) {
160             // If the timeout in nanoseconds cannot be expressed using a 64-bit integer, then we
161             // might as well wait forever.
162             wait(lock);
163             return true;
164         }
165         
166         auto relativeTimeout =
167             std::chrono::nanoseconds(static_cast<int64_t>(relativeTimeoutNanoseconds));
168
169         return waitForImpl(lock, relativeTimeout);
170     }
171     
172     template<typename LockType, typename DurationType>
173     bool waitForImpl(LockType& lock, const DurationType& relativeTimeout)
174     {
175         return waitUntil(lock, absoluteFromRelative(relativeTimeout));
176     }
177
178     template<typename DurationType>
179     Clock::time_point absoluteFromRelative(const DurationType& relativeTimeout)
180     {
181         if (relativeTimeout < DurationType::zero())
182             return Clock::time_point::min();
183
184         if (relativeTimeout > Clock::duration::max()) {
185             // This is highly unlikely. But if it happens, we want to not do anything dumb. Sleeping
186             // without a timeout seems sensible when the timeout duration is greater than what can be
187             // expressed using steady_clock.
188             return Clock::time_point::max();
189         }
190         
191         Clock::duration myRelativeTimeout =
192             std::chrono::duration_cast<Clock::duration>(relativeTimeout);
193
194         return Clock::now() + myRelativeTimeout;
195     }
196
197     uint8_t m_dummy;
198 };
199
200 } // namespace WTF
201
202 using WTF::Condition;
203
204 #endif // WTF_Condition_h
205