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 }