blob: 47ff4fe7f4f8cfa04398fce62101a06a1f1f22cf [file] [log] [blame]
// Copyright 2016 The Fuchsia Authors
// Copyright (c) 2008-2014 Travis Geiselbrecht
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
/**
* @file
* @brief Event wait and signal functions for threads.
* @defgroup event Events
*
* An event is a subclass of a wait queue.
*
* Threads wait for events, with optional timeouts.
*
* Events are "signaled", releasing waiting threads to continue.
* Signals may be one-shot signals (Event::AUTOUNSIGNAL), in which
* case one signal releases only one thread, at which point it is
* automatically cleared. Otherwise, signals release all waiting threads
* to continue immediately until the signal is manually cleared with
* Event::Unsignal().
*
* @{
*/
#include "kernel/event.h"
#include <assert.h>
#include <debug.h>
#include <lib/zircon-internal/macros.h>
#include <sys/types.h>
#include <zircon/errors.h>
#include <zircon/types.h>
#include <kernel/spinlock.h>
#include <kernel/thread.h>
#include <kernel/thread_lock.h>
/**
* @brief Destruct an Event object.
*
* Event's resources are freed and it may no longer be used.
* Will panic if there are any threads still waiting.
*/
Event::~Event() {
DEBUG_ASSERT(magic_ == kMagic);
magic_ = 0;
result_.store(kNotSignalled, ktl::memory_order_relaxed);
flags_ = Flags(0);
}
zx_status_t Event::WaitWorker(const Deadline& deadline, Interruptible interruptible,
uint signal_mask) {
DEBUG_ASSERT(magic_ == kMagic);
DEBUG_ASSERT(!arch_blocking_disallowed());
Guard<MonitoredSpinLock, IrqSave> guard{ThreadLock::Get(), SOURCE_TAG};
zx_status_t ret = result_.load(ktl::memory_order_relaxed);
if (ret == kNotSignalled) {
/* unsignaled, block here */
return wait_.BlockEtc(deadline, signal_mask, ResourceOwnership::Normal, interruptible);
}
/* signaled, we're going to fall through */
if (flags_ & Event::AUTOUNSIGNAL) {
/* autounsignal flag lets one thread fall through before unsignaling */
result_.store(kNotSignalled, ktl::memory_order_relaxed);
}
return ret;
}
void Event::SignalInternal(bool reschedule, zx_status_t wait_result) TA_REQ(thread_lock) {
DEBUG_ASSERT(magic_ == kMagic);
DEBUG_ASSERT(wait_result != kNotSignalled);
if (result_.load(ktl::memory_order_relaxed) == kNotSignalled) {
if (flags_ & Event::AUTOUNSIGNAL) {
/* try to release one thread and leave unsignaled if successful */
if (!wait_.WakeOne(reschedule, wait_result)) {
/*
* if we didn't actually find a thread to wake up, go to
* signaled state and let the next call to Wait
* unsignal the event.
*/
result_.store(wait_result, ktl::memory_order_relaxed);
}
} else {
/* release all threads and remain signaled */
result_.store(wait_result, ktl::memory_order_relaxed);
wait_.WakeAll(reschedule, wait_result);
}
}
}
/**
* @brief Signal an event
*
* Signals an event. If Event::AUTOUNSIGNAL is set in the event
* object's flags, only one waiting thread is allowed to proceed. Otherwise,
* all waiting threads are allowed to proceed until such time as
* Event::Unsignal() is called.
*
* @param e Event object
* @param reschedule If true, waiting thread(s) are executed immediately,
* and the current thread resumes only after the
* waiting threads have been satisfied. If false,
* waiting threads are placed at the head of the run
* queue.
* @param wait_result What status a wait call will return to the
* thread or threads that are woken up.
*/
void Event::SignalEtc(bool reschedule, zx_status_t wait_result) {
Guard<MonitoredSpinLock, IrqSave> guard{ThreadLock::Get(), SOURCE_TAG};
SignalInternal(reschedule, wait_result);
}
/* same as above, but the thread lock must already be held */
void Event::SignalThreadLocked() {
DEBUG_ASSERT(arch_ints_disabled());
DEBUG_ASSERT(thread_lock.IsHeld());
SignalInternal(false, ZX_OK);
}
/**
* @brief Clear the "signaled" property of an event
*
* Used mainly for event objects without the Event::AUTOUNSIGNAL
* flag. Once this function is called, threads that call Event::Wait()
* functions will once again need to wait until the event object
* is signaled.
*
* @param e Event object
*
* @return Returns ZX_OK on success.
*/
zx_status_t Event::Unsignal() {
DEBUG_ASSERT(magic_ == kMagic);
result_.store(kNotSignalled, ktl::memory_order_relaxed);
return ZX_OK;
}