diff --git a/ManagedPool.sln b/ManagedPool.sln new file mode 100644 index 0000000..d792b9d --- /dev/null +++ b/ManagedPool.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.28307.271 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ManagedPool", "ManagedPool\ManagedPool.csproj", "{6E45952C-6966-4CF0-9C32-7A35D54326FE}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6E45952C-6966-4CF0-9C32-7A35D54326FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6E45952C-6966-4CF0-9C32-7A35D54326FE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6E45952C-6966-4CF0-9C32-7A35D54326FE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6E45952C-6966-4CF0-9C32-7A35D54326FE}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {0737324E-EA5C-4D9A-8EF2-90C8717E38E3} + EndGlobalSection +EndGlobal diff --git a/ManagedPool/ManagedPool.csproj b/ManagedPool/ManagedPool.csproj new file mode 100644 index 0000000..bc41c57 --- /dev/null +++ b/ManagedPool/ManagedPool.csproj @@ -0,0 +1,13 @@ + + + + netcoreapp2.1 + false + eu.railduction.netcore.dll.ManagedPool + 1.1.0 + false + true + eu.railduction_assembly-key.pfx + + + diff --git a/ManagedPool/Pool_T_.cs b/ManagedPool/Pool_T_.cs new file mode 100644 index 0000000..c331d80 --- /dev/null +++ b/ManagedPool/Pool_T_.cs @@ -0,0 +1,269 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace eu.railduction.netcore.dll.ManagedPool +{ + public class Pool where T : class + { + private Func creator; + /// Method to handle item-spawning (needs to return the item-type) + public Func Creator { get => creator; set => creator = value; } + + private Func healthChecker = (T) => { return true; }; + /// Method to handle item-health-checking (useful for unstable items) + /// While running the health-check, the Thread will be blocked! + public Func HealthChecker { get => healthChecker; set => healthChecker = value; } + + private Action destructor; + /// Method to handle item-deleting/killing (needs to take the item-type as parameter) + public Action Destructor { get => destructor; set => destructor = value; } + + private ConcurrentStack pool = new ConcurrentStack(); + private int current = 0, max = 100, min = 10, batch = 5, increaseBuffer = 5, decreaseBuffer = 10; + + /// The current amount of items in the pool + public int Current { get => min; } + /// The current amount of items avaiaible in the pool + public int CurrentAvaiaible { get => pool.Count; } + + /// The minimum amount of items in the pool + public int Min { get => min; set => min = value; } + /// The maximum amount of items in the pool + public int Max { get => max; set => max = value; } + /// Amount if items being spawned at a time if needed + public int Batch { get => batch; set => batch = value; } + /// If less items are in the pool that the buffer, it will spawn 'batch'-amount + public int IncreaseBuffer { get => increaseBuffer; set => increaseBuffer = value; } + /// If more items are in the pool that the buffer, it will delete/kill the overflowing items + public int DecreaseBuffer { get => decreaseBuffer; set => decreaseBuffer = value; } + + /// + /// Creates a managed pool + /// Will automatically spawn items + /// + /// The minimum amount of items in the pool + /// The maximum amount of items in the pool + /// Amount if items being spawned at a time if needed + /// If less items are in the pool than the , it will spawn -amount + /// If more items are in the pool than the , it will delete/kill the overflowing items + /// Method to handle item-spawning (needs to return the item-type) + /// Method to handle item-deleting/killing (needs to take the item-type as parameter) + public Pool(int min, int max, int batch, int increaseBuffer, int decreaseBuffer, Func creator, Action destructor) + { + this.min = min; + this.max = max; + this.batch = batch; + this.increaseBuffer = increaseBuffer; + this.decreaseBuffer = decreaseBuffer; + + // Validate objects state + validate(); + + this.creator = creator; + this.destructor = destructor; + + // Create the minimum amount of objects + Task.Run(() => createNewObjects(min)); + } + + /// + /// Creates a managed pool + /// Will automatically spawn items + /// The health-check is run before an item is used for execution + /// + /// The minimum amount of items in the pool + /// The maximum amount of items in the pool + /// Amount if items being spawned at a time if needed + /// If less items are in the pool than the , it will spawn -amount + /// If more items are in the pool than the , it will delete/kill the overflowing items + /// Method to handle item-spawning + /// Method to handle item-deleting/killing + /// Method to handle item-health-checking (useful for unstable items) + /// While running the health-check, the Thread will be blocked! + public Pool(int min, int max, int batch, int increaseBuffer, int decreaseBuffer, Func creator, Action destructor, Func healthChecker) + { + this.min = min; + this.max = max; + this.batch = batch; + this.increaseBuffer = increaseBuffer; + this.decreaseBuffer = decreaseBuffer; + + // Validate objects state + validate(); + + this.creator = creator; + this.destructor = destructor; + this.healthChecker = healthChecker; + + // Create the minimum amount of objects + Task.Run(() => createNewObjects(min)); + } + + private void validate() + { + if (min <= 0) throw new InvalidOperationException($"Invalid parameter min='{min}'. Must be at least '1'!"); + if (max < min) throw new InvalidOperationException($"Invalid parameter max='{max}'. Must be at least min='{min}'!"); + if (batch <= 0) throw new InvalidOperationException($"Invalid parameter batch='{batch}'. Must be at least '1'!"); + if (increaseBuffer <= 0) throw new InvalidOperationException($"Invalid parameter increaseBuffer='{increaseBuffer}'. Must be at least '1'!"); + if (decreaseBuffer <= 0) throw new InvalidOperationException($"Invalid parameter decreaseBuffer='{decreaseBuffer}'. Must be at least '1'!"); + } + + /// + /// Will try to get an avaible item out of the pool + /// If there is no item avaible, this function will block the thread and retry + /// + /// The item + public T getItem() + { + do{ + T item; + bool popped = pool.TryPop(out item); + + if (pool.Count < increaseBuffer) // less than 'buffer'-amount of items avaiaible + { + Task.Run(() => createNewObjects(batch)); // Start generating new ones + } + if (pool.Count > decreaseBuffer) // more than 'buffer'-amount of items avaiaible + { + Task.Run(() => decrease()); // run decease check + } + + if (popped) + { + if (healthChecker(item)) // Check for item-health + { + return item; // Item is healthy, return it + } + else + { + Task.Run(() => { + destruct(item); // Item is not healthy, destruct it + createNewObjects(1); // Create new one + }); + + popped = false; + } + } + + // No item or healthCheck failed + Thread.Sleep(1); // Block the current thread for short time + + } while (true); // retry infinite + + } + + private void createNewObjects(int size) + { + // create 'size'-amount of items, as long we dont hit our maximum + for (int i = 0; i < size && current < max; i++) + { + // create new item + T item = create(); + // add new item to the pool + pool.Push(item); + } + } + + private T create() + { + // Try to run create-method + T item; + try + { + current++; + item = creator(); + + // Check if create-delegate returned a null-reference + if (item == null) + { + throw new NullReferenceException("Creator-delegate returned 'null'-reference!"); + } + else + { + return item; + } + } + catch (Exception ex) + { + current--; + throw new Exception($"Error while creating new '{typeof(T).ToString()}'", ex); + } + } + + private void decrease() + { + if (pool.Count > decreaseBuffer + min) + { + for (int i = 0; i < pool.Count - decreaseBuffer - min; i++) + { + T item; + bool popped = pool.TryPop(out item); + if (popped) + { + // Run descructor-method with item + destruct(item); + } + } + } + } + + private void destruct(T item) + { + // decrease counter + current--; + // Destruct it + destructor(item); + } + + /// + /// Execute an function-task with an item of the pool + /// Will get an item out of the pool, run the and then return it to the pool. + /// + /// Task to run + public U Exec(Func task) + { + T item = getItem(); // Get item from stack + + try + { + return task(item); // Run task with item and return result + } + catch (Exception ex) + { + throw new Exception($"Error while executing task with '{typeof(T).ToString()}'", ex); + } + finally + { + pool.Push(item); // Put it back to the stack + Task.Run(() => decrease()); // Run decrease check + } + } + + /// + /// Execute an action-task with an item of the pool + /// Will get an item out of the pool, run the and then return it to the pool. + /// + /// Task to run + public void Exec(Action task) + { + T item = getItem(); // Get item from stack + + try + { + task(item); // Run my task with item + } + catch (Exception ex) + { + throw new Exception($"Error while executing task with '{typeof(T).ToString()}'", ex); + } + finally + { + pool.Push(item); // Put it back to the stack + Task.Run(() => decrease()); // Run decrease check + } + } + } +}