1 /** 2 * Copyright © DiamondMVC 2019 3 * License: MIT (https://github.com/DiamondMVC/Diamond/blob/master/LICENSE) 4 * Author: Jacob Jensen (bausshf) 5 */ 6 module diamond.tasks.scheduler; 7 8 import diamond.core.apptype; 9 10 static if (isWeb) 11 { 12 import diamond.errors.checks; 13 14 /// Global lock to ensure tasks etc. are synced properly between threads. 15 private static shared globalSchedulerLock = new Object; 16 17 /// The next scheduler id. 18 private static __gshared size_t _nextSchedulerId; 19 20 public import core.time : Duration, dur, weeks, days, hours, minutes, seconds, 21 msecs, usecs, hnsecs, nsecs; 22 23 /// Wrapper around a asynchronous scheduler. 24 final class Scheduler 25 { 26 private: 27 /// The id of the scheduler. 28 size_t _id; 29 /// The collection of tasks associated with the scheduler. 30 ScheduledTask[size_t] _tasks; 31 /// The next task id. 32 size_t _nextTaskId; 33 34 public: 35 final: 36 /// Creates a new asynchronous scheduler. 37 this() 38 { 39 synchronized (globalSchedulerLock) 40 { 41 _nextSchedulerId++; 42 _id = _nextSchedulerId; 43 } 44 } 45 46 @property 47 { 48 /// Gets the id of the scheduler. 49 size_t id() { return _id; } 50 } 51 52 /** 53 * Addds a scheduled task to the scheduler. 54 * Params: 55 * task = the task to add. 56 */ 57 void addTask(ScheduledTask task) 58 { 59 synchronized (globalSchedulerLock) 60 { 61 enforce(!task.scheduler, "The task is associated with a scheduler."); 62 63 _nextTaskId++; 64 task._id = _nextTaskId; 65 task._scheduler = this; 66 67 _tasks[task.id] = task; 68 69 task.execute(); 70 } 71 } 72 73 /** 74 * Removes a task from the scheduler and halts its execution. 75 * Params: 76 * task = The task to remove. 77 */ 78 void removeTask(ScheduledTask task) 79 { 80 synchronized (globalSchedulerLock) 81 { 82 enforce(task.scheduler && task.scheduler.id == _id, "The task is not associated with this scheduler."); 83 84 task._scheduler = null; 85 _tasks.remove(task._id); 86 } 87 } 88 } 89 90 /// Wrapper around an asynchronous scheduled task. 91 final class ScheduledTask 92 { 93 private: 94 /// The id of the task. 95 size_t _id; 96 /// The amount of times the task should be repeated. 97 size_t _repeat; 98 /// The remaining amount of times the task should be repeated. 99 size_t _remaningRepeats; 100 /// The time to wait between each execution of the task. 101 Duration _waitTime; 102 /// The associated execution task. 103 void delegate() _task; 104 /// The scheduler. 105 Scheduler _scheduler; 106 107 public: 108 final: 109 /** 110 * Creates a new asynchronous scheduled task. 111 * Params: 112 * waitTime = the time to wait between each execution of the task. 113 * task = The task to execute. 114 * repeat = (optional) The amount of times the task should be repeated. 0 = forever. 115 */ 116 this(Duration waitTime, void delegate() task, size_t repeat = 0) 117 { 118 _waitTime = waitTime; 119 _task = task; 120 _repeat = repeat; 121 _remaningRepeats = _repeat; 122 } 123 124 @property 125 { 126 /// Gets the id of the task. 127 size_t id() { return _id; } 128 129 /// Gets the time to wait between each execution of the task. 130 Duration waitTime() { return _waitTime; } 131 132 /// Gets the amount of times the task should be repeated. 133 size_t repeat() { return _repeat; } 134 135 /// Gets the scheduler of the task. 136 Scheduler scheduler() { return _scheduler; } 137 } 138 139 private: 140 /// Executes the task. 141 void execute() 142 { 143 import diamond.tasks.core; 144 145 executeTask( 146 { 147 while (_scheduler !is null && (_repeat == 0 || _remaningRepeats > 0)) 148 { 149 sleep(_waitTime); 150 151 if (_scheduler) 152 { 153 _task(); 154 155 if (_repeat != 0) 156 { 157 _remaningRepeats--; 158 } 159 } 160 } 161 }); 162 } 163 } 164 }