1/*
2 * Copyright (C) 2017 Yusuke Suzuki <[email protected]>.
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 <wtf/Condition.h>
29#include <wtf/ThreadGroup.h>
30#include <wtf/Vector.h>
31
32namespace TestWebKitAPI {
33
34enum class Mode { Add, AddCurrentThread };
35static void testThreadGroup(Mode mode)
36{
37 std::shared_ptr<ThreadGroup> threadGroup = ThreadGroup::create();
38 unsigned numberOfThreads = 16;
39 unsigned waitingThreads = 0;
40 bool restarting = false;
41 Lock lock;
42 Condition condition;
43 Condition restartCondition;
44 Vector<Ref<Thread>> threads;
45
46 {
47 auto locker = holdLock(lock);
48 for (unsigned i = 0; i < numberOfThreads; ++i) {
49 Ref<Thread> thread = Thread::create("ThreadGroupWorker", [&] {
50 auto locker = holdLock(lock);
51 if (mode == Mode::AddCurrentThread)
52 threadGroup->addCurrentThread();
53 ++waitingThreads;
54 condition.notifyOne();
55 restartCondition.wait(lock, [&] {
56 return restarting;
57 });
58 });
59 if (mode == Mode::Add)
60 EXPECT_TRUE(threadGroup->add(thread.get()) == ThreadGroupAddResult::NewlyAdded);
61 threads.append(WTFMove(thread));
62 }
63
64 condition.wait(lock, [&] {
65 return waitingThreads == numberOfThreads;
66 });
67 }
68
69 {
70 auto threadGroupLocker = holdLock(threadGroup->getLock());
71 EXPECT_EQ(threads.size(), numberOfThreads);
72 EXPECT_EQ(threadGroup->threads(threadGroupLocker).size(), numberOfThreads);
73 {
74 auto locker = holdLock(lock);
75 restarting = true;
76 restartCondition.notifyAll();
77 }
78
79 // While holding ThreadGroup lock, threads do not exit.
80 WTF::sleep(100_ms);
81 EXPECT_EQ(threadGroup->threads(threadGroupLocker).size(), numberOfThreads);
82 }
83 {
84 for (auto& thread : threads)
85 thread->waitForCompletion();
86
87 auto threadGroupLocker = holdLock(threadGroup->getLock());
88 EXPECT_EQ(threadGroup->threads(threadGroupLocker).size(), 0u);
89 }
90}
91
92TEST(WTF, ThreadGroupAdd)
93{
94 testThreadGroup(Mode::Add);
95}
96
97TEST(WTF, ThreadGroupAddCurrentThread)
98{
99 testThreadGroup(Mode::AddCurrentThread);
100}
101
102TEST(WTF, ThreadGroupDoNotAddDeadThread)
103{
104 std::shared_ptr<ThreadGroup> threadGroup = ThreadGroup::create();
105 Ref<Thread> thread = Thread::create("ThreadGroupWorker", [&] { });
106 thread->waitForCompletion();
107 EXPECT_TRUE(threadGroup->add(thread.get()) == ThreadGroupAddResult::NotAdded);
108
109 auto threadGroupLocker = holdLock(threadGroup->getLock());
110 EXPECT_EQ(threadGroup->threads(threadGroupLocker).size(), 0u);
111}
112
113TEST(WTF, ThreadGroupAddDuplicateThreads)
114{
115 bool restarting = false;
116 Lock lock;
117 Condition restartCondition;
118 std::shared_ptr<ThreadGroup> threadGroup = ThreadGroup::create();
119 Ref<Thread> thread = Thread::create("ThreadGroupWorker", [&] {
120 auto locker = holdLock(lock);
121 restartCondition.wait(lock, [&] {
122 return restarting;
123 });
124 });
125 EXPECT_TRUE(threadGroup->add(thread.get()) == ThreadGroupAddResult::NewlyAdded);
126 EXPECT_TRUE(threadGroup->add(thread.get()) == ThreadGroupAddResult::AlreadyAdded);
127
128 {
129 auto threadGroupLocker = holdLock(threadGroup->getLock());
130 EXPECT_EQ(threadGroup->threads(threadGroupLocker).size(), 1u);
131 }
132
133 {
134 auto locker = holdLock(lock);
135 restarting = true;
136 restartCondition.notifyAll();
137 }
138 thread->waitForCompletion();
139 {
140 auto threadGroupLocker = holdLock(threadGroup->getLock());
141 EXPECT_EQ(threadGroup->threads(threadGroupLocker).size(), 0u);
142 }
143}
144
145} // namespace TestWebKitAPI
146