PageRenderTime 50ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/src/concurrent/java/Actor.java

https://bitbucket.org/bedlaczech/fan-1.0
Java | 385 lines | 267 code | 62 blank | 56 comment | 54 complexity | 7e8f58a98af98cc94f78369e788ae58a MD5 | raw file
Possible License(s): CC-BY-SA-3.0
  1. //
  2. // Copyright (c) 2009, Brian Frank and Andy Frank
  3. // Licensed under the Academic Free License version 3.0
  4. //
  5. // History:
  6. // 26 Mar 09 Brian Frank Creation
  7. //
  8. package fan.concurrent;
  9. import fan.sys.*;
  10. import java.util.HashMap;
  11. /**
  12. * Actor is a worker who processes messages asynchronously.
  13. */
  14. public class Actor
  15. extends FanObj
  16. implements ThreadPool.Work
  17. {
  18. //////////////////////////////////////////////////////////////////////////
  19. // Construction
  20. //////////////////////////////////////////////////////////////////////////
  21. public static Actor make(ActorPool pool) { return make(pool, null); }
  22. public static Actor make(ActorPool pool, Func receive)
  23. {
  24. Actor self = new Actor();
  25. make$(self, pool, receive);
  26. return self;
  27. }
  28. public static void make$(Actor self, ActorPool pool) { make$(self, pool, null); }
  29. public static void make$(Actor self, ActorPool pool, Func receive)
  30. {
  31. // check pool
  32. if (pool == null)
  33. throw NullErr.make("pool is null");
  34. // check receive method
  35. if (receive == null && self.typeof().qname().equals("concurrent::Actor"))
  36. throw ArgErr.make("must supply receive func or subclass Actor");
  37. if (receive != null) receive = (Func)receive.toImmutable();
  38. // init
  39. self.pool = pool;
  40. self.receive = receive;
  41. self.queue = new Queue();
  42. }
  43. public static Actor makeCoalescing(ActorPool pool, Func k, Func c) { return makeCoalescing(pool, k, c, null); }
  44. public static Actor makeCoalescing(ActorPool pool, Func k, Func c, Func r)
  45. {
  46. Actor self = new Actor();
  47. makeCoalescing$(self, pool, k, c, r);
  48. return self;
  49. }
  50. public static void makeCoalescing$(Actor self, ActorPool pool, Func k, Func c) { makeCoalescing$(self, pool, k, c, null); }
  51. public static void makeCoalescing$(Actor self, ActorPool pool, Func k, Func c, Func r)
  52. {
  53. if (k != null) k = (Func)k.toImmutable();
  54. if (c != null) c = (Func)c.toImmutable();
  55. make$(self, pool, r);
  56. self.queue = new CoalescingQueue(k, c);
  57. }
  58. public Actor()
  59. {
  60. this.context = new Context(this);
  61. }
  62. //////////////////////////////////////////////////////////////////////////
  63. // Obj
  64. //////////////////////////////////////////////////////////////////////////
  65. public Type typeof()
  66. {
  67. if (type == null) type = Type.find("concurrent::Actor");
  68. return type;
  69. }
  70. private static Type type;
  71. //////////////////////////////////////////////////////////////////////////
  72. // Actor
  73. //////////////////////////////////////////////////////////////////////////
  74. public final ActorPool pool() { return pool; }
  75. public final Future send(Object msg) { return _send(msg, null, null); }
  76. public final Future sendLater(Duration d, Object msg) { return _send(msg, d, null); }
  77. public final Future sendWhenDone(Future f, Object msg) { return _send(msg, null, f); }
  78. protected Object receive(Object msg)
  79. {
  80. if (receive != null) return receive.call(msg);
  81. System.out.println("WARNING: " + typeof() + ".receive not overridden");
  82. return null;
  83. }
  84. public final long queueSize() { return queue.size; }
  85. //////////////////////////////////////////////////////////////////////////
  86. // Utils
  87. //////////////////////////////////////////////////////////////////////////
  88. public static void sleep(Duration duration)
  89. {
  90. try
  91. {
  92. long ticks = duration.ticks;
  93. java.lang.Thread.sleep(ticks/1000000L, (int)(ticks%1000000L));
  94. }
  95. catch (InterruptedException e)
  96. {
  97. throw InterruptedErr.make(e);
  98. }
  99. }
  100. public static Map locals() { return (Map)locals.get(); }
  101. private static final ThreadLocal locals = new ThreadLocal()
  102. {
  103. protected Object initialValue()
  104. {
  105. return new Map(Sys.StrType, Sys.ObjType.toNullable());
  106. }
  107. };
  108. //////////////////////////////////////////////////////////////////////////
  109. // Implementation
  110. //////////////////////////////////////////////////////////////////////////
  111. private Future _send(Object msg, Duration dur, Future whenDone)
  112. {
  113. // ensure immutable or safe copy
  114. msg = Sys.safe(msg);
  115. // don't deliver new messages to a stopped pool
  116. if (pool.isStopped()) throw Err.make("ActorPool is stopped");
  117. // get the future instance to manage this message's lifecycle
  118. Future f = new Future(msg);
  119. // either enqueue immediately or schedule with pool
  120. if (dur != null)
  121. pool.schedule(this, dur, f);
  122. else if (whenDone != null)
  123. whenDone.sendWhenDone(this, f);
  124. else
  125. f = _enqueue(f, true);
  126. return f;
  127. }
  128. final Future _enqueue(Future f, boolean coalesce)
  129. {
  130. synchronized (lock)
  131. {
  132. // attempt to coalesce
  133. if (coalesce)
  134. {
  135. Future c = queue.coalesce(f);
  136. if (c != null) return c;
  137. }
  138. // add to queue
  139. queue.add(f);
  140. // submit to thread pool if not submitted or current running
  141. if (!submitted)
  142. {
  143. submitted = true;
  144. pool.submit(this);
  145. }
  146. return f;
  147. }
  148. }
  149. public final void _work()
  150. {
  151. // set locals for this actor
  152. locals.set(context.locals);
  153. Locale.setCur(context.locale);
  154. // process up to 100 messages before yielding the thread
  155. int maxMessages = (int)pool.maxMsgsBeforeYield;
  156. for (int count = 0; count < maxMessages; count++)
  157. {
  158. // get next message, or if none pending we are done
  159. Future future = null;
  160. synchronized (lock) { future = queue.get(); }
  161. if (future == null) break;
  162. // dispatch the messge
  163. _dispatch(future);
  164. }
  165. // flush locals back to context
  166. context.locale = Locale.cur();
  167. // done dispatching, either clear the submitted
  168. // flag or resubmit to the thread pool
  169. synchronized (lock)
  170. {
  171. if (queue.size == 0)
  172. {
  173. submitted = false;
  174. }
  175. else
  176. {
  177. submitted = true;
  178. pool.submit(this);
  179. }
  180. }
  181. }
  182. final void _dispatch(Future future)
  183. {
  184. try
  185. {
  186. if (future.isCancelled()) return;
  187. if (pool.killed) { future.cancel(); return; }
  188. future.set(receive(future.msg));
  189. }
  190. catch (Err e)
  191. {
  192. future.err(e);
  193. }
  194. catch (Throwable e)
  195. {
  196. future.err(Err.make(e));
  197. }
  198. }
  199. public void _kill()
  200. {
  201. // get/reset the pending queue
  202. Queue queue = null;
  203. synchronized (lock)
  204. {
  205. queue = this.queue;
  206. this.queue = new Queue();
  207. }
  208. // cancel all pending messages
  209. while (true)
  210. {
  211. Future future = queue.get();
  212. if (future == null) break;
  213. future.cancel();
  214. }
  215. }
  216. //////////////////////////////////////////////////////////////////////////
  217. // Queue
  218. //////////////////////////////////////////////////////////////////////////
  219. static class Queue
  220. {
  221. public Future get()
  222. {
  223. if (head == null) return null;
  224. Future f = head;
  225. head = f.next;
  226. if (head == null) tail = null;
  227. f.next = null;
  228. size--;
  229. return f;
  230. }
  231. public void add(Future f)
  232. {
  233. if (tail == null) { head = tail = f; f.next = null; }
  234. else { tail.next = f; tail = f; }
  235. size++;
  236. }
  237. public Future coalesce(Future f)
  238. {
  239. return null;
  240. }
  241. Future head, tail;
  242. int size;
  243. }
  244. //////////////////////////////////////////////////////////////////////////
  245. // CoalescingQueue
  246. //////////////////////////////////////////////////////////////////////////
  247. static class CoalescingQueue extends Queue
  248. {
  249. CoalescingQueue(Func toKeyFunc, Func coalesceFunc)
  250. {
  251. this.toKeyFunc = toKeyFunc;
  252. this.coalesceFunc = coalesceFunc;
  253. }
  254. public Future get()
  255. {
  256. Future f = super.get();
  257. if (f != null)
  258. {
  259. try
  260. {
  261. Object key = toKey(f.msg);
  262. if (key != null) pending.remove(key);
  263. }
  264. catch (Throwable e)
  265. {
  266. e.printStackTrace();
  267. }
  268. }
  269. return f;
  270. }
  271. public void add(Future f)
  272. {
  273. try
  274. {
  275. Object key = toKey(f.msg);
  276. if (key != null) pending.put(key, f);
  277. }
  278. catch (Throwable e)
  279. {
  280. e.printStackTrace();
  281. }
  282. super.add(f);
  283. }
  284. public Future coalesce(Future incoming)
  285. {
  286. Object key = toKey(incoming.msg);
  287. if (key == null) return null;
  288. Future orig = (Future)pending.get(key);
  289. if (orig == null) return null;
  290. orig.msg = coalesce(orig.msg, incoming.msg);
  291. return orig;
  292. }
  293. private Object toKey(Object obj)
  294. {
  295. return toKeyFunc == null ? obj : toKeyFunc.call(obj);
  296. }
  297. private Object coalesce(Object orig, Object incoming)
  298. {
  299. return coalesceFunc == null ? incoming : coalesceFunc.call(orig, incoming);
  300. }
  301. Func toKeyFunc, coalesceFunc;
  302. HashMap pending = new HashMap();
  303. }
  304. //////////////////////////////////////////////////////////////////////////
  305. // Context
  306. //////////////////////////////////////////////////////////////////////////
  307. static final class Context
  308. {
  309. Context(Actor actor) { this.actor = actor; }
  310. final Actor actor;
  311. final Map locals = new Map(Sys.StrType, Sys.ObjType.toNullable());
  312. Locale locale = Locale.cur();
  313. }
  314. //////////////////////////////////////////////////////////////////////////
  315. // Fields
  316. //////////////////////////////////////////////////////////////////////////
  317. final Context context; // mutable world state of actor
  318. private ActorPool pool; // pooled controller
  319. private Func receive; // func to invoke on receive or null
  320. private Object lock = new Object(); // lock for message queue
  321. private Queue queue; // message queue linked list
  322. private boolean submitted = false; // is actor submitted to thread pool
  323. }