1 /**
2  * Defines the job interface, and some simple utilities to go with it.
3  */
4 module scheduled.job;
5 
6 import std.datetime;
7 import scheduled.schedule;
8 
9 /**
10  * A job is a task which is submitted to the scheduler, to be run one or more
11  * times, according to a given schedule.
12  */
13 public interface Job {
14     /**
15      * The method which is called to execute this job.
16      */
17     public void run();
18 }
19 
20 /** 
21  * Simple job that executes a function.
22  */
23 public class FunctionJob : Job {
24     /** 
25      * The function to execute when this job is run.
26      */
27     private void function() fn;
28 
29     /** 
30      * Constructs a job that will run the given function.
31      * Params:
32      *   fn = The function to execute.
33      */
34     this(void function() fn) {
35         this.fn = fn;
36     }
37 
38     /** 
39      * Runs the function.
40      */
41     override public void run() {
42         this.fn();
43     }
44 }
45 
46 /** 
47  * Represents a pairing of a Job with a schedule. This is a component that
48  * is utilized internally by Schedulers.
49  */
50 package final class ScheduledJob {
51     /** 
52      * The component which is used to obtain current timestamps.
53      */
54     private const CurrentTimeProvider timeProvider;
55 
56     /** 
57      * The schedule which defines when the associated job will run.
58      */
59     private JobSchedule schedule;
60 
61     /** 
62      * The job which will run according to the associated schedule.
63      */
64     private Job job;
65 
66     /** 
67      * The unique id for this scheduled job, within the context of the
68      * scheduler that's responsible for it. This id should be unique among all
69      * jobs currently managed by the scheduler, but ids may be reused once old
70      * jobs are removed.
71      */
72     private immutable long id;
73 
74     /** 
75      * Constructs a new pairing of a job and a schedule.
76      * Params:
77      *   job = The job which is scheduled.
78      *   schedule = The schedule that defines when the job will run.
79      *   id = The unique id for this job.
80      *   timeProvider = Provider of current timestamps.
81      */
82     package this(Job job, JobSchedule schedule, long id, CurrentTimeProvider timeProvider) {
83         this.job = job;
84         this.schedule = schedule;
85         this.timeProvider = timeProvider;
86         this.id = id;
87     }
88 
89     /** 
90      * Constructs a new pairing of a job and a schedule, with the default
91      * system time provider.
92      * Params:
93      *   job = The job which is scheduled.
94      *   schedule = The schedule that defines when the job will run.
95      *   id = The unique id for this job.
96      */
97     package this(Job job, JobSchedule schedule, long id) {
98         this(job, schedule, id, new SysTimeProvider);
99     }
100 
101     /** 
102      * Gets the schedule from this pairing.
103      * Returns: The schedule.
104      */
105     public JobSchedule getSchedule() {
106         return this.schedule;
107     }
108 
109     /** 
110      * Gets the job from this pairing.
111      * Returns: The job.
112      */
113     public Job getJob() {
114         return this.job;
115     }
116 
117     /** 
118      * Gets the id for this scheduled job.
119      * Returns: The id.
120      */
121     public long getId() {
122         return this.id;
123     }
124 
125     /** 
126      * Compares two scheduled jobs, such that jobs whose next execution time
127      * is earlier, are considered greater than others.
128      * Params:
129      *   other = The object to compare to.
130      * Returns: 1 if this object is greater, -1 if this object is less, or 0
131      * otherwise.
132      */
133     override int opCmp(Object other) {
134         if (auto otherJob = cast(ScheduledJob) other) {
135             if (this.getId() == otherJob.getId()) return 0; // Exit right away if we're comparing to the same scheduled job.
136             SysTime now = timeProvider.now;
137             auto t1 = this.getSchedule().getNextExecutionTime(now);
138             auto t2 = otherJob.getSchedule().getNextExecutionTime(now);
139             if (t1.isNull && t2.isNull) return 0;
140             if (!t1.isNull && t2.isNull) return 1;
141             if (t1.isNull) return -1;
142             return t2.get.opCmp(t1.get);
143         } else {
144             return 0;
145         }
146     }
147 }
148 
149 /**
150  * Tests the functionality of comparing two scheduled jobs.
151  */
152 unittest {
153     import std.experimental.logger;
154     import scheduled.schedules.one_time;
155 
156     JobSchedule s1 = new OneTimeSchedule(msecs(50));
157     JobSchedule s2 = new OneTimeSchedule(msecs(500));
158     JobSchedule s3 = new OneTimeSchedule(msecs(2500));
159 
160     class IncrementJob : Job {
161         public uint x = 0;
162         public void run() {
163             x++;
164             logf(LogLevel.info, "Incrementing counter to %d.", x);
165         }
166     }
167     auto j = new IncrementJob;
168 
169     ScheduledJob jobA = new ScheduledJob(j, s1, 1);
170     ScheduledJob jobA2 = new ScheduledJob(j, s1, 2);
171     ScheduledJob jobB = new ScheduledJob(j, s2, 3);
172     ScheduledJob jobC = new ScheduledJob(j, s3, 4);
173     assert(jobA > jobB);
174     assert(jobA >= jobA2 && jobA <= jobA2);
175     assert(jobB > jobC);
176     assert(jobA > jobC);
177 }