1 // Copyright 2017 The Fuchsia Authors
2 //
3 // Use of this source code is governed by a MIT-style
4 // license that can be found in the LICENSE file or at
5 // https://opensource.org/licenses/MIT
6 
7 #include <object/semaphore.h>
8 
9 #include <err.h>
10 #include <kernel/thread_lock.h>
11 #include <zircon/compiler.h>
12 #include <zircon/types.h>
13 
Semaphore(int64_t initial_count)14 Semaphore::Semaphore(int64_t initial_count) : count_(initial_count) {
15 }
16 
~Semaphore()17 Semaphore::~Semaphore() {
18 }
19 
Post()20 void Semaphore::Post() {
21     // If the count is or was negative then a thread is waiting for a resource,
22     // otherwise it's safe to just increase the count available with no downsides.
23     Guard<spin_lock_t, IrqSave> guard{ThreadLock::Get()};
24     if (unlikely(++count_ <= 0))
25         waitq_.WakeOne(true, ZX_OK);
26 }
27 
Wait(zx_time_t deadline,TimerSlack slack)28 zx_status_t Semaphore::Wait(zx_time_t deadline, TimerSlack slack) {
29     thread_t *current_thread = get_current_thread();
30 
31      // If there are no resources available then we need to
32      // sit in the wait queue until sem_post adds some.
33     zx_status_t ret = ZX_OK;
34 
35     {
36         Guard<spin_lock_t, IrqSave> guard{ThreadLock::Get()};
37         current_thread->interruptable = true;
38         bool block = --count_ < 0;
39 
40         if (unlikely(block)) {
41             ret = waitq_.Block(deadline, slack);
42             if (ret < ZX_OK) {
43                 if ((ret == ZX_ERR_TIMED_OUT) || (ret == ZX_ERR_INTERNAL_INTR_KILLED))
44                     count_++;
45             }
46         }
47 
48         current_thread->interruptable = false;
49     }
50 
51     return ret;
52 }
53