blob: ee1eb5e4c065caf8dcda58f09aa249193c4e2a66 [file] [log] [blame]
/*
* Copyright (C) 2021 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 <wtf/SuspendableWorkQueue.h>
namespace WTF {
Ref<SuspendableWorkQueue> SuspendableWorkQueue::create(const char* name, WorkQueue::QOS qos)
{
return adoptRef(*new SuspendableWorkQueue(name, qos));
}
SuspendableWorkQueue::SuspendableWorkQueue(const char* name, QOS qos)
: WorkQueue(name, qos)
{
ASSERT(isMainThread());
}
void SuspendableWorkQueue::suspend(Function<void()>&& suspendFunction, CompletionHandler<void()>&& completionHandler)
{
ASSERT(isMainThread());
Locker suspensionLocker { m_suspensionLock };
if (m_state == State::Suspended)
return completionHandler();
// Last suspend function will be the one that is used.
m_suspendFunction = WTFMove(suspendFunction);
m_suspensionCompletionHandlers.append(WTFMove(completionHandler));
if (m_state == State::WillSuspend)
return;
m_state = State::WillSuspend;
// Make sure queue will be suspended when there is no task scheduled on the queue.
WorkQueue::dispatch([this] {
suspendIfNeeded();
});
}
void SuspendableWorkQueue::resume()
{
ASSERT(isMainThread());
Locker suspensionLocker { m_suspensionLock };
if (m_state == State::Running)
return;
if (m_state == State::Suspended)
m_suspensionCondition.notifyOne();
m_state = State::Running;
}
void SuspendableWorkQueue::dispatch(Function<void()>&& function)
{
// WorkQueue will protect this in dispatch().
WorkQueue::dispatch([this, function = WTFMove(function)] {
suspendIfNeeded();
function();
});
}
void SuspendableWorkQueue::dispatchAfter(Seconds seconds, Function<void()>&& function)
{
WorkQueue::dispatchAfter(seconds, [this, function = WTFMove(function)] {
suspendIfNeeded();
function();
});
}
void SuspendableWorkQueue::dispatchSync(Function<void()>&& function)
{
// This function should be called only when queue is not about to be suspended,
// otherwise thread may be blocked.
if (isMainThread()) {
Locker suspensionLocker { m_suspensionLock };
RELEASE_ASSERT(m_state == State::Running);
}
WorkQueue::dispatchSync(WTFMove(function));
}
void SuspendableWorkQueue::invokeAllSuspensionCompletionHandlers()
{
ASSERT(!isMainThread());
if (m_suspensionCompletionHandlers.isEmpty())
return;
callOnMainThread([completionHandlers = std::exchange(m_suspensionCompletionHandlers, { })]() mutable {
for (auto& completionHandler : completionHandlers) {
if (completionHandler)
completionHandler();
}
});
}
void SuspendableWorkQueue::suspendIfNeeded()
{
ASSERT(!isMainThread());
Locker suspensionLocker { m_suspensionLock };
auto suspendFunction = std::exchange(m_suspendFunction, { });
if (m_state != State::WillSuspend)
return;
m_state = State::Suspended;
suspendFunction();
invokeAllSuspensionCompletionHandlers();
while (m_state != State::Running)
m_suspensionCondition.wait(m_suspensionLock);
}
} // namespace WTF