-
Notifications
You must be signed in to change notification settings - Fork 2.1k
/
solve_interrupter.h
152 lines (127 loc) · 5.49 KB
/
solve_interrupter.h
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
// Copyright 2010-2024 Google LLC
// 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.
#ifndef OR_TOOLS_UTIL_SOLVE_INTERRUPTER_H_
#define OR_TOOLS_UTIL_SOLVE_INTERRUPTER_H_
#include <atomic>
#include <cstdint>
#include <functional>
#include <optional>
#include "absl/base/thread_annotations.h"
#include "absl/synchronization/mutex.h"
#include "ortools/base/linked_hash_map.h"
#include "ortools/base/strong_int.h"
namespace operations_research {
// Interrupter used by solvers to know if/when they should interrupt the solve.
//
// Once triggered with Interrupt(), an interrupter can't be reset. It can be
// triggered from any thread.
//
// Thread-safety: APIs on this class are safe to call concurrently from multiple
// threads.
class SolveInterrupter {
public:
// Id used to identify a callback.
DEFINE_STRONG_INT_TYPE(CallbackId, int64_t);
using Callback = std::function<void()>;
SolveInterrupter() = default;
SolveInterrupter(const SolveInterrupter&) = delete;
SolveInterrupter& operator=(const SolveInterrupter&) = delete;
// Interrupts the solve as soon as possible.
//
// Once requested the interruption can't be reset. The user should use a new
// SolveInterrupter for later solves.
//
// It is safe to call this function multiple times. Only the first call will
// have visible effects; other calls will be ignored.
void Interrupt();
// Returns true if the solve interruption has been requested.
//
// This API is fast; it costs the read of an atomic.
inline bool IsInterrupted() const { return interrupted_.load(); }
// Registers a callback to be called when the interruption is requested.
//
// The callback is immediately called if the interrupter has already been
// triggered or if it is triggered during the registration. This is typically
// useful for a solver implementation so that it does not have to test
// IsInterrupted() to do the same thing it does in the callback. Simply
// registering the callback is enough.
//
// The callback function can't make calls to AddInterruptionCallback(),
// RemoveInterruptionCallback() and Interrupt(). This would result is a
// deadlock. Calling IsInterrupted() is fine though.
//
// This method is `const` since it does not modify the state of the
// interrupter (the result of IsInterrupted()). This enables passing a
// const-ref to solvers, making sure they can't call Interrupt() by mistake.
CallbackId AddInterruptionCallback(Callback callback) const;
// Unregisters a callback previously registered. It fails (with a CHECK) if
// the callback was already unregistered or unkonwn. After this calls returns,
// the caller can assume the callback won't be called.
//
// This function can't be called from a callback since this would result in a
// deadlock.
void RemoveInterruptionCallback(CallbackId id) const;
private:
// This atomic must never be reset to false!
//
// The mutex_ should be held when setting it to true.
std::atomic<bool> interrupted_ = false;
mutable absl::Mutex mutex_;
// The id to use for the next registered callback.
mutable CallbackId next_callback_id_ ABSL_GUARDED_BY(mutex_) = {};
// The list of callbacks. We use a linked_hash_map to make sure the order of
// calls to callback when the interrupter is triggered is stable.
mutable gtl::linked_hash_map<CallbackId, Callback> callbacks_
ABSL_GUARDED_BY(mutex_);
};
// Class implementing RAII for interruption callbacks.
//
// Usage:
//
// const SolveInterrupter* const interrupter = ...;
// {
// const ScopedSolveInterrupterCallback scoped_intr_cb(interrupter, [](){
// // Do something when/if interrupter is not nullptr and is triggered.
// }
// ...
// }
// // At this point, the callback will have been removed.
//
// The function RemoveCallbackIfNecessary() can be used to remove the callback
// before the destruction of this object.
class ScopedSolveInterrupterCallback {
public:
// Adds a callback to the interrupter if it is not nullptr. Does nothing when
// interrupter is nullptr.
ScopedSolveInterrupterCallback(const SolveInterrupter* interrupter,
SolveInterrupter::Callback callback);
ScopedSolveInterrupterCallback(const ScopedSolveInterrupterCallback&) =
delete;
ScopedSolveInterrupterCallback& operator=(
const ScopedSolveInterrupterCallback&) = delete;
// Removes the callback if necessary.
~ScopedSolveInterrupterCallback();
// Removes the callback from the interrupter. If it has already been removed
// by a previous call or if a null interrupter was passed to the constructor,
// this function has no effect.
void RemoveCallbackIfNecessary();
// Returns the optional interrupter.
const SolveInterrupter* interrupter() const { return interrupter_; }
private:
// Optional interrupter.
const SolveInterrupter* const interrupter_;
// Unset after the callback has been reset.
std::optional<SolveInterrupter::CallbackId> callback_id_;
};
} // namespace operations_research
#endif // OR_TOOLS_UTIL_SOLVE_INTERRUPTER_H_