2011-06-28 16:33:27 -07:00
|
|
|
/* Copyright 2011 Yorba Foundation
|
|
|
|
|
*
|
|
|
|
|
* This software is licensed under the GNU Lesser General Public License
|
|
|
|
|
* (version 2.1 or later). See the COPYING file in this distribution.
|
|
|
|
|
*/
|
|
|
|
|
|
2011-07-15 16:50:12 -07:00
|
|
|
public class Geary.NonblockingSemaphore {
|
2011-06-28 16:33:27 -07:00
|
|
|
private class Pending {
|
|
|
|
|
public SourceFunc cb;
|
2011-07-01 15:40:20 -07:00
|
|
|
public Cancellable? cancellable;
|
2011-06-28 16:33:27 -07:00
|
|
|
|
2011-07-01 15:40:20 -07:00
|
|
|
public signal void cancelled();
|
|
|
|
|
|
|
|
|
|
public Pending(SourceFunc cb, Cancellable? cancellable) {
|
2011-06-28 16:33:27 -07:00
|
|
|
this.cb = cb;
|
2011-07-01 15:40:20 -07:00
|
|
|
this.cancellable = cancellable;
|
|
|
|
|
|
|
|
|
|
if (cancellable != null)
|
|
|
|
|
cancellable.cancelled.connect(on_cancelled);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
~Pending() {
|
|
|
|
|
if (cancellable != null)
|
|
|
|
|
cancellable.cancelled.disconnect(on_cancelled);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void on_cancelled() {
|
|
|
|
|
cancelled();
|
2011-06-28 16:33:27 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-07-01 15:40:20 -07:00
|
|
|
private bool broadcast;
|
2011-06-28 16:33:27 -07:00
|
|
|
private Cancellable? cancellable;
|
|
|
|
|
private bool passed = false;
|
|
|
|
|
private Gee.List<Pending> pending_queue = new Gee.LinkedList<Pending>();
|
|
|
|
|
|
2011-07-01 15:40:20 -07:00
|
|
|
public NonblockingSemaphore(bool broadcast, Cancellable? cancellable = null) {
|
|
|
|
|
this.broadcast = broadcast;
|
2011-06-28 16:33:27 -07:00
|
|
|
this.cancellable = cancellable;
|
|
|
|
|
|
|
|
|
|
if (cancellable != null)
|
|
|
|
|
cancellable.cancelled.connect(on_cancelled);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
~NonblockingSemaphore() {
|
|
|
|
|
if (pending_queue.size > 0)
|
|
|
|
|
warning("Nonblocking semaphore destroyed with %d pending callers", pending_queue.size);
|
|
|
|
|
}
|
|
|
|
|
|
2011-07-01 15:40:20 -07:00
|
|
|
private void trigger(bool all) {
|
|
|
|
|
if (pending_queue.size == 0)
|
|
|
|
|
return;
|
2011-06-28 16:33:27 -07:00
|
|
|
|
2011-07-01 15:40:20 -07:00
|
|
|
if (all) {
|
|
|
|
|
foreach (Pending pending in pending_queue)
|
|
|
|
|
Idle.add(pending.cb);
|
|
|
|
|
|
|
|
|
|
pending_queue.clear();
|
|
|
|
|
} else {
|
|
|
|
|
Pending pending = pending_queue.remove_at(0);
|
|
|
|
|
Idle.add(pending.cb);
|
|
|
|
|
}
|
2011-06-28 16:33:27 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void notify() throws Error {
|
|
|
|
|
check_cancelled();
|
|
|
|
|
|
|
|
|
|
passed = true;
|
2011-07-01 15:40:20 -07:00
|
|
|
|
|
|
|
|
trigger(broadcast);
|
2011-06-28 16:33:27 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: Allow the caller to pass their own cancellable in if they want to be able to cancel
|
|
|
|
|
// this particular wait (and not all waiting threads of execution)
|
2011-07-01 15:40:20 -07:00
|
|
|
public async void wait_async(Cancellable? cancellable = null) throws Error {
|
2011-06-28 16:33:27 -07:00
|
|
|
for (;;) {
|
2011-07-01 15:40:20 -07:00
|
|
|
check_user_cancelled(cancellable);
|
2011-06-28 16:33:27 -07:00
|
|
|
check_cancelled();
|
|
|
|
|
|
|
|
|
|
if (passed)
|
|
|
|
|
return;
|
|
|
|
|
|
2011-07-01 15:40:20 -07:00
|
|
|
Pending pending = new Pending(wait_async.callback, cancellable);
|
|
|
|
|
pending.cancelled.connect(on_pending_cancelled);
|
|
|
|
|
|
|
|
|
|
pending_queue.add(pending);
|
2011-06-28 16:33:27 -07:00
|
|
|
yield;
|
2011-07-01 15:40:20 -07:00
|
|
|
|
|
|
|
|
pending.cancelled.disconnect(on_pending_cancelled);
|
2011-06-28 16:33:27 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-07-01 15:40:20 -07:00
|
|
|
public void reset() {
|
|
|
|
|
passed = false;
|
|
|
|
|
}
|
|
|
|
|
|
2011-06-28 16:33:27 -07:00
|
|
|
public bool is_cancelled() {
|
|
|
|
|
return (cancellable != null) ? cancellable.is_cancelled() : false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void check_cancelled() throws Error {
|
|
|
|
|
if (is_cancelled())
|
|
|
|
|
throw new IOError.CANCELLED("Semaphore cancelled");
|
|
|
|
|
}
|
|
|
|
|
|
2011-07-01 15:40:20 -07:00
|
|
|
private static void check_user_cancelled(Cancellable? cancellable) throws Error {
|
|
|
|
|
if (cancellable != null && cancellable.is_cancelled())
|
|
|
|
|
throw new IOError.CANCELLED("User cancelled operation");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void on_pending_cancelled(Pending pending) {
|
|
|
|
|
bool removed = pending_queue.remove(pending);
|
|
|
|
|
assert(removed);
|
|
|
|
|
|
|
|
|
|
Idle.add(pending.cb);
|
|
|
|
|
}
|
|
|
|
|
|
2011-06-28 16:33:27 -07:00
|
|
|
private void on_cancelled() {
|
2011-07-01 15:40:20 -07:00
|
|
|
trigger(true);
|
2011-06-28 16:33:27 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|