summaryrefslogtreecommitdiff
path: root/src/threads/Semaphore.vala
diff options
context:
space:
mode:
Diffstat (limited to 'src/threads/Semaphore.vala')
-rw-r--r--src/threads/Semaphore.vala160
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;
+ }
+}
+