1/*
2 * Copyright (C) 2016-2019 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#include "config.h"
27#include <wtf/AutomaticThread.h>
28
29#include <wtf/DataLog.h>
30#include <wtf/Threading.h>
31
32namespace WTF {
33
34static constexpr bool verbose = false;
35
36Ref<AutomaticThreadCondition> AutomaticThreadCondition::create()
37{
38 return adoptRef(*new AutomaticThreadCondition);
39}
40
41AutomaticThreadCondition::AutomaticThreadCondition()
42{
43}
44
45AutomaticThreadCondition::~AutomaticThreadCondition()
46{
47}
48
49void AutomaticThreadCondition::notifyOne(const AbstractLocker& locker)
50{
51 for (AutomaticThread* thread : m_threads) {
52 if (thread->isWaiting(locker)) {
53 thread->notify(locker);
54 return;
55 }
56 }
57
58 for (AutomaticThread* thread : m_threads) {
59 if (!thread->hasUnderlyingThread(locker)) {
60 thread->start(locker);
61 return;
62 }
63 }
64
65 m_condition.notifyOne();
66}
67
68void AutomaticThreadCondition::notifyAll(const AbstractLocker& locker)
69{
70 m_condition.notifyAll();
71
72 for (AutomaticThread* thread : m_threads) {
73 if (thread->isWaiting(locker))
74 thread->notify(locker);
75 else if (!thread->hasUnderlyingThread(locker))
76 thread->start(locker);
77 }
78}
79
80void AutomaticThreadCondition::wait(Lock& lock)
81{
82 m_condition.wait(lock);
83}
84
85bool AutomaticThreadCondition::waitFor(Lock& lock, Seconds time)
86{
87 return m_condition.waitFor(lock, time);
88}
89
90void AutomaticThreadCondition::add(const AbstractLocker&, AutomaticThread* thread)
91{
92 ASSERT(!m_threads.contains(thread));
93 m_threads.append(thread);
94}
95
96void AutomaticThreadCondition::remove(const AbstractLocker&, AutomaticThread* thread)
97{
98 m_threads.removeFirst(thread);
99 ASSERT(!m_threads.contains(thread));
100}
101
102bool AutomaticThreadCondition::contains(const AbstractLocker&, AutomaticThread* thread)
103{
104 return m_threads.contains(thread);
105}
106
107AutomaticThread::AutomaticThread(const AbstractLocker& locker, Box<Lock> lock, Ref<AutomaticThreadCondition>&& condition, Seconds timeout)
108 : m_lock(lock)
109 , m_condition(WTFMove(condition))
110 , m_timeout(timeout)
111{
112 if (verbose)
113 dataLog(RawPointer(this), ": Allocated AutomaticThread.\n");
114 m_condition->add(locker, this);
115}
116
117AutomaticThread::~AutomaticThread()
118{
119 if (verbose)
120 dataLog(RawPointer(this), ": Deleting AutomaticThread.\n");
121 LockHolder locker(*m_lock);
122
123 // It's possible that we're in a waiting state with the thread shut down. This is a goofy way to
124 // die, but it could happen.
125 m_condition->remove(locker, this);
126}
127
128bool AutomaticThread::tryStop(const AbstractLocker&)
129{
130 if (!m_isRunning)
131 return true;
132 if (m_hasUnderlyingThread)
133 return false;
134 m_isRunning = false;
135 return true;
136}
137
138bool AutomaticThread::isWaiting(const AbstractLocker& locker)
139{
140 return hasUnderlyingThread(locker) && m_isWaiting;
141}
142
143bool AutomaticThread::notify(const AbstractLocker& locker)
144{
145 ASSERT_UNUSED(locker, hasUnderlyingThread(locker));
146 m_isWaiting = false;
147 return m_waitCondition.notifyOne();
148}
149
150void AutomaticThread::join()
151{
152 LockHolder locker(*m_lock);
153 while (m_isRunning)
154 m_isRunningCondition.wait(*m_lock);
155}
156
157void AutomaticThread::start(const AbstractLocker&)
158{
159 RELEASE_ASSERT(m_isRunning);
160
161 RefPtr<AutomaticThread> preserveThisForThread = this;
162
163 m_hasUnderlyingThread = true;
164
165 Thread::create(
166 name(),
167 [=] () {
168 if (verbose)
169 dataLog(RawPointer(this), ": Running automatic thread!\n");
170
171 RefPtr<AutomaticThread> thread = preserveThisForThread;
172 thread->threadDidStart();
173
174 if (!ASSERT_DISABLED) {
175 LockHolder locker(*m_lock);
176 ASSERT(m_condition->contains(locker, this));
177 }
178
179 auto stopImpl = [&] (const AbstractLocker& locker) {
180 thread->threadIsStopping(locker);
181 thread->m_hasUnderlyingThread = false;
182 };
183
184 auto stopPermanently = [&] (const AbstractLocker& locker) {
185 m_isRunning = false;
186 m_isRunningCondition.notifyAll();
187 stopImpl(locker);
188 };
189
190 auto stopForTimeout = [&] (const AbstractLocker& locker) {
191 stopImpl(locker);
192 };
193
194 for (;;) {
195 {
196 LockHolder locker(*m_lock);
197 for (;;) {
198 PollResult result = poll(locker);
199 if (result == PollResult::Work)
200 break;
201 if (result == PollResult::Stop)
202 return stopPermanently(locker);
203 RELEASE_ASSERT(result == PollResult::Wait);
204
205 // Shut the thread down after a timeout.
206 m_isWaiting = true;
207 bool awokenByNotify =
208 m_waitCondition.waitFor(*m_lock, m_timeout);
209 if (verbose && !awokenByNotify && !m_isWaiting)
210 dataLog(RawPointer(this), ": waitFor timed out, but notified via m_isWaiting flag!\n");
211 if (m_isWaiting && shouldSleep(locker)) {
212 m_isWaiting = false;
213 if (verbose)
214 dataLog(RawPointer(this), ": Going to sleep!\n");
215 // It's important that we don't release the lock until we have completely
216 // indicated that the thread is kaput. Otherwise we'll have a a notify
217 // race that manifests as a deadlock on VM shutdown.
218 return stopForTimeout(locker);
219 }
220 }
221 }
222
223 WorkResult result = work();
224 if (result == WorkResult::Stop) {
225 LockHolder locker(*m_lock);
226 return stopPermanently(locker);
227 }
228 RELEASE_ASSERT(result == WorkResult::Continue);
229 }
230 })->detach();
231}
232
233void AutomaticThread::threadDidStart()
234{
235}
236
237void AutomaticThread::threadIsStopping(const AbstractLocker&)
238{
239}
240
241} // namespace WTF
242
243