4e8d6804903c3900ce3ef1da3552b5478c333693
[WebKit-https.git] / Tools / TestWebKitAPI / Tests / WTF / Condition.cpp
1 /*
2  * Copyright (C) 2015-2016 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. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include <mutex>
28 #include <thread>
29 #include <wtf/Condition.h>
30 #include <wtf/CurrentTime.h>
31 #include <wtf/DataLog.h>
32 #include <wtf/Deque.h>
33 #include <wtf/Lock.h>
34 #include <wtf/StringPrintStream.h>
35 #include <wtf/Threading.h>
36 #include <wtf/Vector.h>
37
38 using namespace WTF;
39
40 namespace TestWebKitAPI {
41
42 namespace {
43
44 const bool verbose = false;
45
46 enum NotifyStyle {
47     AlwaysNotifyOne,
48     TacticallyNotifyAll
49 };
50
51 template<typename Functor>
52 void wait(Condition& condition, std::unique_lock<Lock>& locker, const Functor& predicate, Seconds timeout)
53 {
54     if (timeout == Seconds::infinity())
55         condition.wait(locker, predicate);
56     else {
57         // This tests timeouts in the sense that it verifies that we can call wait() again after a
58         // timeout happened. That's a non-trivial piece of functionality since upon timeout the
59         // ParkingLot has to remove us from the queue.
60         while (!predicate())
61             condition.waitFor(locker, timeout, predicate);
62     }
63 }
64
65 void notify(NotifyStyle notifyStyle, Condition& condition, bool shouldNotify)
66 {
67     switch (notifyStyle) {
68     case AlwaysNotifyOne:
69         condition.notifyOne();
70         break;
71     case TacticallyNotifyAll:
72         if (shouldNotify)
73             condition.notifyAll();
74         break;
75     }
76 }
77
78 void runTest(
79     unsigned numProducers,
80     unsigned numConsumers,
81     unsigned maxQueueSize,
82     unsigned numMessagesPerProducer,
83     NotifyStyle notifyStyle,
84     Seconds timeout = Seconds::infinity(),
85     Seconds delay = 0_s)
86 {
87     Deque<unsigned> queue;
88     bool shouldContinue = true;
89     Lock lock;
90     Condition emptyCondition;
91     Condition fullCondition;
92
93     Vector<Ref<Thread>> consumerThreads;
94     Vector<Ref<Thread>> producerThreads;
95
96     Vector<unsigned> received;
97     Lock receivedLock;
98     
99     for (unsigned i = numConsumers; i--;) {
100         consumerThreads.append(Thread::create(
101             "Consumer thread",
102             [&] () {
103                 for (;;) {
104                     unsigned result;
105                     unsigned shouldNotify = false;
106                     {
107                         std::unique_lock<Lock> locker(lock);
108                         wait(
109                             emptyCondition, locker, 
110                             [&] () {
111                                 if (verbose)
112                                     dataLog(toString(Thread::current(), ": Checking consumption predicate with shouldContinue = ", shouldContinue, ", queue.size() == ", queue.size(), "\n"));
113                                 return !shouldContinue || !queue.isEmpty();
114                             },
115                             timeout);
116                         if (!shouldContinue && queue.isEmpty())
117                             return;
118                         shouldNotify = queue.size() == maxQueueSize;
119                         result = queue.takeFirst();
120                     }
121                     notify(notifyStyle, fullCondition, shouldNotify);
122
123                     {
124                         std::lock_guard<Lock> locker(receivedLock);
125                         received.append(result);
126                     }
127                 }
128             }));
129     }
130
131     sleep(delay);
132
133     for (unsigned i = numProducers; i--;) {
134         producerThreads.append(Thread::create(
135             "Producer Thread",
136             [&] () {
137                 for (unsigned i = 0; i < numMessagesPerProducer; ++i) {
138                     bool shouldNotify = false;
139                     {
140                         std::unique_lock<Lock> locker(lock);
141                         wait(
142                             fullCondition, locker,
143                             [&] () {
144                                 if (verbose)
145                                     dataLog(toString(Thread::current(), ": Checking production predicate with shouldContinue = ", shouldContinue, ", queue.size() == ", queue.size(), "\n"));
146                                 return queue.size() < maxQueueSize;
147                             },
148                             timeout);
149                         shouldNotify = queue.isEmpty();
150                         queue.append(i);
151                     }
152                     notify(notifyStyle, emptyCondition, shouldNotify);
153                 }
154             }));
155     }
156
157     for (auto& thread : producerThreads)
158         thread->waitForCompletion();
159
160     {
161         std::lock_guard<Lock> locker(lock);
162         shouldContinue = false;
163     }
164     emptyCondition.notifyAll();
165
166     for (auto& thread : consumerThreads)
167         thread->waitForCompletion();
168
169     EXPECT_EQ(numProducers * numMessagesPerProducer, received.size());
170     std::sort(received.begin(), received.end());
171     for (unsigned messageIndex = 0; messageIndex < numMessagesPerProducer; ++messageIndex) {
172         for (unsigned producerIndex = 0; producerIndex < numProducers; ++producerIndex)
173             EXPECT_EQ(messageIndex, received[messageIndex * numProducers + producerIndex]);
174     }
175 }
176
177 } // anonymous namespace
178
179 TEST(WTF_Condition, OneProducerOneConsumerOneSlot)
180 {
181     runTest(1, 1, 1, 100000, TacticallyNotifyAll);
182 }
183
184 TEST(WTF_Condition, OneProducerOneConsumerOneSlotTimeout)
185 {
186     runTest(
187         1, 1, 1, 100000, TacticallyNotifyAll,
188         Seconds::fromMilliseconds(10),
189         Seconds(1));
190 }
191
192 TEST(WTF_Condition, OneProducerOneConsumerHundredSlots)
193 {
194     runTest(1, 1, 100, 1000000, TacticallyNotifyAll);
195 }
196
197 TEST(WTF_Condition, TenProducersOneConsumerOneSlot)
198 {
199     runTest(10, 1, 1, 10000, TacticallyNotifyAll);
200 }
201
202 TEST(WTF_Condition, TenProducersOneConsumerHundredSlotsNotifyAll)
203 {
204     runTest(10, 1, 100, 10000, TacticallyNotifyAll);
205 }
206
207 TEST(WTF_Condition, TenProducersOneConsumerHundredSlotsNotifyOne)
208 {
209     runTest(10, 1, 100, 10000, AlwaysNotifyOne);
210 }
211
212 TEST(WTF_Condition, OneProducerTenConsumersOneSlot)
213 {
214     runTest(1, 10, 1, 10000, TacticallyNotifyAll);
215 }
216
217 TEST(WTF_Condition, OneProducerTenConsumersHundredSlotsNotifyAll)
218 {
219     runTest(1, 10, 100, 100000, TacticallyNotifyAll);
220 }
221
222 TEST(WTF_Condition, OneProducerTenConsumersHundredSlotsNotifyOne)
223 {
224     runTest(1, 10, 100, 100000, AlwaysNotifyOne);
225 }
226
227 TEST(WTF_Condition, TenProducersTenConsumersOneSlot)
228 {
229     runTest(10, 10, 1, 50000, TacticallyNotifyAll);
230 }
231
232 TEST(WTF_Condition, TenProducersTenConsumersHundredSlotsNotifyAll)
233 {
234     runTest(10, 10, 100, 50000, TacticallyNotifyAll);
235 }
236
237 TEST(WTF_Condition, TenProducersTenConsumersHundredSlotsNotifyOne)
238 {
239     runTest(10, 10, 100, 50000, AlwaysNotifyOne);
240 }
241
242 TEST(WTF_Condition, TimeoutTimesOut)
243 {
244     Lock lock;
245     Condition condition;
246
247     lock.lock();
248     bool result = condition.waitFor(
249         lock, Seconds::fromMilliseconds(10), [] () -> bool { return false; });
250     lock.unlock();
251
252     EXPECT_FALSE(result);
253 }
254
255 } // namespace TestWebKitAPI
256