PageRenderTime 51ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/src/concurrent/dotnet/ThreadPool.cs

https://bitbucket.org/bedlaczech/fan-1.0
C# | 352 lines | 197 code | 41 blank | 114 comment | 37 complexity | 32c357ea984357b0d38d64839606a0ff 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. // 30 Mar 09 Andy Frank Creation
  7. //
  8. using System.Collections;
  9. using System.Collections.Generic;
  10. using System.Runtime.CompilerServices;
  11. using System.Threading;
  12. namespace Fanx.Util
  13. {
  14. /// <summary>
  15. /// ThreadPool manages a pool of threads optimized for the Actor framework.
  16. /// </summary>
  17. public class ThreadPool
  18. {
  19. //////////////////////////////////////////////////////////////////////////
  20. // Constructor
  21. //////////////////////////////////////////////////////////////////////////
  22. /// <summary>
  23. /// Construct with max number of threads.
  24. /// </summary>
  25. public ThreadPool(int max)
  26. {
  27. this.max = max;
  28. this.idleTime = 5000; // 5sec
  29. this.idle = new LinkedList<Worker>();
  30. this.pending = new LinkedList<Work>();
  31. this.workers = new Hashtable(max*3);
  32. this.state = RUNNING;
  33. }
  34. //////////////////////////////////////////////////////////////////////////
  35. // Lifecycle
  36. //////////////////////////////////////////////////////////////////////////
  37. /// <summary>
  38. /// Has this pool been stopped or killed.
  39. /// </summary>
  40. public bool isStopped()
  41. {
  42. return state != RUNNING;
  43. }
  44. /// <summary>
  45. /// Has all the work in this queue finished processing and
  46. /// all threads terminated.
  47. /// </summary>
  48. public bool isDone()
  49. {
  50. if (state == DONE) return true;
  51. lock (this)
  52. {
  53. if (state == RUNNING || workers.Count > 0) return false;
  54. state = DONE;
  55. return true;
  56. }
  57. }
  58. /// <summary>
  59. /// Orderly shutdown of threads. All pending work items are processed.
  60. /// </summary>
  61. [MethodImpl(MethodImplOptions.Synchronized)]
  62. public void stop()
  63. {
  64. state = STOPPING;
  65. // immediately wake up all the idle workers so they can die
  66. while (true)
  67. {
  68. LinkedListNode<Worker> node = idle.First;
  69. if (node == null) break;
  70. Worker w = (Worker)node.Value;
  71. idle.RemoveFirst();
  72. w.run(null);
  73. }
  74. }
  75. /// <summary>
  76. /// Unorderly shutdown of threads. All pending work are discarded,
  77. /// and interrupt is sent to each thread.
  78. /// </summary>
  79. [MethodImpl(MethodImplOptions.Synchronized)]
  80. public void kill()
  81. {
  82. state = STOPPING;
  83. // kill all the pending work
  84. while (true)
  85. {
  86. LinkedListNode<Work> node = pending.First;
  87. if (node == null) break;
  88. Work work = (Work)node.Value;
  89. pending.RemoveFirst();
  90. work._kill();
  91. }
  92. // interupt each thread
  93. IEnumerator en = workers.Values.GetEnumerator();
  94. while (en.MoveNext()) ((Worker)en.Current).thread.Interrupt();
  95. }
  96. /// <summary>
  97. /// Wait for all threads to stop.
  98. /// Return true on success or false on timeout.
  99. /// </summary>
  100. [MethodImpl(MethodImplOptions.Synchronized)]
  101. public bool join(long msTimeout)
  102. {
  103. long deadline = Fan.Sys.Sys.nanoTime()/1000000L + msTimeout;
  104. while (true)
  105. {
  106. // if all workers have completed, then return success
  107. if (workers.Count == 0) return true;
  108. // if we have gone past our deadline, return false
  109. long toSleep = deadline - Fan.Sys.Sys.nanoTime()/1000000L;
  110. if (toSleep <= 0) return false;
  111. // sleep until something interesting happens
  112. Monitor.Wait(this, (int)toSleep);
  113. }
  114. }
  115. //////////////////////////////////////////////////////////////////////////
  116. // Work Management
  117. //////////////////////////////////////////////////////////////////////////
  118. /// <summary>
  119. /// Submit the given work to be run by a thread in this pool.
  120. /// If an idle thread is available, the work is immediately
  121. /// run. If no idle threads are available, but the current number
  122. /// of threads is less than max, then launch a new thread to
  123. /// execute the work. If the current number of threads is at
  124. /// max, then queue the work until a thread becomes available.
  125. /// </summary>
  126. [MethodImpl(MethodImplOptions.Synchronized)]
  127. public void submit(Work work)
  128. {
  129. // if we have an idle thread, use it
  130. LinkedListNode<Worker> node = idle.First;
  131. if (node != null)
  132. {
  133. Worker worker = (Worker)node.Value;
  134. idle.RemoveFirst();
  135. worker.run(work);
  136. return;
  137. }
  138. // if we are below max, then spawn a new thread
  139. if (workers.Count < max)
  140. {
  141. Worker worker = new Worker(this, work);
  142. Thread thread = new Thread(worker.run);
  143. thread.Name = "ThreadPool-Worker-" + (counter++);
  144. worker.thread = thread;
  145. thread.Start();
  146. workers[worker] = worker;
  147. return;
  148. }
  149. // queue the runnable until we have an idle thread
  150. pending.AddLast(work);
  151. }
  152. /// <summary>
  153. /// This is called by a worker when it completes a work item.
  154. /// If there is pending work return it. Otherwise if idle time
  155. /// is over then free the worker and let it die. If idle time is
  156. /// not over then put the worker into our idle queue.
  157. /// </summary>
  158. [MethodImpl(MethodImplOptions.Synchronized)]
  159. internal Work ready(Worker w, bool idleTimeOver)
  160. {
  161. // if we have a pending work, then immediately reuse the worker
  162. LinkedListNode<Work> node = pending.First;
  163. if (node != null)
  164. {
  165. pending.RemoveFirst();
  166. return node.Value;
  167. }
  168. // if the worker's idle time is over or we are
  169. // shutting down, then free the worker and let it die
  170. if (idleTimeOver || state != RUNNING)
  171. {
  172. free(w);
  173. return null;
  174. }
  175. // add to head of idle list (we let oldest threads die out first)
  176. idle.AddFirst(w);
  177. return null;
  178. }
  179. /// <summary>
  180. /// Free worker from all data structures and let it die.
  181. /// </summary>
  182. [MethodImpl(MethodImplOptions.Synchronized)]
  183. internal void free(Worker w)
  184. {
  185. idle.Remove(w);
  186. workers.Remove(w);
  187. Monitor.PulseAll(this);
  188. }
  189. //////////////////////////////////////////////////////////////////////////
  190. // Debug
  191. //////////////////////////////////////////////////////////////////////////
  192. public void dump(Fan.Sys.List args)
  193. {
  194. Fan.Sys.OutStream @out = Fan.Sys.Env.cur().@out();
  195. if (args != null && args.size() > 0)
  196. @out = (Fan.Sys.OutStream)args.get(0);
  197. @out.printLine("ThreadPool");
  198. @out.printLine(" pending: " + pending.Count);
  199. @out.printLine(" idle: " + idle.Count);
  200. @out.printLine(" workers: " + workers.Count);
  201. IEnumerator en = workers.Values.GetEnumerator();
  202. while (en.MoveNext())
  203. {
  204. Worker w = (Worker)en.Current;
  205. @out.printLine(" " + w + " " + w.work);
  206. }
  207. }
  208. //////////////////////////////////////////////////////////////////////////
  209. // Worker
  210. //////////////////////////////////////////////////////////////////////////
  211. /// <summary>
  212. /// Worker is a reusable thread within the thread pool.
  213. /// </summary>
  214. internal class Worker
  215. {
  216. /// <summary>
  217. /// Construct with name and initial work to execute.
  218. /// </summary>
  219. public Worker(ThreadPool pool, Work work)
  220. {
  221. this.pool = pool;
  222. this.work = work;
  223. }
  224. /// <summary>
  225. /// Equality must be reference for storage in a hash table.
  226. /// </summary>
  227. public bool equals(object o)
  228. {
  229. return this == o;
  230. }
  231. /// <summary>
  232. /// A worker thread loops repeatly executing work until it times out.
  233. /// </summary>
  234. public void run()
  235. {
  236. try
  237. {
  238. // loop processing runnables
  239. while (true)
  240. {
  241. // execute work
  242. try { work._work(); } catch (System.Exception e) { Fan.Sys.Err.dumpStack(e); }
  243. work = null;
  244. // once I am finished this work, I need to
  245. // get more work or enter an idle state
  246. lock (this)
  247. {
  248. // let the thread pool know I am idle, if it has pending
  249. // work for me, then immediately execute it
  250. work = pool.ready(this, false);
  251. if (work != null) continue;
  252. // idle this thread for a period of time to
  253. // see if any new work becomes available
  254. try { Monitor.Wait(this, pool.idleTime); } catch (ThreadInterruptedException) {}
  255. // if work was given to me while I was waiting, then do it
  256. if (work != null) continue;
  257. // check back again for pending work but this time pass true for
  258. // idleTimeOver, if still no work for me then it is time to die
  259. work = pool.ready(this, true);
  260. if (work == null) return;
  261. }
  262. }
  263. }
  264. catch (System.Exception e)
  265. {
  266. // if an exception is raised, free worker
  267. Fan.Sys.Err.dumpStack(e);
  268. pool.free(this);
  269. }
  270. }
  271. /// <summary>
  272. /// Give this thread a work item and wake it up from its idle state.
  273. /// This method should never be called unless in the idle state.
  274. /// </summary>
  275. [MethodImpl(MethodImplOptions.Synchronized)]
  276. public void run(Work work)
  277. {
  278. this.work = work;
  279. Monitor.PulseAll(this);
  280. }
  281. internal ThreadPool pool;
  282. internal Thread thread;
  283. internal Work work;
  284. }
  285. //////////////////////////////////////////////////////////////////////////
  286. // Work
  287. //////////////////////////////////////////////////////////////////////////
  288. /// <summary>
  289. /// Item of work to execute in the thread pool.
  290. /// Note: method _work() is used so we don't polluate Actor's namespace.
  291. /// </summary>
  292. public interface Work
  293. {
  294. void _work();
  295. void _kill();
  296. }
  297. //////////////////////////////////////////////////////////////////////////
  298. // Fields
  299. //////////////////////////////////////////////////////////////////////////
  300. const int RUNNING = 0;
  301. const int STOPPING = 1;
  302. const int DONE = 2;
  303. internal readonly int max; // maximum number of threads to use
  304. internal readonly int idleTime; // time in ms to let threads idle (5sec)
  305. private volatile int state; // life cycle state
  306. private LinkedList<Worker> idle; // idle threads waiting for work
  307. private LinkedList<Work> pending; // pending working we don't have threads for yet
  308. private Hashtable workers; // map of all worker threads
  309. private int counter; // counter for all threads ever created
  310. }
  311. }