-
-
Notifications
You must be signed in to change notification settings - Fork 4
/
operationqueue.hpp
220 lines (165 loc) · 7.37 KB
/
operationqueue.hpp
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
// SPDX-License-Identifier: GPL-3.0-or-later
//
// Copyright (c) 2013-2023 plan44.ch / Lukas Zeller, Zurich, Switzerland
//
// Author: Lukas Zeller <[email protected]>
//
// This file is part of p44utils.
//
// p44utils is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// p44utils is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with p44utils. If not, see <http://www.gnu.org/licenses/>.
//
#ifndef __p44utils__operationqueue__
#define __p44utils__operationqueue__
#include "p44utils_main.hpp"
using namespace std;
namespace p44 {
class OQError : public Error
{
public:
// Errors
typedef enum {
OK,
Aborted,
TimedOut,
numErrorCodes
} ErrorCodes;
static const char *domain() { return "OperationQueue"; };
virtual const char *getErrorDomain() const P44_OVERRIDE { return OQError::domain(); };
OQError(ErrorCodes aError) : Error(ErrorCode(aError)) {};
#if ENABLE_NAMED_ERRORS
protected:
virtual const char* errorName() const P44_OVERRIDE { return errNames[getErrorCode()]; };
private:
static constexpr const char* const errNames[numErrorCodes] = {
"OK",
"Aborted",
"TimedOut",
};
#endif // ENABLE_NAMED_ERRORS
};
class Operation;
class OperationQueue;
/// Operation
typedef boost::intrusive_ptr<Operation> OperationPtr;
class Operation : public P44Obj
{
protected:
bool mInitiated;
bool mAborted;
MLMicroSeconds mTimeout; ///< timeout
MLMicroSeconds mTimesOutAt; ///< absolute time for timeout
MLMicroSeconds mInitiationDelay; ///< how much to delay initiation after first attempt to initiate (or after last initiation, see below)
bool mFromLastInitiation; ///< if set, initiationDelay counts from last initiation happened on this queue
MLMicroSeconds mInitiatesNotBefore; ///< absolute time for earliest initiation
StatusCB mCompletionCB; ///< completion callback
OperationPtr mChainedOp; ///< operation to insert once this operation has finalized
public:
/// if this flag is set, no operation queued after this operation will execute
bool mInSequence;
/// constructor
Operation();
virtual ~Operation();
/// reset operation (clear callbacks to break ownership loops)
/// @note no callbacks are called
void reset();
/// set completion callback
/// @param aCompletionCB will be called when operation completes or fails
void setCompletionCallback(StatusCB aCompletionCB);
/// chain another operation
/// @note after this operation has finalized, the specified operation will be inserted
/// into the queue in place of this operation
/// @note when an operation is chained, the completion callback will not be called.
/// Still it makes sense to set it in case the original operation is aborted.
void setChainedOperation(OperationPtr aChainedOp);
/// set delay for initiation (after first attempt to initiate)
/// @param aInitiationDelay how long to delay initiation of the operation minimally
/// @param aFromLastInitiation if set, delay is measured from last initiation on this queue
void setInitiationDelay(MLMicroSeconds aInitiationDelay, bool aFromLastInitiation = false);
/// set earliest time to execute
/// @param aInitiatesAt when to initiate the operation earliest
void setInitiatesAt(MLMicroSeconds aInitiatesAt);
/// set timeout (from initiation)
/// @param aTimeout after initiation, how long to wait for completion without timeout. Can be Never
void setTimeout(MLMicroSeconds aTimeout);
/// check if already initiated
bool isInitiated();
/// check if already aborted
bool isAborted();
/// called to check if operation has timed out
bool hasTimedOutAt(MLMicroSeconds aRefTime = MainLoop::now());
/// Methods to override by concrete subclasses of Operation
/// @{
/// check if can be initiated
/// @param aLastInitiation time when last operation was initiated on the queue (or Never)
/// @return false if cannot be initiated now and must be retried
/// @note base class implementation implements initiation delay here.
/// Derived classes might check other criteria in addition
virtual bool canInitiate(MLMicroSeconds aLastInitiation);
/// call to initiate operation
/// @note base class starts timeout when initiation has occurred
/// @note must only be called after canInitiate() returns true!
/// @return false if could not be initiated despite canInitiate() having returned true
/// (canInitiate() and initiate() will be retried in this case)
virtual bool initiate();
/// call to check if operation has completed (after being initiated)
/// @return true if completed
/// @note base class alwayys returns true.
/// Derived classes can signal operation in process by returning true
virtual bool hasCompleted();
/// call to execute after completion, can chain another operation by returning it
/// @note base class calls callback if one was set by setCompletionCallback(),
/// and then chains operation set by setChainedOperation();
virtual OperationPtr finalize();
/// abort operation
/// @param aError if set, abortion might be reported back by callbacks. If NULL, no callback will happen
/// @note base class calls callback if one was set by setCompletionCallback()
virtual void abortOperation(ErrorPtr aError);
/// @}
};
/// Operation queue
typedef boost::intrusive_ptr<OperationQueue> OperationQueuePtr;
class OperationQueue : public P44LoggingObj
{
MainLoop &mMainLoop;
bool mIsProcessingQueue; ///< set when queue is currently processing
MLTicket mRecheckTicket; ///< regular checking of the queue
MLMicroSeconds mLastInitiation; ///< time when last initiation was fired
protected:
typedef list<OperationPtr> OperationList;
OperationList mOperationQueue;
public:
/// create operation queue linked into specified mainloop
OperationQueue(MainLoop &aMainLoop = MainLoop::currentMainLoop());
/// destructor
virtual ~OperationQueue();
/// terminate
virtual void terminate();
/// queue a new operation
/// @param aOperation the operation to queue
void queueOperation(OperationPtr aOperation);
/// process immediately pending operations now
void processOperations();
/// abort all pending operations
/// @param aError if set, this will be passed on to each operation, which might cause them to execute call backs.
void abortOperations(ErrorPtr aError = ErrorPtr());
private:
/// periodic re-check of operation queue
void queueRecheck(MLTimer &aTimer);
/// process at most one operation
/// @return true if operations processed for now, i.e. no need to call again immediately
/// false if processOperations() should be called ASAP again (with no or little delay, if possible)
bool processOneOperation();
};
} // namespace p44
#endif /* defined(__p44utils__operationqueue__) */