// Copyright © 2017-2018 Atomic Software, LLC. All Rights Reserved. // See LICENSE.md for full license information. using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; namespace Atom.Core.Threading { public class ThreadPool { public static readonly ThreadPool Instance = new ThreadPool(); private readonly int MaxThreadCount = Environment.ProcessorCount + 2; private readonly Collections.List Workers = new Collections.List(); private readonly Queue WorkItems = new Queue(); private readonly ManualResetEvent WorkAvailable = new ManualResetEvent(false); private SpinLock SpinLock = new SpinLock(); private int ActiveThreadCount; public void QueueWorkItem(Action workItem) { var LockTaken = false; try { SpinLock.Enter(ref LockTaken); PooledDelegateHelper.AddReference(workItem); WorkItems.Enqueue(workItem); if (ActiveThreadCount + 1 >= Workers.Count && Workers.Count < MaxThreadCount) { Workers.Add(Task.Factory.StartNew(ProcessWorkItems, Workers.Count, TaskCreationOptions.LongRunning)); } WorkAvailable.Set(); } finally { if (LockTaken) { SpinLock.Exit(true); } } } private void ProcessWorkItems(object state) { while (true) { Action WorkItem = null; var LockTaken = false; try { SpinLock.Enter(ref LockTaken); if (WorkItems.Count > 0) { try { WorkItem = WorkItems.Dequeue(); if (WorkItems.Count == 0) { WorkAvailable.Reset(); } } catch { // ignored } } } finally { if (LockTaken) { SpinLock.Exit(true); } } if (WorkItem != null) { try { Interlocked.Increment(ref ActiveThreadCount); WorkItem.Invoke(); } catch (Exception) { // ignored } finally { PooledDelegateHelper.Release(WorkItem); Interlocked.Decrement(ref ActiveThreadCount); } } WorkAvailable.WaitOne(); } } } }