blob: 393be8142fdaa0c55ffa75f565f3392c0ab72894 [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
#ifndef ZIRCON_KERNEL_INCLUDE_KERNEL_EVENT_H_
#define ZIRCON_KERNEL_INCLUDE_KERNEL_EVENT_H_
#include <stdint.h>
#include <sys/types.h>
#include <ktl/atomic.h>
#include <zircon/compiler.h>
#include <zircon/types.h>
#include <fbl/canary.h>
#include <kernel/thread.h>
#include <kernel/timer.h>
// Rules for Events and AutounsignalEvents:
// - Events may be signaled from interrupt context *but* preemption must be
// disabled.
// - Events may not be waited upon from interrupt context.
// - Standard Events:
// - Wake up any waiting threads when signaled.
// - Continue to do so (no threads will wait) until unsignaled.
// - Stores a single result value when first signaled. This result is
// returned to waiters and cleared when unsignaled.
// - AutounsignalEvents:
// - If one or more threads are waiting when signaled, one thread will
// be woken up and return. The signaled state will not be set.
// - If no threads are waiting when signaled, the AutounsignalEvent will remain
// in the signaled state until a thread attempts to wait (at which
// time it will unsignal atomicly and return immediately) or
// AutounsignalEvent::Unsignal() is called.
// - Stores a single result value when signaled until a thread is woken.
class Event {
public:
constexpr explicit Event(bool initial = false) : Event(initial, Flags(0)) {}
~Event();
Event(const Event&) = delete;
Event& operator=(const Event&) = delete;
// Event::Wait() and other Wait functions will return ZX_OK if already signaled, even if deadline
// has passed. They will return ZX_ERR_TIMED_OUT after the deadline passes if the event has not
// been signaled.
// Returns:
// ZX_OK - signaled
// ZX_ERR_TIMED_OUT - time out expired
// ZX_ERR_INTERNAL_INTR_KILLED - thread killed
// ZX_ERR_INTERNAL_INTR_RETRY - thread is suspended
// Or the |status| which the caller specified in Event::Signal(status)
zx_status_t Wait(const Deadline& deadline) { return WaitWorker(deadline, Interruptible::Yes, 0); }
// Same as Wait() but waits forever and gives a mask of signals to ignore.
// The caller must be interruptible.
zx_status_t WaitWithMask(uint signal_mask) {
return WaitWorker(Deadline::infinite(), Interruptible::Yes, signal_mask);
}
// no deadline, non interruptible version of the above.
zx_status_t Wait() { return WaitWorker(Deadline::infinite(), Interruptible::No, 0); }
// Wait until deadline
// Interruptible arg allows it to return early with ZX_ERR_INTERNAL_INTR_KILLED if thread
// is signaled for kill or with ZX_ERR_INTERNAL_INTR_RETRY if the thread is suspended.
zx_status_t WaitDeadline(zx_time_t deadline, Interruptible interruptible) {
return WaitWorker(Deadline::no_slack(deadline), interruptible, 0);
}
void Signal(zx_status_t wait_result = ZX_OK) TA_EXCL(thread_lock);
void SignalLocked() TA_REQ(thread_lock);
zx_status_t Unsignal() TA_EXCL(thread_lock);
protected:
enum Flags : uint32_t {
AUTOUNSIGNAL = 1,
};
// Only our AutounsignalEvent subclass can also access this, to keep the flags private.
constexpr Event(bool initial, Flags flags)
: magic_(kMagic), result_(initial ? ZX_OK : kNotSignalled), flags_(flags) {}
private:
zx_status_t WaitWorker(const Deadline& deadline, Interruptible interruptible, uint signal_mask);
void SignalInternal(zx_status_t wait_result) TA_REQ(thread_lock);
static constexpr uint32_t kMagic = fbl::magic("evnt");
uint32_t magic_;
static constexpr zx_status_t kNotSignalled = INT_MAX;
ktl::atomic<zx_status_t> result_;
Flags flags_;
WaitQueue wait_;
};
class AutounsignalEvent : public Event {
public:
constexpr explicit AutounsignalEvent(bool initial = false)
: Event(initial, Flags::AUTOUNSIGNAL) {}
};
#endif // ZIRCON_KERNEL_INCLUDE_KERNEL_EVENT_H_