package xapi.gwt.process; import java.lang.Thread.UncaughtExceptionHandler; import xapi.annotation.inject.SingletonOverride; import xapi.collect.api.Fifo; import xapi.except.NotYetImplemented; import xapi.gwt.collect.JsFifo; import xapi.log.X_Log; import xapi.platform.GwtPlatform; import xapi.process.api.AsyncCondition; import xapi.process.api.AsyncLock; import xapi.process.api.ConcurrentEnvironment; import xapi.process.impl.ConcurrencyServiceAbstract; import xapi.process.service.ConcurrencyService; import xapi.util.api.ErrorHandler; import xapi.util.api.RemovalHandler; import xapi.util.api.SuccessHandler; import com.google.gwt.core.client.Duration; import com.google.gwt.core.client.Scheduler; import com.google.gwt.core.client.Scheduler.ScheduledCommand; import com.google.gwt.user.client.Timer; @GwtPlatform @SingletonOverride(implFor=ConcurrencyService.class) public class ConcurrencyServiceGwt extends ConcurrencyServiceAbstract{ private static final AsyncLock NO_OP = new SingleThreadedLock(); class GwtEnvironment extends ConcurrentEnvironment { private final Fifo<Runnable> defers = JsFifo.newFifo(); private final Fifo<Runnable> finalies = JsFifo.newFifo(); private final Fifo<Runnable> eventualies = JsFifo.newFifo(); private final Fifo<Thread> threads = JsFifo.newFifo(); @Override public Iterable<Thread> getThreads() { return threads.forEach(); } @Override public Iterable<Runnable> getDeferred() { return defers.forEach(); } @Override public Iterable<Runnable> getFinally() { return finalies.forEach(); } @Override public void pushDeferred(Runnable cmd) { defers.give(cmd); } @Override public void pushFinally(Runnable cmd) { finalies.give(cmd); } @Override public void pushThread(Thread childThread) { threads.give(childThread); } @Override public void pushEventually(Runnable cmd) { eventualies.give(cmd); } } @Override protected ConcurrentEnvironment initializeEnvironment(Thread key, UncaughtExceptionHandler params) { return new GwtEnvironment(); } @Override public void runDeferred(final Runnable cmd) { Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { cmd.run(); } }); } @Override public void runFinally(final Runnable cmd) { Scheduler.get().scheduleFinally(new ScheduledCommand() { @Override public void execute() { cmd.run(); } }); } @Override public void runTimeout(final Runnable cmd, int millisToWait) { new Timer() { @Override public void run() { cmd.run(); } }.schedule(millisToWait); } @Override public void runEventually(Runnable cmd) { currentEnvironment().pushEventually(cmd); } @Override public AsyncLock newLock() { return NO_OP; } @Override public boolean trySleep(float millis) { //push a sleep command onto stack. //Note that it won't cause execution to stop, //but it will delay how long until the thread is called again. return false; } @Override public double now() { return Duration.currentTimeMillis(); } } class SingleThreadedLock implements AsyncLock { private boolean locked; private Fifo<SuccessHandler<AsyncLock>> pending = JsFifo.newFifo(); @Override public AsyncCondition newCondition() { throw new NotYetImplemented("AsyncCondition not yet implemented"); } @Override public boolean tryLock() { if (locked) { return false; } return (locked = true); } @Override public RemovalHandler lock(final SuccessHandler<AsyncLock> onLocked) { pending.give(onLocked); return new RemovalHandler() { @Override public void remove() { pending.remove(onLocked); } }; } @Override @SuppressWarnings({"rawtypes","unchecked"}) public void unlock() { locked = false; // Pull task off queue and do it too. if (pending.isEmpty()) return; SuccessHandler<AsyncLock> handler = pending.take(); try { handler.onSuccess(this); } catch (Throwable e) { if (handler instanceof ErrorHandler) { try { ((ErrorHandler)handler).onError(e); } catch (Exception ignored) {} } else { X_Log.warn("Error in AsyncLock.unlock() for "+getClass(), e); } } } };