Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
solve_interrupter.h
Go to the documentation of this file.
1// Copyright 2010-2024 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14#ifndef OR_TOOLS_UTIL_SOLVE_INTERRUPTER_H_
15#define OR_TOOLS_UTIL_SOLVE_INTERRUPTER_H_
16
17#include <atomic>
18#include <cstdint>
19#include <functional>
20#include <optional>
21
22#include "absl/base/thread_annotations.h"
23#include "absl/synchronization/mutex.h"
26
27namespace operations_research {
28
29// Interrupter used by solvers to know if/when they should interrupt the solve.
30//
31// Once triggered with Interrupt(), an interrupter can't be reset. It can be
32// triggered from any thread.
33//
34// Thread-safety: APIs on this class are safe to call concurrently from multiple
35// threads.
37 public:
38 // Id used to identify a callback.
39 DEFINE_STRONG_INT_TYPE(CallbackId, int64_t);
40
41 using Callback = std::function<void()>;
42
43 SolveInterrupter() = default;
44
47
48 // Interrupts the solve as soon as possible.
49 //
50 // Once requested the interruption can't be reset. The user should use a new
51 // SolveInterrupter for later solves.
52 //
53 // It is safe to call this function multiple times. Only the first call will
54 // have visible effects; other calls will be ignored.
55 void Interrupt();
56
57 // Returns true if the solve interruption has been requested.
58 //
59 // This API is fast; it costs the read of an atomic.
60 inline bool IsInterrupted() const { return interrupted_.load(); }
61
62 // Registers a callback to be called when the interruption is requested.
63 //
64 // The callback is immediately called if the interrupter has already been
65 // triggered or if it is triggered during the registration. This is typically
66 // useful for a solver implementation so that it does not have to test
67 // IsInterrupted() to do the same thing it does in the callback. Simply
68 // registering the callback is enough.
69 //
70 // The callback function can't make calls to AddInterruptionCallback(),
71 // RemoveInterruptionCallback() and Interrupt(). This would result is a
72 // deadlock. Calling IsInterrupted() is fine though.
73 //
74 // This method is `const` since it does not modify the state of the
75 // interrupter (the result of IsInterrupted()). This enables passing a
76 // const-ref to solvers, making sure they can't call Interrupt() by mistake.
78
79 // Unregisters a callback previously registered. It fails (with a CHECK) if
80 // the callback was already unregistered or unkonwn. After this calls returns,
81 // the caller can assume the callback won't be called.
82 //
83 // This function can't be called from a callback since this would result in a
84 // deadlock.
85 void RemoveInterruptionCallback(CallbackId id) const;
86
87 private:
88 // This atomic must never be reset to false!
89 //
90 // The mutex_ should be held when setting it to true.
91 std::atomic<bool> interrupted_ = false;
92
93 mutable absl::Mutex mutex_;
94
95 // The id to use for the next registered callback.
96 mutable CallbackId next_callback_id_ ABSL_GUARDED_BY(mutex_) = {};
97
98 // The list of callbacks. We use a linked_hash_map to make sure the order of
99 // calls to callback when the interrupter is triggered is stable.
101 ABSL_GUARDED_BY(mutex_);
102};
103
104// Class implementing RAII for interruption callbacks.
105//
106// Usage:
107//
108// const SolveInterrupter* const interrupter = ...;
109// {
110// const ScopedSolveInterrupterCallback scoped_intr_cb(interrupter, [](){
111// // Do something when/if interrupter is not nullptr and is triggered.
112// }
113// ...
114// }
115// // At this point, the callback will have been removed.
116//
117// The function RemoveCallbackIfNecessary() can be used to remove the callback
118// before the destruction of this object.
120 public:
121 // Adds a callback to the interrupter if it is not nullptr. Does nothing when
122 // interrupter is nullptr.
125
127 delete;
129 const ScopedSolveInterrupterCallback&) = delete;
130
131 // Removes the callback if necessary.
133
134 // Removes the callback from the interrupter. If it has already been removed
135 // by a previous call or if a null interrupter was passed to the constructor,
136 // this function has no effect.
138
139 // Returns the optional interrupter.
140 const SolveInterrupter* interrupter() const { return interrupter_; }
141
142 private:
143 // Optional interrupter.
144 const SolveInterrupter* const interrupter_;
145
146 // Unset after the callback has been reset.
147 std::optional<SolveInterrupter::CallbackId> callback_id_;
148};
149
150} // namespace operations_research
151
152#endif // OR_TOOLS_UTIL_SOLVE_INTERRUPTER_H_
~ScopedSolveInterrupterCallback()
Removes the callback if necessary.
const SolveInterrupter * interrupter() const
Returns the optional interrupter.
ScopedSolveInterrupterCallback(const ScopedSolveInterrupterCallback &)=delete
ScopedSolveInterrupterCallback(const SolveInterrupter *interrupter, SolveInterrupter::Callback callback)
ScopedSolveInterrupterCallback & operator=(const ScopedSolveInterrupterCallback &)=delete
void RemoveInterruptionCallback(CallbackId id) const
SolveInterrupter & operator=(const SolveInterrupter &)=delete
CallbackId AddInterruptionCallback(Callback callback) const
DEFINE_STRONG_INT_TYPE(CallbackId, int64_t)
Id used to identify a callback.
SolveInterrupter(const SolveInterrupter &)=delete
MPCallback * callback
In SWIG mode, we don't want anything besides these top-level includes.