[WTF] Use Semaphore and BinarySemaphore instead of dispatch_semaphore_t
[WebKit-https.git] / Tools / TestWebKitAPI / Tests / WTF / SynchronizedFixedQueue.cpp
1 /*
2  * Copyright (C) 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
28 #include <chrono>
29 #include <thread>
30
31 #include <wtf/ASCIICType.h>
32 #include <wtf/SynchronizedFixedQueue.h>
33 #include <wtf/WorkQueue.h>
34 #include <wtf/text/CString.h>
35 #include <wtf/threads/BinarySemaphore.h>
36
37 namespace TestWebKitAPI {
38
39 static char const* textItem(size_t index)
40 {
41     static char const* items[] = { "first", "second", "third", "fourth", "fifth", "sixth" };
42     return index < sizeof(items) / sizeof(items[0]) ? items[index] : nullptr;
43 }
44
45 static CString toUpper(const CString& lower)
46 {
47     CString upper = lower;
48
49     for (char* buffer = upper.mutableData(); *buffer; ++buffer)
50         *buffer = toASCIIUpper(*buffer);
51
52     return upper;
53 }
54
55 template <size_t BufferSize>
56 class ToUpperConverter {
57 public:
58     ToUpperConverter()
59         : m_lowerQueue(SynchronizedFixedQueue<CString, BufferSize>::create())
60         , m_upperQueue(SynchronizedFixedQueue<CString, BufferSize>::create())
61     {
62     }
63
64     WorkQueue* produceQueue()
65     {
66         if (!m_produceQueue)
67             m_produceQueue = WorkQueue::create("org.webkit.Produce");
68         return m_produceQueue.get();
69     }
70
71     WorkQueue* consumeQueue()
72     {
73         if (!m_consumeQueue)
74             m_consumeQueue = WorkQueue::create("org.webkit.Consume");
75         return m_consumeQueue.get();
76     }
77
78     void startProducing()
79     {
80         if (isProducing())
81             return;
82
83         produceQueue()->dispatch([this] {
84             CString lower;
85             while (m_lowerQueue->dequeue(lower)) {
86                 m_upperQueue->enqueue(toUpper(lower));
87                 EXPECT_TRUE(lower == textItem(m_produceCount++));
88 #if PLATFORM(WIN)
89                 auto sleepAmount = std::chrono::milliseconds(20);
90 #else
91                 auto sleepAmount = std::chrono::milliseconds(10);
92 #endif
93                 std::this_thread::sleep_for(sleepAmount);
94             }
95             m_produceCloseSemaphore.signal();
96         });
97     }
98
99     void startConsuming()
100     {
101         if (isConsuming())
102             return;
103
104         consumeQueue()->dispatch([this] {
105             CString upper;
106             while (m_upperQueue->dequeue(upper)) {
107                 EXPECT_TRUE(upper == toUpper(textItem(m_consumeCount++)));
108                 std::this_thread::sleep_for(std::chrono::milliseconds(50));
109             }
110             m_consumeCloseSemaphore.signal();
111         });
112     }
113     
114     void start()
115     {
116         startProducing();
117         startConsuming();
118     }
119
120     void stopProducing()
121     {
122         if (!isProducing())
123             return;
124
125         m_lowerQueue->close();
126         m_produceCloseSemaphore.wait();
127         m_produceQueue = nullptr;
128     }
129     
130     void stopConsuming()
131     {
132         if (!isConsuming())
133             return;
134
135         m_upperQueue->close();
136         m_consumeCloseSemaphore.wait();
137         m_consumeQueue = nullptr;
138     }
139     
140     void stop()
141     {
142         stopProducing();
143         stopConsuming();
144     }
145
146     void enqueueLower(const CString& lower)
147     {
148         m_lowerQueue->enqueue(lower);
149     }
150
151     bool isProducing() { return m_produceQueue; }
152     bool isConsuming() { return m_consumeQueue; }
153
154     size_t produceCount() const { return m_produceCount; }
155     size_t consumeCount() const { return m_consumeCount; }
156
157 private:
158     Ref<SynchronizedFixedQueue<CString, BufferSize>> m_lowerQueue;
159     Ref<SynchronizedFixedQueue<CString, BufferSize>> m_upperQueue;
160     RefPtr<WorkQueue> m_produceQueue;
161     RefPtr<WorkQueue> m_consumeQueue;
162     BinarySemaphore m_produceCloseSemaphore;
163     BinarySemaphore m_consumeCloseSemaphore;
164     size_t m_produceCount { 0 };
165     size_t m_consumeCount { 0 };
166 };
167
168 TEST(WTF_SynchronizedFixedQueue, Basic)
169 {
170     ToUpperConverter<4U> converter;
171
172     converter.start();
173     EXPECT_TRUE(converter.isProducing() && converter.isConsuming());
174
175     converter.stop();
176     EXPECT_FALSE(converter.isProducing() || converter.isConsuming());
177
178     EXPECT_EQ(converter.produceCount(), 0U);
179     EXPECT_EQ(converter.consumeCount(), 0U);
180 }
181
182 TEST(WTF_SynchronizedFixedQueue, ProduceOnly)
183 {
184     ToUpperConverter<4U> converter;
185     
186     converter.startProducing();
187     EXPECT_TRUE(converter.isProducing() && !converter.isConsuming());
188
189     size_t count = 0;
190     while (char const* item = textItem(count)) {
191         converter.enqueueLower(item);
192         ++count;
193         
194         std::this_thread::sleep_for(std::chrono::milliseconds(1));
195     }
196
197     converter.stop();
198     EXPECT_FALSE(converter.isProducing() || converter.isConsuming());
199 }
200
201 TEST(WTF_SynchronizedFixedQueue, ConsumeOnly)
202 {
203     ToUpperConverter<4U> converter;
204     
205     converter.startConsuming();
206     EXPECT_TRUE(!converter.isProducing() && converter.isConsuming());
207     
208     converter.stop();
209     EXPECT_FALSE(converter.isProducing() || converter.isConsuming());
210 }
211
212 TEST(WTF_SynchronizedFixedQueue, Limits)
213 {
214     ToUpperConverter<4U> converter;
215
216     converter.start();
217     EXPECT_TRUE(converter.isProducing() && converter.isConsuming());
218
219     size_t count = 0;
220     while (char const* item = textItem(count)) {
221         converter.enqueueLower(item);
222         ++count;
223
224         std::this_thread::sleep_for(std::chrono::milliseconds(1));
225     }
226
227     std::this_thread::sleep_for(std::chrono::milliseconds(400));
228
229     converter.stop();
230     EXPECT_FALSE(converter.isProducing() || converter.isConsuming());
231     
232     EXPECT_EQ(converter.produceCount(), count);
233     EXPECT_EQ(converter.consumeCount(), count);
234 }
235
236 }