summaryrefslogtreecommitdiff
path: root/src/mongo/util/background.h
blob: bc4cc28df6c91f29265d5adb75d8ec8cb74fcd64 (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
// @file background.h

/*    Copyright 2009 10gen Inc.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

#pragma once

#include <string>
#include <vector>

#include "concurrency/spin_lock.h"

namespace mongo {

    /**
     *  Background thread dispatching.
     *  subclass and define run()
     *
     *  It is ok to call go(), that is, run the job, more than once -- if the
     *  previous invocation has finished. Thus one pattern of use is to embed
     *  a backgroundjob in your object and reuse it (or same thing with
     *  inheritance).  Each go() call spawns a new thread.
     *
     *  Thread safety:
     *    note when job destructs, the thread is not terminated if still running.
     *    generally if the thread could still be running, allocate the job dynamically
     *    and set deleteSelf to true.
     *
     *    go() and wait() are not thread safe
     *    run() will be executed on the background thread
     *    BackgroundJob object must exist for as long the background thread is running
     */

    class BackgroundJob : boost::noncopyable {
    protected:
        /**
         * sub-class must instantiate the BackgroundJob
         *
         * @param selfDelete if set to true, object will destruct itself after the run() finished
         * @note selfDelete instances cannot be wait()-ed upon
         */
        explicit BackgroundJob(bool selfDelete = false);

        virtual std::string name() const = 0;

        /**
         * define this to do your work.
         * after this returns, state is set to done.
         * after this returns, deleted if deleteSelf true.
         *
         * NOTE:
         *   if run() throws, the exception will be caught within 'this' object and will ultimately lead to the
         *   BackgroundJob's thread being finished, as if run() returned.
         *
         */
        virtual void run() = 0;

    public:
        enum State {
            NotStarted,
            Running,
            Done
        };

        virtual ~BackgroundJob() { }

        /**
         * starts job.
         * returns immediately after dispatching.
         *
         * @note the BackgroundJob object must live for as long the thread is still running, ie
         * until getState() returns Done.
         */
        BackgroundJob& go();

        /**
         * wait for completion.
         *
         * @param msTimeOut maximum amount of time to wait in milliseconds
         * @return true if did not time out. false otherwise.
         *
         * @note you can call wait() more than once if the first call times out.
         * but you cannot call wait on a self-deleting job.
         */
        bool wait( unsigned msTimeOut = 0 );

        // accessors
        State getState() const;
        bool running() const;

    private:
        struct JobStatus;
        boost::shared_ptr<JobStatus> _status;  // shared between 'this' and body() thread

        void jobBody( boost::shared_ptr<JobStatus> status );

    };
    
    /**
     * these run "roughly" every minute
     * instantiate statically
     * class MyTask : public PeriodicTask {
     * public:
     *   virtual std::string name() const { return "MyTask; " }
     *   virtual void doWork() { log() << "hi" << endl; }
     * } myTask;
     */
    class PeriodicTask {
    public:
        PeriodicTask();
        virtual ~PeriodicTask();

        virtual void taskDoWork() = 0;
        virtual std::string taskName() const = 0;

        class Runner : public BackgroundJob {
        public:
            virtual ~Runner(){}

            virtual std::string name() const { return "PeriodicTask::Runner"; }
            
            virtual void run();
            
            void add( PeriodicTask* task );
            void remove( PeriodicTask* task );

        private:
            
            SpinLock _lock;
            
            // these are NOT owned by Runner
            // Runner will not delete these
            // this never gets smaller
            // only fields replaced with nulls
            std::vector< PeriodicTask* > _tasks;

        };

        static Runner* theRunner;

    };




} // namespace mongo