1 // @file background.h
2 
3 
4 /**
5  *    Copyright (C) 2018-present MongoDB, Inc.
6  *
7  *    This program is free software: you can redistribute it and/or modify
8  *    it under the terms of the Server Side Public License, version 1,
9  *    as published by MongoDB, Inc.
10  *
11  *    This program is distributed in the hope that it will be useful,
12  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *    Server Side Public License for more details.
15  *
16  *    You should have received a copy of the Server Side Public License
17  *    along with this program. If not, see
18  *    <http://www.mongodb.com/licensing/server-side-public-license>.
19  *
20  *    As a special exception, the copyright holders give permission to link the
21  *    code of portions of this program with the OpenSSL library under certain
22  *    conditions as described in each individual source file and distribute
23  *    linked combinations including the program with the OpenSSL library. You
24  *    must comply with the Server Side Public License in all respects for
25  *    all of the code used other than as permitted herein. If you modify file(s)
26  *    with this exception, you may extend this exception to your version of the
27  *    file(s), but you are not obligated to do so. If you do not wish to do so,
28  *    delete this exception statement from your version. If you delete this
29  *    exception statement from all source files in the program, then also delete
30  *    it in the license file.
31  */
32 
33 #pragma once
34 
35 #include <memory>
36 #include <string>
37 #include <vector>
38 
39 #include "mongo/base/disallow_copying.h"
40 #include "mongo/base/status.h"
41 
42 namespace mongo {
43 
44 /**
45  *  Background thread dispatching.
46  *  subclass and define run()
47  *
48  *  It is not possible to run the job more than once. An attempt to call 'go' while the
49  *  task is running will fail. Calling 'go' after the task has finished are ignored and
50  *  will not start the job again.
51  *
52  *  Thread safety: Note that when the job destructs, the thread is not terminated if still
53  *  running. Generally, if the thread could still be running, allocate the job dynamically
54  *  and set deleteSelf to true.
55  *
56  *  The overridden run() method will be executed on the background thread, so the
57  *  BackgroundJob object must exist for as long the background thread is running.
58  */
59 
60 class BackgroundJob {
61     MONGO_DISALLOW_COPYING(BackgroundJob);
62 
63 protected:
64     /**
65      * sub-class must instantiate the BackgroundJob
66      *
67      * @param selfDelete if set to true, object will destruct itself after the run() finished
68      * @note selfDelete instances cannot be wait()-ed upon
69      */
70     explicit BackgroundJob(bool selfDelete = false);
71 
72     virtual std::string name() const = 0;
73 
74     /**
75      * define this to do your work.
76      * after this returns, state is set to done.
77      * after this returns, deleted if deleteSelf true.
78      *
79      * NOTE:
80      *   if run() throws, the exception will be caught within 'this' object and will ultimately lead
81      *   to the BackgroundJob's thread being finished, as if run() returned.
82      *
83      */
84     virtual void run() = 0;
85 
86 public:
87     enum State { NotStarted, Running, Done };
88 
89     virtual ~BackgroundJob();
90 
91     /**
92      * starts job.
93      * returns immediately after dispatching.
94      *
95      * @note the BackgroundJob object must live for as long the thread is still running, ie
96      * until getState() returns Done.
97      */
98     void go();
99 
100 
101     /**
102      * If the job has not yet started, transitions the job to the 'done' state immediately,
103      * such that subsequent calls to 'go' are ignored, and notifies any waiters waiting in
104      * 'wait'. If the job has already been started, this method returns a not-ok status: it
105      * does not cancel running jobs. For this reason, you must still call 'wait' on a
106      * BackgroundJob even after calling 'cancel'.
107      */
108     Status cancel();
109 
110     /**
111      * wait for completion.
112      *
113      * @param msTimeOut maximum amount of time to wait in milliseconds
114      * @return true if did not time out. false otherwise.
115      *
116      * @note you can call wait() more than once if the first call times out.
117      * but you cannot call wait on a self-deleting job.
118      */
119     bool wait(unsigned msTimeOut = 0);
120 
121     // accessors. Note that while the access to the internal state is synchronized within
122     // these methods, there is no guarantee that the BackgroundJob is still in the
123     // indicated state after returning.
124     State getState() const;
125     bool running() const;
126 
127 private:
128     const bool _selfDelete;
129 
130     struct JobStatus;
131     const std::unique_ptr<JobStatus> _status;
132 
133     void jobBody();
134 };
135 
136 /**
137  * these run "roughly" every minute
138  * instantiate statically
139  * class MyTask : public PeriodicTask {
140  * public:
141  *   virtual std::string name() const { return "MyTask; " }
142  *   virtual void doWork() { log() << "hi" << std::endl; }
143  * } myTask;
144  */
145 class PeriodicTask {
146 public:
147     PeriodicTask();
148     virtual ~PeriodicTask();
149 
150     virtual void taskDoWork() = 0;
151     virtual std::string taskName() const = 0;
152 
153     /**
154      *  Starts the BackgroundJob that runs PeriodicTasks. You may call this multiple times,
155      *  from multiple threads, and the BackgroundJob will be started only once. Please note
156      *  that since this method starts threads, it is not appropriate to call it from within
157      *  a mongo initializer. Calling this method after calling 'stopRunningPeriodicTasks'
158      *  does not re-start the background job.
159      */
160     static void startRunningPeriodicTasks();
161 
162     /**
163      *  Waits 'gracePeriodMillis' for the BackgroundJob responsible for PeriodicTask
164      *  execution to finish any running tasks, then destroys it. If the BackgroundJob was
165      *  never started, returns Status::OK right away. If the BackgroundJob does not
166      *  terminate within the grace period, returns an invalid status. It is safe to call
167      *  this method repeatedly from one thread if the grace period is overshot. It is not
168      *  safe to call this method from multiple threads, or in a way that races with
169      *  'startRunningPeriodicTasks'.
170      */
171     static Status stopRunningPeriodicTasks(int gracePeriodMillis);
172 };
173 
174 
175 }  // namespace mongo
176