summaryrefslogtreecommitdiff
path: root/Source/WTF/wtf/AutomaticThread.cpp
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
commit1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch)
tree46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WTF/wtf/AutomaticThread.cpp
parent32761a6cee1d0dee366b885b7b9c777e67885688 (diff)
downloadWebKitGtk-tarball-master.tar.gz
Diffstat (limited to 'Source/WTF/wtf/AutomaticThread.cpp')
-rw-r--r--Source/WTF/wtf/AutomaticThread.cpp235
1 files changed, 235 insertions, 0 deletions
diff --git a/Source/WTF/wtf/AutomaticThread.cpp b/Source/WTF/wtf/AutomaticThread.cpp
new file mode 100644
index 000000000..387c6e25d
--- /dev/null
+++ b/Source/WTF/wtf/AutomaticThread.cpp
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2016-2017 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.
+ */
+
+#include "config.h"
+#include "AutomaticThread.h"
+
+#include "DataLog.h"
+
+namespace WTF {
+
+static const bool verbose = false;
+
+RefPtr<AutomaticThreadCondition> AutomaticThreadCondition::create()
+{
+ return adoptRef(new AutomaticThreadCondition());
+}
+
+AutomaticThreadCondition::AutomaticThreadCondition()
+{
+}
+
+AutomaticThreadCondition::~AutomaticThreadCondition()
+{
+}
+
+void AutomaticThreadCondition::notifyOne(const AbstractLocker& locker)
+{
+ for (AutomaticThread* thread : m_threads) {
+ if (thread->isWaiting(locker)) {
+ thread->notify(locker);
+ return;
+ }
+ }
+
+ for (AutomaticThread* thread : m_threads) {
+ if (!thread->hasUnderlyingThread(locker)) {
+ thread->start(locker);
+ return;
+ }
+ }
+
+ m_condition.notifyOne();
+}
+
+void AutomaticThreadCondition::notifyAll(const AbstractLocker& locker)
+{
+ m_condition.notifyAll();
+
+ for (AutomaticThread* thread : m_threads) {
+ if (thread->isWaiting(locker))
+ thread->notify(locker);
+ else if (!thread->hasUnderlyingThread(locker))
+ thread->start(locker);
+ }
+}
+
+void AutomaticThreadCondition::wait(Lock& lock)
+{
+ m_condition.wait(lock);
+}
+
+void AutomaticThreadCondition::add(const AbstractLocker&, AutomaticThread* thread)
+{
+ ASSERT(!m_threads.contains(thread));
+ m_threads.append(thread);
+}
+
+void AutomaticThreadCondition::remove(const AbstractLocker&, AutomaticThread* thread)
+{
+ m_threads.removeFirst(thread);
+ ASSERT(!m_threads.contains(thread));
+}
+
+bool AutomaticThreadCondition::contains(const AbstractLocker&, AutomaticThread* thread)
+{
+ return m_threads.contains(thread);
+}
+
+AutomaticThread::AutomaticThread(const AbstractLocker& locker, Box<Lock> lock, RefPtr<AutomaticThreadCondition> condition)
+ : m_lock(lock)
+ , m_condition(condition)
+{
+ if (verbose)
+ dataLog(RawPointer(this), ": Allocated AutomaticThread.\n");
+ m_condition->add(locker, this);
+}
+
+AutomaticThread::~AutomaticThread()
+{
+ if (verbose)
+ dataLog(RawPointer(this), ": Deleting AutomaticThread.\n");
+ LockHolder locker(*m_lock);
+
+ // It's possible that we're in a waiting state with the thread shut down. This is a goofy way to
+ // die, but it could happen.
+ m_condition->remove(locker, this);
+}
+
+bool AutomaticThread::tryStop(const AbstractLocker&)
+{
+ if (!m_isRunning)
+ return true;
+ if (m_hasUnderlyingThread)
+ return false;
+ m_isRunning = false;
+ return true;
+}
+
+bool AutomaticThread::isWaiting(const AbstractLocker& locker)
+{
+ return hasUnderlyingThread(locker) && m_isWaiting;
+}
+
+bool AutomaticThread::notify(const AbstractLocker& locker)
+{
+ ASSERT_UNUSED(locker, hasUnderlyingThread(locker));
+ m_isWaiting = false;
+ return m_waitCondition.notifyOne();
+}
+
+void AutomaticThread::join()
+{
+ LockHolder locker(*m_lock);
+ while (m_isRunning)
+ m_isRunningCondition.wait(*m_lock);
+}
+
+void AutomaticThread::start(const AbstractLocker&)
+{
+ RELEASE_ASSERT(m_isRunning);
+
+ RefPtr<AutomaticThread> preserveThisForThread = this;
+
+ m_hasUnderlyingThread = true;
+
+ ThreadIdentifier thread = createThread(
+ "WTF::AutomaticThread",
+ [=] () {
+ if (verbose)
+ dataLog(RawPointer(this), ": Running automatic thread!\n");
+
+ RefPtr<AutomaticThread> thread = preserveThisForThread;
+ thread->threadDidStart();
+
+ if (!ASSERT_DISABLED) {
+ LockHolder locker(*m_lock);
+ ASSERT(m_condition->contains(locker, this));
+ }
+
+ auto stopImpl = [&] (const AbstractLocker& locker) {
+ thread->threadIsStopping(locker);
+ thread->m_hasUnderlyingThread = false;
+ };
+
+ auto stopPermanently = [&] (const AbstractLocker& locker) {
+ m_isRunning = false;
+ m_isRunningCondition.notifyAll();
+ stopImpl(locker);
+ };
+
+ auto stopForTimeout = [&] (const AbstractLocker& locker) {
+ stopImpl(locker);
+ };
+
+ for (;;) {
+ {
+ LockHolder locker(*m_lock);
+ for (;;) {
+ PollResult result = poll(locker);
+ if (result == PollResult::Work)
+ break;
+ if (result == PollResult::Stop)
+ return stopPermanently(locker);
+ RELEASE_ASSERT(result == PollResult::Wait);
+ // Shut the thread down after one second.
+ m_isWaiting = true;
+ bool awokenByNotify =
+ m_waitCondition.waitFor(*m_lock, 1_s);
+ if (verbose && !awokenByNotify && !m_isWaiting)
+ dataLog(RawPointer(this), ": waitFor timed out, but notified via m_isWaiting flag!\n");
+ if (m_isWaiting) {
+ m_isWaiting = false;
+ if (verbose)
+ dataLog(RawPointer(this), ": Going to sleep!\n");
+ // It's important that we don't release the lock until we have completely
+ // indicated that the thread is kaput. Otherwise we'll have a a notify
+ // race that manifests as a deadlock on VM shutdown.
+ return stopForTimeout(locker);
+ }
+ }
+ }
+
+ WorkResult result = work();
+ if (result == WorkResult::Stop) {
+ LockHolder locker(*m_lock);
+ return stopPermanently(locker);
+ }
+ RELEASE_ASSERT(result == WorkResult::Continue);
+ }
+ });
+ detachThread(thread);
+}
+
+void AutomaticThread::threadDidStart()
+{
+}
+
+void AutomaticThread::threadIsStopping(const AbstractLocker&)
+{
+}
+
+} // namespace WTF
+