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#pragma once
27
28#include <wtf/Box.h>
29#include <wtf/Condition.h>
30#include <wtf/Lock.h>
31#include <wtf/ThreadSafeRefCounted.h>
32#include <wtf/Vector.h>
33
34namespace WTF {
35
36// Often, we create threads that have this as their body:
37//
38// for (;;) {
39// {
40// LockHolder locker(m_lock);
41// for (;;) {
42// [1] stuff that could break, return, or fall through;
43// m_condition.wait(m_lock);
44// }
45// }
46//
47// [2] do work;
48// }
49//
50// When we do this, we don't always do a good job of managing this thread's lifetime, which may lead
51// to this thread sitting around even when it is not needed.
52//
53// AutomaticThread is here to help you in these situations. It encapsulates a lock, a condition
54// variable, and a thread. It will automatically shut the thread down after a timeout of inactivity.
55// You use AutomaticThread by subclassing it, and put any state that is needed between [1] and [2]
56// in the subclass.
57//
58// The terminology we use is:
59//
60// [1] PollResult AutomaticThread::poll()
61// [2] WorkResult AutomaticThread::work()
62//
63// Note that poll() and work() may not be called on the same thread every time, since this will shut
64// down the thread as necessary. This is legal since m_condition.wait(m_lock) can drop the lock, and
65// so there is no reason to keep the thread around.
66
67class AutomaticThread;
68
69class AutomaticThreadCondition : public ThreadSafeRefCounted<AutomaticThreadCondition> {
70public:
71 static WTF_EXPORT_PRIVATE Ref<AutomaticThreadCondition> create();
72
73 WTF_EXPORT_PRIVATE ~AutomaticThreadCondition();
74
75 WTF_EXPORT_PRIVATE void notifyOne(const AbstractLocker&);
76 WTF_EXPORT_PRIVATE void notifyAll(const AbstractLocker&);
77
78 // You can reuse this condition for other things, just as you would any other condition.
79 // However, since conflating conditions could lead to thundering herd, it's best to avoid it.
80 // One known-good case for one-true-condition is when the communication involves just two
81 // threads. In such cases, the thread doing the notifyAll() can wake up at most one thread -
82 // its partner.
83 WTF_EXPORT_PRIVATE void wait(Lock&);
84 WTF_EXPORT_PRIVATE bool waitFor(Lock&, Seconds);
85
86private:
87 friend class AutomaticThread;
88
89 WTF_EXPORT_PRIVATE AutomaticThreadCondition();
90
91 void add(const AbstractLocker&, AutomaticThread*);
92 void remove(const AbstractLocker&, AutomaticThread*);
93 bool contains(const AbstractLocker&, AutomaticThread*);
94
95 Condition m_condition;
96 Vector<AutomaticThread*> m_threads;
97};
98
99class WTF_EXPORT_PRIVATE AutomaticThread : public ThreadSafeRefCounted<AutomaticThread> {
100public:
101 // Note that if you drop all of your references to an AutomaticThread then as soon as there is a
102 // timeout during which it doesn't get woken up, it will simply die on its own. This is a
103 // permanent kind of death where the AutomaticThread object goes away, rather than the temporary
104 // kind of death where AutomaticThread lives but its underlying thread dies. All you have to do
105 // to prevent permanent death is keep a ref to AutomaticThread. At time of writing, every user of
106 // AutomaticThread keeps a ref to it and does join() as part of the shutdown process, so only the
107 // temporary kind of automatic death happens in practice. We keep the permanent death feature
108 // because it leads to an easy-to-understand reference counting discipline (AutomaticThread holds
109 // strong ref to AutomaticThreadCondition and the underlying thread holds a strong ref to
110 // AutomaticThread).
111 virtual ~AutomaticThread();
112
113 // Sometimes it's possible to optimize for the case that there is no underlying thread.
114 bool hasUnderlyingThread(const AbstractLocker&) const { return m_hasUnderlyingThread; }
115
116 // This attempts to quickly stop the thread. This will succeed if the thread happens to not be
117 // running. Returns true if the thread has been stopped. A good idiom for stopping your automatic
118 // thread is to first try this, and if that doesn't work, to tell the thread using your own
119 // mechanism (set some flag and then notify the condition).
120 bool tryStop(const AbstractLocker&);
121
122 bool isWaiting(const AbstractLocker&);
123
124 bool notify(const AbstractLocker&);
125
126 void join();
127
128 virtual const char* name() const { return "WTF::AutomaticThread"; }
129
130protected:
131 // This logically creates the thread, but in reality the thread won't be created until someone
132 // calls AutomaticThreadCondition::notifyOne() or notifyAll().
133 AutomaticThread(const AbstractLocker&, Box<Lock>, Ref<AutomaticThreadCondition>&&, Seconds timeout = 10_s);
134
135 // To understand PollResult and WorkResult, imagine that poll() and work() are being called like
136 // so:
137 //
138 // void AutomaticThread::runThread()
139 // {
140 // for (;;) {
141 // {
142 // LockHolder locker(m_lock);
143 // for (;;) {
144 // PollResult result = poll();
145 // if (result == PollResult::Work)
146 // break;
147 // if (result == PollResult::Stop)
148 // return;
149 // RELEASE_ASSERT(result == PollResult::Wait);
150 // m_condition.wait(m_lock);
151 // }
152 // }
153 //
154 // WorkResult result = work();
155 // if (result == WorkResult::Stop)
156 // return;
157 // RELEASE_ASSERT(result == WorkResult::Continue);
158 // }
159 // }
160
161 enum class PollResult { Work, Stop, Wait };
162 virtual PollResult poll(const AbstractLocker&) = 0;
163
164 enum class WorkResult { Continue, Stop };
165 virtual WorkResult work() = 0;
166
167 // It's sometimes useful to allocate resources while the thread is running, and to destroy them
168 // when the thread dies. These methods let you do this. You can override these methods, and you
169 // can be sure that the default ones don't do anything (so you don't need a super call).
170 virtual void threadDidStart();
171 virtual void threadIsStopping(const AbstractLocker&);
172
173 // Control whether this automatic thread should sleep when timeout happens.
174 // By overriding this function, we can customize how automatic threads will sleep.
175 // For example, when you have thread pool, you can decrease active threads moderately.
176 virtual bool shouldSleep(const AbstractLocker&) { return true; }
177
178private:
179 friend class AutomaticThreadCondition;
180
181 void start(const AbstractLocker&);
182
183 Box<Lock> m_lock;
184 Ref<AutomaticThreadCondition> m_condition;
185 Seconds m_timeout;
186 bool m_isRunning { true };
187 bool m_isWaiting { false };
188 bool m_hasUnderlyingThread { false };
189 Condition m_waitCondition;
190 Condition m_isRunningCondition;
191};
192
193} // namespace WTF
194
195using WTF::AutomaticThread;
196using WTF::AutomaticThreadCondition;
197