[MSE][GStreamer] Introduce AbortableTaskQueue
[WebKit-https.git] / Tools / TestWebKitAPI / Tests / WebCore / AbortableTaskQueue.cpp
1 /*
2  * Copyright (C) 2018 Igalia, S.L.
3  * Copyright (C) 2018 Metrological Group B.V.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
15  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
18  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
24  * THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include "config.h"
28
29 #include <AbortableTaskQueue.h>
30 #include <Utilities.h>
31 #include <wtf/Threading.h>
32
33 using namespace WebCore;
34
35 namespace TestWebKitAPI {
36
37 TEST(AbortableTaskQueue, AsyncTasks)
38 {
39     AbortableTaskQueue taskQueue;
40     bool testFinished { false };
41     int currentStep { 0 };
42     RunLoop::initializeMainRunLoop();
43
44     auto backgroundThreadFunction = [&]() {
45         EXPECT_FALSE(isMainThread());
46         taskQueue.enqueueTask([&]() {
47             EXPECT_TRUE(isMainThread());
48             currentStep++;
49             EXPECT_EQ(1, currentStep);
50         });
51         taskQueue.enqueueTask([&]() {
52             EXPECT_TRUE(isMainThread());
53             currentStep++;
54             EXPECT_EQ(2, currentStep);
55             testFinished = true;
56         });
57     };
58     RunLoop::current().dispatch([backgroundThreadFunction = WTFMove(backgroundThreadFunction)]() mutable {
59         WTF::Thread::create("atq-background", WTFMove(backgroundThreadFunction))->detach();
60     });
61
62     Util::run(&testFinished);
63 }
64
65 struct FancyResponse {
66     WTF_MAKE_NONCOPYABLE(FancyResponse);
67 public:
68     FancyResponse(int fancyInt)
69         : fancyInt(fancyInt)
70     { }
71
72     FancyResponse(FancyResponse&& a)
73         : fancyInt(a.fancyInt)
74     { }
75
76     FancyResponse& operator=(FancyResponse&& a)
77     {
78         this->fancyInt = a.fancyInt;
79         return *this;
80     }
81
82     int fancyInt;
83 };
84
85 TEST(AbortableTaskQueue, SyncTasks)
86 {
87     AbortableTaskQueue taskQueue;
88     bool testFinished { false };
89     int currentStep { 0 };
90     RunLoop::initializeMainRunLoop();
91
92     auto backgroundThreadFunction = [&]() {
93         EXPECT_FALSE(isMainThread());
94         std::optional<FancyResponse> response = taskQueue.enqueueTaskAndWait<FancyResponse>([&]() -> FancyResponse {
95             EXPECT_TRUE(isMainThread());
96             currentStep++;
97             EXPECT_EQ(1, currentStep);
98             FancyResponse returnValue(100);
99             return returnValue;
100         });
101         currentStep++;
102         EXPECT_EQ(2, currentStep);
103         EXPECT_TRUE(response);
104         EXPECT_EQ(100, response->fancyInt);
105         RunLoop::main().dispatch([&]() {
106             testFinished = true;
107         });
108     };
109     RunLoop::current().dispatch([backgroundThreadFunction = WTFMove(backgroundThreadFunction)]() mutable {
110         WTF::Thread::create("atq-background", WTFMove(backgroundThreadFunction))->detach();
111     });
112
113     Util::run(&testFinished);
114 }
115
116 template <typename ThreadEnum>
117 class DeterministicScheduler {
118     WTF_MAKE_NONCOPYABLE(DeterministicScheduler);
119 public:
120     DeterministicScheduler(ThreadEnum firstThread)
121         : m_currentThread(firstThread)
122     { }
123
124     class ThreadContext {
125         WTF_MAKE_NONCOPYABLE(ThreadContext);
126     public:
127         ThreadContext(DeterministicScheduler& scheduler, ThreadEnum thisThread)
128             : m_scheduler(scheduler), m_thisThread(thisThread)
129         { }
130
131         void waitMyTurn()
132         {
133             LockHolder lock(m_scheduler.m_mutex);
134             m_scheduler.m_currentThreadChanged.wait(m_scheduler.m_mutex, [this]() {
135                 return m_scheduler.m_currentThread == m_thisThread;
136             });
137         }
138
139         void yieldToThread(ThreadEnum nextThread)
140         {
141             LockHolder lock(m_scheduler.m_mutex);
142             m_scheduler.m_currentThread = nextThread;
143             m_scheduler.m_currentThreadChanged.notifyAll();
144         }
145
146     private:
147         DeterministicScheduler& m_scheduler;
148         ThreadEnum m_thisThread;
149     };
150
151
152 private:
153     ThreadEnum m_currentThread;
154     Condition m_currentThreadChanged;
155     Lock m_mutex;
156 };
157
158 enum class TestThread {
159     Main,
160     Background
161 };
162
163 TEST(AbortableTaskQueue, Abort)
164 {
165     DeterministicScheduler<TestThread> scheduler(TestThread::Background);
166
167     AbortableTaskQueue taskQueue;
168     bool testFinished { false };
169     RunLoop::initializeMainRunLoop();
170
171     auto backgroundThreadFunction = [&]() {
172         EXPECT_FALSE(isMainThread());
173         DeterministicScheduler<TestThread>::ThreadContext backgroundThreadContext(scheduler, TestThread::Background);
174
175         taskQueue.enqueueTask([]() {
176             // This task should not have been able to run under the scheduling of this test.
177             EXPECT_TRUE(false);
178         });
179         backgroundThreadContext.yieldToThread(TestThread::Main);
180         backgroundThreadContext.waitMyTurn();
181
182         // Main thread has called startAborting().
183
184         taskQueue.enqueueTask([]() {
185             // This task should not have been able to run under the scheduling of this test.
186             EXPECT_TRUE(false);
187         });
188         // This call must return immediately because we are aborting.
189         std::optional<FancyResponse> response = taskQueue.enqueueTaskAndWait<FancyResponse>([]() -> FancyResponse {
190             // This task should not have been able to run under the scheduling of this test.
191             EXPECT_TRUE(false);
192             return FancyResponse(100);
193         });
194         EXPECT_FALSE(response);
195         backgroundThreadContext.yieldToThread(TestThread::Main);
196         backgroundThreadContext.waitMyTurn();
197
198         // Main thread has called finishAborting().
199
200         taskQueue.enqueueTask([&]() {
201             testFinished = true;
202         });
203     };
204     RunLoop::current().dispatch([&, backgroundThreadFunction = WTFMove(backgroundThreadFunction)]() mutable {
205         EXPECT_TRUE(isMainThread());
206         DeterministicScheduler<TestThread>::ThreadContext mainThreadContext(scheduler, TestThread::Main);
207         WTF::Thread::create("atq-background", WTFMove(backgroundThreadFunction))->detach();
208
209         mainThreadContext.waitMyTurn();
210
211         taskQueue.startAborting();
212         mainThreadContext.yieldToThread(TestThread::Background);
213         mainThreadContext.waitMyTurn();
214
215         taskQueue.finishAborting();
216         mainThreadContext.yieldToThread(TestThread::Background);
217     });
218
219     Util::run(&testFinished);
220 }
221
222 TEST(AbortableTaskQueue, AbortDuringSyncTask)
223 {
224     AbortableTaskQueue taskQueue;
225     bool testFinished { false };
226     RunLoop::initializeMainRunLoop();
227
228     auto backgroundThreadFunction = [&]() {
229         EXPECT_FALSE(isMainThread());
230
231         std::optional<FancyResponse> response = taskQueue.enqueueTaskAndWait<FancyResponse>([]() -> FancyResponse {
232             // This task should not have been able to run under the scheduling of this test.
233             EXPECT_TRUE(false);
234             return FancyResponse(100);
235         });
236
237         // Main thread has called startAborting().
238         EXPECT_FALSE(response);
239
240         RunLoop::main().dispatch([&]() {
241             testFinished = true;
242         });
243     };
244     RunLoop::current().dispatch([&, backgroundThreadFunction = WTFMove(backgroundThreadFunction)]() mutable {
245         EXPECT_TRUE(isMainThread());
246         WTF::Thread::create("atq-background", WTFMove(backgroundThreadFunction))->detach();
247
248         // Give the background thread a bit of time to get blocked waiting for a response.
249         WTF::sleep(100_ms);
250
251         taskQueue.startAborting();
252     });
253
254     Util::run(&testFinished);
255 }
256
257 } // namespace TestWebKitAPI