Unreviewed, rolling out r234489.
[WebKit-https.git] / Source / WTF / wtf / AutomaticThread.cpp
1 /*
2  * Copyright (C) 2016-2017 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 "AutomaticThread.h"
28
29 #include "DataLog.h"
30 #include "Threading.h"
31
32 namespace WTF {
33
34 static const bool verbose = false;
35
36 Ref<AutomaticThreadCondition> AutomaticThreadCondition::create()
37 {
38     return adoptRef(*new AutomaticThreadCondition);
39 }
40
41 AutomaticThreadCondition::AutomaticThreadCondition()
42 {
43 }
44
45 AutomaticThreadCondition::~AutomaticThreadCondition()
46 {
47 }
48
49 void 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
68 void 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
80 void AutomaticThreadCondition::wait(Lock& lock)
81 {
82     m_condition.wait(lock);
83 }
84
85 bool AutomaticThreadCondition::waitFor(Lock& lock, Seconds time)
86 {
87     return m_condition.waitFor(lock, time);
88 }
89
90 void AutomaticThreadCondition::add(const AbstractLocker&, AutomaticThread* thread)
91 {
92     ASSERT(!m_threads.contains(thread));
93     m_threads.append(thread);
94 }
95
96 void AutomaticThreadCondition::remove(const AbstractLocker&, AutomaticThread* thread)
97 {
98     m_threads.removeFirst(thread);
99     ASSERT(!m_threads.contains(thread));
100 }
101
102 bool AutomaticThreadCondition::contains(const AbstractLocker&, AutomaticThread* thread)
103 {
104     return m_threads.contains(thread);
105 }
106
107 AutomaticThread::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
117 AutomaticThread::~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
128 bool 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
138 bool AutomaticThread::isWaiting(const AbstractLocker& locker)
139 {
140     return hasUnderlyingThread(locker) && m_isWaiting;
141 }
142
143 bool AutomaticThread::notify(const AbstractLocker& locker)
144 {
145     ASSERT_UNUSED(locker, hasUnderlyingThread(locker));
146     m_isWaiting = false;
147     return m_waitCondition.notifyOne();
148 }
149
150 void AutomaticThread::join()
151 {
152     LockHolder locker(*m_lock);
153     while (m_isRunning)
154         m_isRunningCondition.wait(*m_lock);
155 }
156
157 void 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
233 void AutomaticThread::threadDidStart()
234 {
235 }
236
237 void AutomaticThread::threadIsStopping(const AbstractLocker&)
238 {
239 }
240
241 } // namespace WTF
242