// Copyright 2017 The Fuchsia Authors
//
// 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
#include "kernel/semaphore.h"

#include <zircon/compiler.h>
#include <zircon/errors.h>
#include <zircon/types.h>

#include <kernel/thread_lock.h>

void Semaphore::Post() {
  Guard<MonitoredSpinLock, IrqSave> guard{ThreadLock::Get(), SOURCE_TAG};

  // Either the number of waiters in the wait queue, or the semaphore count,
  // must be 0.  It should never be possible for there to be waiters, and a
  // positive count.
  DEBUG_ASSERT((count_ == 0) || waitq_.IsEmpty());

  // If we have no waiters, increment the count.  Otherwise, release a waiter.
  if (waitq_.IsEmpty()) {
    ++count_;
  } else {
    waitq_.WakeOne(true, ZX_OK);
  }
}

zx_status_t Semaphore::Wait(const Deadline& deadline) {
  Guard<MonitoredSpinLock, IrqSave> guard{ThreadLock::Get(), SOURCE_TAG};

  DEBUG_ASSERT((count_ == 0) || waitq_.IsEmpty());

  // If the count is positive, simply decrement it and get out.
  if (count_ > 0) {
    --count_;
    return ZX_OK;
  }

  // Wait in an interruptible state.  We will either be woken by a Post
  // operation, or by a timeout or signal.  Whatever happens, return the reason
  // the wait operation ended.
  return waitq_.Block(deadline, Interruptible::Yes);
}
