| /* |
| * Copyright (C) 2016-2020 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #pragma once |
| |
| #include <wtf/Box.h> |
| #include <wtf/Condition.h> |
| #include <wtf/Lock.h> |
| #include <wtf/ThreadSafeRefCounted.h> |
| #include <wtf/Threading.h> |
| #include <wtf/Vector.h> |
| |
| namespace WTF { |
| |
| // Often, we create threads that have this as their body: |
| // |
| // for (;;) { |
| // { |
| // Locker locker { m_lock }; |
| // for (;;) { |
| // [1] stuff that could break, return, or fall through; |
| // m_condition.wait(m_lock); |
| // } |
| // } |
| // |
| // [2] do work; |
| // } |
| // |
| // When we do this, we don't always do a good job of managing this thread's lifetime, which may lead |
| // to this thread sitting around even when it is not needed. |
| // |
| // AutomaticThread is here to help you in these situations. It encapsulates a lock, a condition |
| // variable, and a thread. It will automatically shut the thread down after a timeout of inactivity. |
| // You use AutomaticThread by subclassing it, and put any state that is needed between [1] and [2] |
| // in the subclass. |
| // |
| // The terminology we use is: |
| // |
| // [1] PollResult AutomaticThread::poll() |
| // [2] WorkResult AutomaticThread::work() |
| // |
| // Note that poll() and work() may not be called on the same thread every time, since this will shut |
| // down the thread as necessary. This is legal since m_condition.wait(m_lock) can drop the lock, and |
| // so there is no reason to keep the thread around. |
| |
| class AutomaticThread; |
| |
| class AutomaticThreadCondition : public ThreadSafeRefCounted<AutomaticThreadCondition> { |
| public: |
| static WTF_EXPORT_PRIVATE Ref<AutomaticThreadCondition> create(); |
| |
| WTF_EXPORT_PRIVATE ~AutomaticThreadCondition(); |
| |
| WTF_EXPORT_PRIVATE void notifyOne(const AbstractLocker&); |
| WTF_EXPORT_PRIVATE void notifyAll(const AbstractLocker&); |
| |
| // You can reuse this condition for other things, just as you would any other condition. |
| // However, since conflating conditions could lead to thundering herd, it's best to avoid it. |
| // One known-good case for one-true-condition is when the communication involves just two |
| // threads. In such cases, the thread doing the notifyAll() can wake up at most one thread - |
| // its partner. |
| WTF_EXPORT_PRIVATE void wait(Lock& lock) WTF_REQUIRES_LOCK(lock); |
| WTF_EXPORT_PRIVATE bool waitFor(Lock& lock, Seconds) WTF_REQUIRES_LOCK(lock); |
| |
| private: |
| friend class AutomaticThread; |
| |
| WTF_EXPORT_PRIVATE AutomaticThreadCondition(); |
| |
| void add(const AbstractLocker&, AutomaticThread*); |
| void remove(const AbstractLocker&, AutomaticThread*); |
| bool contains(const AbstractLocker&, AutomaticThread*); |
| |
| Condition m_condition; |
| Vector<AutomaticThread*> m_threads; |
| }; |
| |
| class WTF_EXPORT_PRIVATE AutomaticThread : public ThreadSafeRefCounted<AutomaticThread> { |
| public: |
| // Note that if you drop all of your references to an AutomaticThread then as soon as there is a |
| // timeout during which it doesn't get woken up, it will simply die on its own. This is a |
| // permanent kind of death where the AutomaticThread object goes away, rather than the temporary |
| // kind of death where AutomaticThread lives but its underlying thread dies. All you have to do |
| // to prevent permanent death is keep a ref to AutomaticThread. At time of writing, every user of |
| // AutomaticThread keeps a ref to it and does join() as part of the shutdown process, so only the |
| // temporary kind of automatic death happens in practice. We keep the permanent death feature |
| // because it leads to an easy-to-understand reference counting discipline (AutomaticThread holds |
| // strong ref to AutomaticThreadCondition and the underlying thread holds a strong ref to |
| // AutomaticThread). |
| virtual ~AutomaticThread(); |
| |
| // Sometimes it's possible to optimize for the case that there is no underlying thread. |
| bool hasUnderlyingThread(const AbstractLocker&) const { return m_hasUnderlyingThread; } |
| |
| // This attempts to quickly stop the thread. This will succeed if the thread happens to not be |
| // running. Returns true if the thread has been stopped. A good idiom for stopping your automatic |
| // thread is to first try this, and if that doesn't work, to tell the thread using your own |
| // mechanism (set some flag and then notify the condition). |
| bool tryStop(const AbstractLocker&); |
| |
| bool isWaiting(const AbstractLocker&); |
| |
| bool notify(const AbstractLocker&); |
| |
| void join(); |
| |
| virtual const char* name() const { return "WTF::AutomaticThread"; } |
| |
| protected: |
| // This logically creates the thread, but in reality the thread won't be created until someone |
| // calls AutomaticThreadCondition::notifyOne() or notifyAll(). |
| AutomaticThread(const AbstractLocker&, Box<Lock>, Ref<AutomaticThreadCondition>&&, Seconds timeout = 10_s); |
| |
| AutomaticThread(const AbstractLocker&, Box<Lock>, Ref<AutomaticThreadCondition>&&, ThreadType, Seconds timeout = 10_s); |
| |
| // To understand PollResult and WorkResult, imagine that poll() and work() are being called like |
| // so: |
| // |
| // void AutomaticThread::runThread() |
| // { |
| // for (;;) { |
| // { |
| // Locker locker { m_lock }; |
| // for (;;) { |
| // PollResult result = poll(); |
| // if (result == PollResult::Work) |
| // break; |
| // if (result == PollResult::Stop) |
| // return; |
| // RELEASE_ASSERT(result == PollResult::Wait); |
| // m_condition.wait(m_lock); |
| // } |
| // } |
| // |
| // WorkResult result = work(); |
| // if (result == WorkResult::Stop) |
| // return; |
| // RELEASE_ASSERT(result == WorkResult::Continue); |
| // } |
| // } |
| |
| enum class PollResult { Work, Stop, Wait }; |
| virtual PollResult poll(const AbstractLocker&) = 0; |
| |
| enum class WorkResult { Continue, Stop }; |
| virtual WorkResult work() = 0; |
| |
| // It's sometimes useful to allocate resources while the thread is running, and to destroy them |
| // when the thread dies. These methods let you do this. You can override these methods, and you |
| // can be sure that the default ones don't do anything (so you don't need a super call). |
| virtual void threadDidStart(); |
| virtual void threadIsStopping(const AbstractLocker&); |
| |
| // Control whether this automatic thread should sleep when timeout happens. |
| // By overriding this function, we can customize how automatic threads will sleep. |
| // For example, when you have thread pool, you can decrease active threads moderately. |
| virtual bool shouldSleep(const AbstractLocker&) { return true; } |
| |
| private: |
| friend class AutomaticThreadCondition; |
| |
| void start(const AbstractLocker&); |
| |
| Box<Lock> m_lock; |
| Ref<AutomaticThreadCondition> m_condition; |
| Seconds m_timeout; |
| ThreadType m_threadType { ThreadType::Unknown }; |
| bool m_isRunning { true }; |
| bool m_isWaiting { false }; |
| bool m_hasUnderlyingThread { false }; |
| Condition m_waitCondition; |
| Condition m_isRunningCondition; |
| }; |
| |
| } // namespace WTF |
| |
| using WTF::AutomaticThread; |
| using WTF::AutomaticThreadCondition; |