| 1 | module sync |
| 2 | |
| 3 | @[heap] |
| 4 | pub struct Cond { |
| 5 | mut: |
| 6 | // Externally provided mutex for shared resource protection |
| 7 | mutex &Mutex |
| 8 | // Internal lock for protecting wait queue access |
| 9 | inner_mutex Mutex |
| 10 | // Queue of waiting channels |
| 11 | waiters []chan bool |
| 12 | } |
| 13 | |
| 14 | // new_cond creates new condition variable associated with given mutex |
| 15 | pub fn new_cond(m &Mutex) &Cond { |
| 16 | return &Cond{ |
| 17 | mutex: m |
| 18 | inner_mutex: new_mutex() |
| 19 | waiters: []chan bool{} |
| 20 | } |
| 21 | } |
| 22 | |
| 23 | // wait waits for condition notification. |
| 24 | // NOTE: Spurious wakeups are possible; always use in a loop: |
| 25 | // mutex.lock() |
| 26 | // for !condition { |
| 27 | // cond.wait() |
| 28 | // } |
| 29 | // mutex.unlock() |
| 30 | @[direct_array_access] |
| 31 | pub fn (mut c Cond) wait() { |
| 32 | // Create a channel for this waiting operation with capacity 1 |
| 33 | ch := chan bool{cap: 1} |
| 34 | defer { |
| 35 | ch.close() |
| 36 | } |
| 37 | |
| 38 | // Add this channel to the waiters queue |
| 39 | c.inner_mutex.lock() |
| 40 | c.waiters << ch |
| 41 | c.inner_mutex.unlock() |
| 42 | |
| 43 | // Release external lock and suspend |
| 44 | c.mutex.unlock() |
| 45 | _ := <-ch // Block until signaled |
| 46 | |
| 47 | c.inner_mutex.lock() |
| 48 | for i := c.waiters.len - 1; i >= 0; i-- { |
| 49 | if c.waiters[i] == ch { |
| 50 | c.waiters.delete(i) |
| 51 | break |
| 52 | } |
| 53 | } |
| 54 | c.inner_mutex.unlock() |
| 55 | // Re-acquire external lock before returning |
| 56 | c.mutex.lock() |
| 57 | } |
| 58 | |
| 59 | // signal wakes one waiting thread. |
| 60 | @[direct_array_access] |
| 61 | pub fn (mut c Cond) signal() { |
| 62 | c.inner_mutex.lock() |
| 63 | defer { c.inner_mutex.unlock() } |
| 64 | if c.waiters.len > 0 { |
| 65 | // Remove first waiter from queue |
| 66 | mut waiter := c.waiters[0] |
| 67 | c.waiters.delete(0) |
| 68 | if !waiter.closed { |
| 69 | waiter <- true // Wake up the thread |
| 70 | } |
| 71 | } |
| 72 | } |
| 73 | |
| 74 | // broadcast wakes all waiting threads. |
| 75 | @[direct_array_access] |
| 76 | pub fn (mut c Cond) broadcast() { |
| 77 | c.inner_mutex.lock() |
| 78 | defer { c.inner_mutex.unlock() } |
| 79 | // Release all waiting ch |
| 80 | for i in 0 .. c.waiters.len { |
| 81 | mut waiter := c.waiters[i] |
| 82 | if !waiter.closed { |
| 83 | waiter <- true // Wake up the thread |
| 84 | } |
| 85 | } |
| 86 | c.waiters.clear() |
| 87 | } |
| 88 | |