aboutsummaryrefslogtreecommitdiff
path: root/src/stable/minicron.h
blob: b273d88463453b1c96c7228933e5fae512f1ef57 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
/*
 * Copyright (C) 2007-2023 Xagasoft, All rights reserved.
 *
 * This file is part of the libbu++ library and is released under the
 * terms of the license contained in the file LICENSE.
 */

#ifndef BU_MINICRON_H
#define BU_MINICRON_H

#include "bu/signals.h"
#include "bu/heap.h"
#include "bu/string.h"

#include <time.h>

namespace Bu
{
    /**
     * A simple cron like system designed to be embedded in any program.  This
     * class creates a simple cron system that can run any number of jobs at
     * customizable intervals or schedules.  It does not support some of the
     * more complex scheduling that some cron systems can do such as load
     * balancing directly, but this could be done on the job side.
     *
     * This system is synchronous, it does not use any threads on it's own, but
     * it is threadsafe, so a cron thread could be created if desired.
     *
     * The operation is fairly simple, jobs can be added at any time, and use
     * any timer they would like, even custom timers.  When it is time for a
     * job to be run it signals the slot provided when the job was added.  Every
     * job slot recieves a handle to the job object so that it may control it's
     * own lifetime and get information about itself.  In addition, every job
     * is assigned a unique ID that can be used to control it's operation
     * at any time.
     *
     * By default a job will continually reschedule itself after being run
     * unless it calls stop() on it's job object, it is removed using
     * removeJob() on the cron object, or it is added with addJobOnce.
     *
     *@todo A minor change to the job execution system could allow a Timer to
     * defer or reschedule execution instead of the job executing.  This would,
     * in effect, allow us to do every type of interesting scheduling that
     * systems like fcron offer, including time constrained load-balanced
     * execution.
     */
    class MiniCron
    {
    public:
        class Job;
        class Timer;
        typedef Bu::Signal1<void, Bu::MiniCron::Job &> CronSignal;
        typedef int JobId;

        MiniCron();
        virtual ~MiniCron();

        /**
         * Tells you if there are jobs registered in the MiniCron.
         *@returns true if there are jobs, false otherwise.
         */
        virtual bool hasJobs();

        /**
         * If there are jobs, tells you the time the next one will execute.
         *@returns The timestamp that the next job will execute at.
         */
        virtual time_t getNextRun();
        
        /**
         * Tells you the time the job matching jid will run next.
         *@returns The timestamp that the job jid will next run.
         */
        virtual time_t getNextRun( JobId jid );

        /**
         * Call this regularly to execute all jobs that should be executed.
         * This will loop until all jobs who's run time match the current time
         * or are below the current time (we've missed them).
         * If there is nothing to run, the runtime of this funcion is constant,
         * it is very fast.  Otherwise it executes at log(N) per job run,
         * O(N*log(N)).
         */
        virtual void poll();

        /**
         * Add a job for repeated scheduling.  Pass in a slot to signal, and a
         * Timer object to use to do the scheduling.  This function returns a
         * JobId which can be used at a later time to control the execution of
         * the job.
         */
        virtual JobId addJob( const Bu::String &sName, CronSignal sigJob,
                const Timer &t );

        /**
         * Add a job for one time scheduling.  Pass in a slot to signal, and a
         * Timer object to use to schodule the one run of this job.  This
         * function returns a JobId which can be used at a later time to control
         * the execution of the job.
         */
        virtual JobId addJobOnce( const Bu::String &sName, CronSignal sigJob,
                const Timer &t );

        /**
         * Remove a job, preventing all future runs of the job.  If there is no
         * job matching the given JobId then nothing will happen.  However, this
         * function is relatively expensive compared to the others in this class
         * and has a worse case runtime of 2*N*log(N), still not that bad, and
         * a O(N*log(N)).
         */
        virtual void removeJob( JobId jid );

        /**
         * Executes the job specified right now.  If bReschedule is true then
         * the job is then removed from the queue and rescheduled as though
         * it's time had come naturally to be run.  Otherwise, it's run without
         * interrupting the normal schedule.
         */
        virtual void runJob( JobId jid, bool bReschedule=false );

        /**
         * Executes the job specified right now.  If bReschedule is true then
         * the job is then removed from the queue and rescheduled as though
         * it's time had come naturally to be run.  Otherwise, it's run without
         * interrupting the normal schedule.
         */
        virtual void runJob( const Bu::String &sName, bool bReschedule=false );

        class JobInfo
        {
        public:
            JobInfo( const Bu::String &sName, JobId jid, time_t tNext );
            virtual ~JobInfo();
            
            bool operator<( const JobInfo &rhs ) const;

            Bu::String sName;
            JobId jid;
            time_t tNext;
        };
        typedef Bu::List<JobInfo> JobInfoList;

        JobInfoList getJobInfo();

        /**
         * The baseclass for timer/schedulers for MiniCron jobs.  Classes that
         * inherit from this are used to determine when jobs will run and at
         * what interval.
         */
        class Timer
        {
        public:
            Timer();
            virtual ~Timer();

            /**
             * Called by MiniCron when each job is run to determine the next
             * time that a job should be run.  When a job is run, this function
             * is actually called before the job is executed again so that the
             * job can tell when the next time it will be run will be.
             */
            virtual time_t nextTime()=0;

            /**
             * This function should return a copy of the child class.
             */
            virtual Timer *clone() const = 0;
        };

        /**
         * Execute the job every tInterval seconds, also you can delay the
         * first run by a different amount of time from the job's creation.
         */
        class TimerInterval : public Timer
        {
        public:
            TimerInterval( time_t tFirst, time_t tInterval );
            virtual ~TimerInterval();

            virtual time_t nextTime();
            virtual Timer *clone() const
                { return new TimerInterval( *this ); }
        private:
            time_t tNext;
            time_t tInterval;
        };

        /**
         * A much more general timer class that can be used for much more
         * "cron-like" functionality.  The constructor takes a string that
         * describes the times that the job should be run.  At the moment the
         * following schemes are understood:
         *
         * "daily [hour] [minute]"
         * "hourly [minute]"
         * "weekly [day] [hour] [minute]"
         *
         * In these examples each word in [brackets] represents a number that
         * matches the data type in the brackets.  [day] is the number of days
         * since sunday, 0-6.  You can also use lowercase three character
         * abbreviations for the day names.
         *
         * Many more forms follow.
         */
        class TimerBasic : public Timer
        {
        public:
            TimerBasic( const Bu::String &s );
            virtual ~TimerBasic();

            virtual time_t nextTime();
            virtual Timer *clone() const
                { return new TimerBasic( *this ); }

        private:
            enum Token
            {
                tokDaily,
                tokHourly,
                tokWeekly,
                tokMonthly,
                tokYearly,
                valInt,
                tokErr,
                tokEos
            };
            Token lex( Bu::String::const_iterator &i );
            int lexInt( Bu::String::const_iterator &i );
            int iVal; //< A temp variable for parsing.
            time_t tLast;
            Bu::String sSpec;
        };
        
        /**
         * Represents a MiniCron Job.  This class is used for both internal
         * job management as well as job slot interaction and control.  Objects
         * of this class are passed into the slots that are signaled when a job
         * is executed.
         */
        class Job
        {
            friend class Bu::MiniCron;
        private:
            Job( const Bu::String &sName, JobId jid, bool bRepeat=true );
            virtual ~Job();

        public:

            /**
             * Execute this job once, increment the runcount and schedule the
             * next occurance of it.
             */
            void run( bool bReschedule=true );

            /**
             * Get the time this job will next run.
             */
            time_t getNextRun() const;

            /**
             * Compute the time this job will next run.
             */
            void calcNextRun();

            /**
             * Replace the current job timer with a new one, this will trigger
             * a re-schedule.
             */
            void setTimer( const Timer &t );

            /**
             * Stop execution of this job, never execute this job again.
             */
            void stop();

            /**
             * Undo a previous stop.  This will cause a job that has been
             * stopped or even added with addJobOnce to be set for repeated
             * scheduling.
             */
            void resume();

            /**
             * Get the unique ID of this job.
             */
            JobId getId() const;

            /**
             * Get the timestamp this job was created.
             */
            time_t getTimeCreated() const;

            /**
             * Get the current run count of this job, how many times it has been
             * executed.  This is incremented before the slot is signaled.
             */
            int getRunCount() const;

            /**
             * Get the next time that this job will be run.  Certain timers may
             * have the ability to delay job executions, so this is the earliest
             * time that the job may run.
             */
            time_t getNextRunTime() const;

            /**
             * Gets the name that was set when the job was created.
             */
            Bu::String getName() const;

        private:
            Bu::String sName;
            CronSignal sigJob;
            time_t tNextRun;
            Timer *pTimer;
            bool bContinue;
            JobId jid;
            time_t tAdded;
            int iRunCount;
        };

    private:
        struct JobPtrCmp
        {
            bool operator()( const Job *pLeft, const Job *pRight )
            {
                return pLeft->tNextRun < pRight->tNextRun;
            }
        };
        typedef Bu::Heap<Job *, JobPtrCmp> JobHeap;
        JobHeap hJobs;
        JobId jidNext;
    };
};

#endif