diff options
Diffstat (limited to 'src/threads/Semaphore.vala')
-rw-r--r-- | src/threads/Semaphore.vala | 160 |
1 files changed, 160 insertions, 0 deletions
diff --git a/src/threads/Semaphore.vala b/src/threads/Semaphore.vala new file mode 100644 index 0000000..dfb0a2f --- /dev/null +++ b/src/threads/Semaphore.vala @@ -0,0 +1,160 @@ +/* Copyright 2011-2014 Yorba Foundation + * + * This software is licensed under the GNU LGPL (version 2.1 or later). + * See the COPYING file in this distribution. + */ + +// Semaphores may be used to be notified when a job is completed. This provides an alternate +// mechanism (essentially, a blocking mechanism) to the system of callbacks that BackgroundJob +// offers. They can also be used for other job-dependent notification mechanisms. +public abstract class AbstractSemaphore { + public enum Type { + SERIAL, + BROADCAST + } + + protected enum NotifyAction { + NONE, + SIGNAL + } + + protected enum WaitAction { + SLEEP, + READY + } + + private Type type; + private Mutex mutex = Mutex(); + private Cond monitor = Cond(); + + public AbstractSemaphore(Type type) { + assert(type == Type.SERIAL || type == Type.BROADCAST); + + this.type = type; + } + + private void trigger() { + if (type == Type.SERIAL) + monitor.signal(); + else + monitor.broadcast(); + } + + public void notify() { + mutex.lock(); + + NotifyAction action = do_notify(); + switch (action) { + case NotifyAction.NONE: + // do nothing + break; + + case NotifyAction.SIGNAL: + trigger(); + break; + + default: + error("Unknown semaphore action: %s", action.to_string()); + } + + mutex.unlock(); + } + + // This method is called by notify() with the semaphore's mutex locked. + protected abstract NotifyAction do_notify(); + + public void wait() { + mutex.lock(); + + while (do_wait() == WaitAction.SLEEP) + monitor.wait(mutex); + + mutex.unlock(); + } + + // This method is called by wait() with the semaphore's mutex locked. + protected abstract WaitAction do_wait(); + + // Returns true if the semaphore is reset, false otherwise. + public bool reset() { + mutex.lock(); + bool is_reset = do_reset(); + mutex.unlock(); + + return is_reset; + } + + // This method is called by reset() with the semaphore's mutex locked. Returns true if reset, + // false if not supported. + protected virtual bool do_reset() { + return false; + } +} + +public class Semaphore : AbstractSemaphore { + bool passed = false; + + public Semaphore() { + base (AbstractSemaphore.Type.BROADCAST); + } + + protected override AbstractSemaphore.NotifyAction do_notify() { + if (passed) + return NotifyAction.NONE; + + passed = true; + + return NotifyAction.SIGNAL; + } + + protected override AbstractSemaphore.WaitAction do_wait() { + return passed ? WaitAction.READY : WaitAction.SLEEP; + } +} + +public class CountdownSemaphore : AbstractSemaphore { + private int total; + private int passed = 0; + + public CountdownSemaphore(int total) { + base (AbstractSemaphore.Type.BROADCAST); + + this.total = total; + } + + protected override AbstractSemaphore.NotifyAction do_notify() { + if (passed >= total) + critical("CountdownSemaphore overrun: %d/%d", passed + 1, total); + + return (++passed >= total) ? NotifyAction.SIGNAL : NotifyAction.NONE; + } + + protected override AbstractSemaphore.WaitAction do_wait() { + return (passed < total) ? WaitAction.SLEEP : WaitAction.READY; + } +} + +public class EventSemaphore : AbstractSemaphore { + bool fired = false; + + public EventSemaphore() { + base (AbstractSemaphore.Type.BROADCAST); + } + + protected override AbstractSemaphore.NotifyAction do_notify() { + fired = true; + + return NotifyAction.SIGNAL; + } + + protected override AbstractSemaphore.WaitAction do_wait() { + return fired ? WaitAction.READY : WaitAction.SLEEP; + } + + protected override bool do_reset() { + fired = false; + + return true; + } +} + |