1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
// Copyright (c) 2017 Stefan Lankes, RWTH Aachen University // 2018 Colin Finck, RWTH Aachen University // // Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or // http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or // http://opensource.org/licenses/MIT>, at your option. This file may not be // copied, modified, or distributed except according to those terms. use arch::percore::*; use scheduler; use scheduler::task::{PriorityTaskQueue, WakeupReason}; use synch::spinlock::SpinlockIrqSave; struct SemaphoreState { /// Resource available count count: isize, /// Priority queue of waiting tasks queue: PriorityTaskQueue, } /// A counting, blocking, semaphore. /// /// Semaphores are a form of atomic counter where access is only granted if the /// counter is a positive value. Each acquisition will block the calling thread /// until the counter is positive, and each release will increment the counter /// and unblock any threads if necessary. /// /// # Examples /// /// ``` /// /// // Create a semaphore that represents 5 resources /// let sem = Semaphore::new(5); /// /// // Acquire one of the resources /// sem.acquire(); /// /// // Acquire one of the resources for a limited period of time /// { /// let _guard = sem.access(); /// // ... /// } // resources is released here /// /// // Release our initially acquired resource /// sem.release(); /// /// Interface is derived from https://doc.rust-lang.org/1.7.0/src/std/sync/semaphore.rs.html /// ``` pub struct Semaphore { state: SpinlockIrqSave<SemaphoreState>, } // Same unsafe impls as `Semaphore` unsafe impl Sync for Semaphore {} unsafe impl Send for Semaphore {} impl Semaphore { /// Creates a new semaphore with the initial count specified. /// /// The count specified can be thought of as a number of resources, and a /// call to `acquire` or `access` will block until at least one resource is /// available. It is valid to initialize a semaphore with a negative count. pub const fn new(count: isize) -> Self { Self { state: SpinlockIrqSave::new(SemaphoreState { count: count, queue: PriorityTaskQueue::new(), }), } } /// Acquires a resource of this semaphore, blocking the current thread until /// it can do so or until the wakeup time has elapsed. /// /// This method will block until the internal count of the semaphore is at /// least 1. pub fn acquire(&self, wakeup_time: Option<u64>) -> bool { // Reset last_wakeup_reason. let core_scheduler = core_scheduler(); core_scheduler.current_task.borrow_mut().last_wakeup_reason = WakeupReason::Custom; // Loop until we have acquired the semaphore. loop { { let mut locked_state = self.state.lock(); if locked_state.count > 0 { // Successfully acquired the semaphore. locked_state.count -= 1; return true; } else if core_scheduler.current_task.borrow().last_wakeup_reason == WakeupReason::Timer { // We could not acquire the semaphore and we were woken up because the wakeup time has elapsed. // Don't try again and return the failure status. locked_state .queue .remove(core_scheduler.current_task.clone()); return false; } // We couldn't acquire the semaphore. // Block the current task and add it to the wakeup queue. core_scheduler .blocked_tasks .lock() .add(core_scheduler.current_task.clone(), wakeup_time); locked_state.queue.push(core_scheduler.current_task.clone()); } // Switch to the next task. core_scheduler.reschedule(); } } pub fn try_acquire(&self) -> bool { let mut locked_state = self.state.lock(); if locked_state.count > 0 { locked_state.count -= 1; true } else { false } } /// Release a resource from this semaphore. /// /// This will increment the number of resources in this semaphore by 1 and /// will notify any pending waiters in `acquire` or `access` if necessary. pub fn release(&self) { if let Some(task) = { let mut locked_state = self.state.lock(); locked_state.count += 1; locked_state.queue.pop() } { // Wake up any task that has been waiting for this semaphore. let core_scheduler = scheduler::get_scheduler(task.borrow().core_id); core_scheduler.blocked_tasks.lock().custom_wakeup(task); }; } }