blob: f8dd1c8326dfbec57757ab11d0696d32d59d8528 [file] [log] [blame] [edit]
// Copyright 2023 The Dawn & Tint Authors
//
// 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.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT HOLDER 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 "dawn/native/SystemEvent.h"
#include <limits>
#include "dawn/common/Assert.h"
#if DAWN_PLATFORM_IS(WINDOWS)
#include "dawn/common/windows_with_undefs.h"
#elif DAWN_PLATFORM_IS(FUCHSIA)
#include <poll.h>
#include <unistd.h>
#elif DAWN_PLATFORM_IS(POSIX)
#include <sys/poll.h>
#include <unistd.h>
#endif
#include <tuple>
#include <utility>
#include <vector>
#include "dawn/native/EventManager.h"
namespace dawn::native {
// SystemEventReceiver
SystemEventReceiver::SystemEventReceiver(SystemHandle primitive)
: mPrimitive(std::move(primitive)) {}
SystemEventReceiver SystemEventReceiver::CreateAlreadySignaled() {
SystemEventPipeSender sender;
SystemEventReceiver receiver;
std::tie(sender, receiver) = CreateSystemEventPipe();
std::move(sender).Signal();
return receiver;
}
const SystemHandle& SystemEventReceiver::GetPrimitive() const {
return mPrimitive;
}
// SystemEventPipeSender
SystemEventPipeSender::~SystemEventPipeSender() {
// Make sure it's been Signaled (or is empty) before being dropped.
// Dropping this would "leak" the receiver (it'll never get signalled).
DAWN_ASSERT(!mPrimitive.IsValid());
}
bool SystemEventPipeSender::IsValid() const {
return mPrimitive.IsValid();
}
void SystemEventPipeSender::Signal() && {
DAWN_ASSERT(mPrimitive.IsValid());
#if DAWN_PLATFORM_IS(WINDOWS)
DAWN_CHECK(SetEvent(mPrimitive.Get()));
#elif DAWN_PLATFORM_IS(POSIX)
// Send one byte to signal the receiver
char zero[1] = {0};
int status = write(mPrimitive.Get(), zero, 1);
DAWN_CHECK(status >= 0);
#else
// Not implemented for this platform.
DAWN_CHECK(false);
#endif
mPrimitive.Close();
}
std::pair<SystemEventPipeSender, SystemEventReceiver> CreateSystemEventPipe() {
#if DAWN_PLATFORM_IS(WINDOWS)
HANDLE eventDup;
HANDLE event = CreateEvent(nullptr, /*bManualReset=*/true, /*bInitialState=*/false, nullptr);
DAWN_CHECK(event != nullptr);
DAWN_CHECK(DuplicateHandle(GetCurrentProcess(), event, GetCurrentProcess(), &eventDup, 0, FALSE,
DUPLICATE_SAME_ACCESS));
DAWN_CHECK(eventDup != nullptr);
SystemEventReceiver receiver;
receiver.mPrimitive = SystemHandle::Acquire(event);
SystemEventPipeSender sender;
sender.mPrimitive = SystemHandle::Acquire(eventDup);
return std::make_pair(std::move(sender), std::move(receiver));
#elif DAWN_PLATFORM_IS(POSIX)
int pipeFds[2];
int status = pipe(pipeFds);
DAWN_CHECK(status >= 0);
SystemEventReceiver receiver;
receiver.mPrimitive = SystemHandle::Acquire(pipeFds[0]);
SystemEventPipeSender sender;
sender.mPrimitive = SystemHandle::Acquire(pipeFds[1]);
return std::make_pair(std::move(sender), std::move(receiver));
#else
// Not implemented for this platform.
DAWN_CHECK(false);
#endif
}
// SystemEvent
// static
Ref<SystemEvent> SystemEvent::CreateSignaled() {
auto ev = AcquireRef(new SystemEvent());
ev->Signal();
return ev;
}
// static
Ref<SystemEvent> SystemEvent::CreateNonProgressingEvent() {
return AcquireRef(new SystemEvent(kNonProgressingPayload));
}
bool SystemEvent::IsProgressing() const {
return GetRefCountPayload() != kNonProgressingPayload;
}
bool SystemEvent::IsSignaled() const {
return mSignaled.load(std::memory_order_acquire);
}
void SystemEvent::Signal() {
if (!mSignaled.exchange(true, std::memory_order_acq_rel)) {
mPipe.Use([](auto pipe) {
// Check if there is a pipe and the sender is valid.
// This function may race with GetOrCreateSystemEventReceiver such that the pipe is
// already signaled and the sender is invalid.
if (*pipe && pipe->value().first.IsValid()) {
std::move(pipe->value().first).Signal();
}
});
}
}
const SystemEventReceiver& SystemEvent::GetOrCreateSystemEventReceiver() {
return mPipe.Use([this](auto pipe) {
if (!*pipe) {
// Check whether the event was marked as completed. This may have happened if
// this function races with another thread performing Signal. If we won
// the race, then the pipe we just created will get signaled inside Signal.
// If we lost the race, then it will not be signaled and we must do it now.
if (IsSignaled()) {
*pipe = {SystemEventPipeSender{}, SystemEventReceiver::CreateAlreadySignaled()};
} else {
*pipe = CreateSystemEventPipe();
}
}
return std::cref(pipe->value().second);
});
}
} // namespace dawn::native