]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.utils.thread/src/org/simantics/utils/threads/ua/AbstractState.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.utils.thread / src / org / simantics / utils / threads / ua / AbstractState.java
diff --git a/bundles/org.simantics.utils.thread/src/org/simantics/utils/threads/ua/AbstractState.java b/bundles/org.simantics.utils.thread/src/org/simantics/utils/threads/ua/AbstractState.java
new file mode 100644 (file)
index 0000000..5f9631e
--- /dev/null
@@ -0,0 +1,325 @@
+/*******************************************************************************\r
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
+ * in Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ *     VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+\r
+package org.simantics.utils.threads.ua;\r
+\r
+import java.util.Set;\r
+import java.util.concurrent.CopyOnWriteArrayList;\r
+import java.util.concurrent.Executor;\r
+import java.util.concurrent.TimeUnit;\r
+import java.util.concurrent.TimeoutException;\r
+\r
+/**\r
+ * This is a default implementation to {@link IStatefulObject}.\r
+ * This class can be subclassed or used as it. \r
+ * The state type is parametrized (typically an enumeration). \r
+ * \r
+ * TODO Remove locks - use spin set and test\r
+ *\r
+ * @see IStatefulObject\r
+ * @see StateListener Listener for state modifications\r
+ * @author Toni Kalajainen (toni.kalajainen@vtt.fi)\r
+ * @param <StateType> \r
+ * @param <ErrorType> \r
+ */\r
+public abstract class AbstractState<StateType, ErrorType extends Throwable> implements IStatefulObject<StateType, ErrorType> {\r
+\r
+       /** Current state */\r
+       private StateType state = null;\r
+       /** Optional error state */\r
+       private StateType errorState = null;\r
+       /** Error cause */\r
+       private ErrorType errorCause;\r
+       \r
+       // Optimization for 1 listener, ListenerList is heavy //\r
+       private StateListener<StateType> firstListener = null; \r
+       private CopyOnWriteArrayList<StateListener<StateType>> listenerList = null;\r
+       private Object lock = new Object();\r
+       \r
+       public AbstractState(StateType initialState)\r
+       {\r
+               state = initialState;\r
+       }\r
+       \r
+       /**\r
+        * Creates a state with a error state. The state object goes to errorState on setError(). \r
+        * \r
+        * @param initialState\r
+        * @param errorState\r
+        */\r
+       public AbstractState(StateType initialState, StateType errorState)\r
+       {\r
+               state = initialState;\r
+               this.errorState = errorState;\r
+       }\r
+       \r
+       @Override\r
+       public synchronized StateType getState() {\r
+               return state;\r
+       }\r
+       \r
+       /**\r
+        * Attempts to change the state. The state will be changed only if current\r
+        * state is one of the expected states. \r
+        * \r
+        * @param prerequisiteState expected current state\r
+        * @param newState\r
+        * @return state after attempt\r
+        */\r
+       protected StateType attemptSetState(Set<StateType> prerequisiteState, StateType newState)\r
+       {\r
+               if (prerequisiteState==null || newState==null)\r
+                       throw new IllegalArgumentException("null arg");\r
+               return setState(newState, null, prerequisiteState);\r
+       }\r
+       \r
+       @Override\r
+       public synchronized void addStateListener(StateListener<StateType> listener) {\r
+               if (listener==null) \r
+                       throw new IllegalArgumentException("null arg");\r
+               if (listenerList!=null)\r
+               {\r
+                       listenerList.add(listener);\r
+                       return;\r
+               }\r
+               if (firstListener==null) {\r
+                       firstListener = listener;\r
+                       return;\r
+               }\r
+               \r
+               listenerList = new CopyOnWriteArrayList<StateListener<StateType>>();\r
+               listenerList.add(listener);\r
+       }\r
+\r
+       @Override\r
+       public void removeStateListener(StateListener<StateType> listener) {\r
+               if (listener==null) \r
+                       throw new IllegalArgumentException("null arg");\r
+               if (listenerList!=null) {\r
+                       listenerList.remove(listener);\r
+                       if (listenerList.isEmpty()) listenerList = null;\r
+                       return;\r
+               }\r
+               if (listener == firstListener) {\r
+                       firstListener = null;\r
+               }\r
+       }\r
+\r
+       protected boolean setState(StateType state)\r
+       {\r
+               return setState(state, null, null) == state;\r
+       }\r
+       \r
+       protected void setError(ErrorType error)\r
+       {\r
+               this.errorCause = error;\r
+               if (errorState==null || !setState(errorState))\r
+               {\r
+                       // wake up sleepers\r
+                       synchronized(lock) \r
+                       {\r
+                               lock.notifyAll();\r
+                       }\r
+               }\r
+       }\r
+       \r
+       protected void clearError()\r
+       {\r
+               errorCause = null;               \r
+       }\r
+       \r
+       public ErrorType getError()\r
+       {\r
+               return errorCause;\r
+       }\r
+       \r
+       public boolean hasError()\r
+       {\r
+               return errorCause!=null;\r
+       }\r
+       \r
+       protected void assertNoError()\r
+       throws ErrorType\r
+       {\r
+               ErrorType e = errorCause;               \r
+               if (e!=null)\r
+                       throw e;\r
+       }\r
+       \r
+       /**\r
+        * Set state\r
+        * \r
+        * @param state\r
+        * @param listenerExecutor executor for post listener handling or null for immediate\r
+        * @param prerequisiteStates old state prerequisite or null \r
+        * @return state after attempt\r
+        */\r
+       protected StateType setState(StateType state, Executor listenerExecutor, Set<StateType> prerequisiteStates)\r
+       {               \r
+               boolean hasListeners;\r
+               StateListener<StateType> fl = null;\r
+               StateType oldState = null;\r
+               StateType newState = null;\r
+               synchronized (this) {\r
+                       oldState = this.state;\r
+                       newState = state;\r
+                       if (oldState==newState) return state;\r
+                       if (prerequisiteStates!=null && !prerequisiteStates.contains(this.state))\r
+                               return state;\r
+                       if (!isStateTransitionAllowed(oldState, newState))\r
+                               return state;\r
+\r
+                       this.state = newState;\r
+                       fl = firstListener;\r
+                       hasListeners = fl!=null || (listenerList!=null && !listenerList.isEmpty());\r
+               }\r
+               final StateListener<StateType> fl_ = fl;\r
+               synchronized(lock) \r
+               {\r
+                       lock.notifyAll();\r
+               }\r
+               // Threads wake up here...\r
+               \r
+               // Handle listeners\r
+               onStateTransition(oldState, newState);\r
+               \r
+               if (hasListeners) {\r
+                       final StateType os = oldState;\r
+                       final StateType ns = newState;\r
+                       if (fl!=null) {\r
+                               if (listenerExecutor==null) {\r
+                                       try {\r
+                                               fl.onStateTransition(this, oldState, newState);\r
+                                       } catch (RuntimeException e) {\r
+                                               onListenerException(e);\r
+                                       }\r
+                               } else {\r
+                                       listenerExecutor.execute(new Runnable() {\r
+                                               @Override\r
+                                               public void run() {\r
+                                                       try {\r
+                                                               fl_.onStateTransition(AbstractState.this, os, ns);\r
+                                                       } catch (RuntimeException e) {\r
+                                                               onListenerException(e);\r
+                                                       }\r
+                                               }});\r
+                               }\r
+                       }\r
+                       if (listenerList!=null && !listenerList.isEmpty())\r
+                       for (final StateListener<StateType> sl : listenerList) {\r
+                               if (listenerExecutor==null) {\r
+                                       try {\r
+                                               sl.onStateTransition(this, oldState, newState);\r
+                                       } catch (RuntimeException e) {\r
+                                               onListenerException(e);\r
+                                       }\r
+                               } else {\r
+                                       listenerExecutor.execute(new Runnable() {\r
+                                               @Override\r
+                                               public void run() {\r
+                                                       try {\r
+                                                               sl.onStateTransition(AbstractState.this, os, ns);                                                       \r
+                                                       } catch (RuntimeException e) {\r
+                                                               onListenerException(e);\r
+                                                       }\r
+                                               }});\r
+                               }\r
+                       }\r
+               }\r
+               return state;\r
+       }\r
+       \r
+       /**\r
+        * Checks whether state transition is allowed.\r
+        * Override this\r
+        * \r
+        * @param oldState\r
+        * @param newState\r
+        * @return true if state transition is allowed\r
+        */\r
+       protected boolean isStateTransitionAllowed(StateType oldState, StateType newState)\r
+       {\r
+               return true;\r
+       }\r
+       \r
+       /**\r
+        * Override this.\r
+        * \r
+        * @param oldState\r
+        * @param newState\r
+        */\r
+       protected void onStateTransition(StateType oldState, StateType newState)\r
+       {               \r
+       }\r
+\r
+       @Override\r
+       public StateType waitForState(Set<StateType> set) \r
+       throws InterruptedException, ErrorType\r
+       {\r
+               // This impl makes unnecessary wakeups but is memory conservative               \r
+               synchronized(lock) {\r
+                       while (!set.contains(state))\r
+                               lock.wait();\r
+                       ErrorType e = getError();\r
+                       if (e!=null)\r
+                               throw e;\r
+                       return state;\r
+               }\r
+       }\r
+\r
+       public StateType waitForStateUninterruptibly(Set<StateType> set) \r
+       throws ErrorType\r
+       {\r
+               // This impl makes unnecessary wakeups but is memory conservative               \r
+               synchronized(lock) {\r
+                       while (!set.contains(state))\r
+                               try {\r
+                                       lock.wait();\r
+                               } catch (InterruptedException qwer) {}\r
+                       ErrorType e = getError();\r
+                       if (e!=null)\r
+                               throw e;\r
+                       return state;\r
+               }\r
+       }\r
+\r
+       @Override\r
+       public StateType waitForState(\r
+                       Set<StateType> set, \r
+                       long timeout,\r
+                       TimeUnit unit) \r
+       throws InterruptedException, TimeoutException, ErrorType {\r
+               long abortTime = System.currentTimeMillis() + unit.toMillis(timeout);\r
+               synchronized(lock) {\r
+                       while (!set.contains(state)) {\r
+                               long waitTime = System.currentTimeMillis() - abortTime;\r
+                               if (waitTime<0)\r
+                                       throw new TimeoutException("timeout");\r
+                               lock.wait(waitTime);\r
+                               ErrorType e = getError();\r
+                               if (e!=null)\r
+                                       throw e;\r
+                       }\r
+                       return state;\r
+               }               \r
+       }\r
+       \r
+       /**\r
+        * Override this.\r
+        * @param rte\r
+        */\r
+       protected void onListenerException(RuntimeException rte)\r
+       {\r
+               rte.printStackTrace();\r
+       }\r
+\r
+}\r