Google OR-Tools v9.15
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-2025 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 ORTOOLS_UTIL_SOLVE_INTERRUPTER_H_
15#define ORTOOLS_UTIL_SOLVE_INTERRUPTER_H_
16
17#include <atomic>
18#include <cstdint>
19#include <optional>
20
21#include "absl/base/nullability.h"
22#include "absl/base/thread_annotations.h"
23#include "absl/functional/any_invocable.h"
24#include "absl/synchronization/mutex.h"
27
28namespace operations_research {
29
30// Interrupter used by solvers to know if/when they should interrupt the solve.
31//
32// Once triggered with Interrupt(), an interrupter can't be reset. It can be
33// triggered from any thread.
34//
35// Thread-safety: APIs on this class are safe to call concurrently from multiple
36// threads.
38 public:
39 // Id used to identify a callback.
40 DEFINE_STRONG_INT_TYPE(CallbackId, int64_t);
41
42 using Callback = absl::AnyInvocable<void() &&>;
43
44 SolveInterrupter() = default;
45
48
49 // Interrupts the solve as soon as possible.
50 //
51 // Once requested the interruption can't be reset. The user should use a new
52 // SolveInterrupter for later solves.
53 //
54 // It is safe to call this function multiple times. Only the first call will
55 // have visible effects; other calls will be ignored.
56 void Interrupt();
57
58 // Returns true if the solve interruption has been requested.
59 //
60 // This API is fast; it costs the read of an atomic.
61 inline bool IsInterrupted() const { return interrupted_.load(); }
62
63 // Registers a callback to be called when the interruption is requested.
64 //
65 // The callback is immediately called if the interrupter has already been
66 // triggered or if it is triggered during the registration. This is typically
67 // useful for a solver implementation so that it does not have to test
68 // IsInterrupted() to do the same thing it does in the callback. Simply
69 // registering the callback is enough.
70 //
71 // The callback function can't make calls to AddInterruptionCallback(),
72 // RemoveInterruptionCallback() and Interrupt(). This would result is a
73 // deadlock. Calling IsInterrupted() is fine though.
74 //
75 // This method is `const` since it does not modify the state of the
76 // interrupter (the result of IsInterrupted()). This enables passing a
77 // const-ref to solvers, making sure they can't call Interrupt() by mistake.
78 CallbackId AddInterruptionCallback(absl_nonnull Callback callback) const;
79
80 // Unregisters a callback previously registered. It fails (with a CHECK) if
81 // the callback was already unregistered or unkonwn. After this calls returns,
82 // the caller can assume the callback won't be called.
83 //
84 // This function can't be called from a callback since this would result in a
85 // deadlock.
86 void RemoveInterruptionCallback(CallbackId id) const;
87
88 private:
89 // This atomic must never be reset to false!
90 //
91 // The mutex_ should be held when setting it to true.
92 std::atomic<bool> interrupted_ = false;
93
94 mutable absl::Mutex mutex_;
95
96 // The id to use for the next registered callback.
97 mutable CallbackId next_callback_id_ ABSL_GUARDED_BY(mutex_) = {};
98
99 // The list of callbacks. We use a linked_hash_map to make sure the order of
100 // calls to callback when the interrupter is triggered is stable.
101 //
102 // The values are absl_nullable since the value will be nullptr:
103 // * either if AddInterruptionCallback() is called after Interrupt(), in which
104 // case the callback is called before AddInterruptionCallback() returns,
105 // * or after Interrupt() has been called.
106 //
107 // This reflects the fact that callback can only be called once.
109 ABSL_GUARDED_BY(mutex_);
110};
111
112// Class implementing RAII for interruption callbacks.
113//
114// Usage:
115//
116// const SolveInterrupter* const interrupter = ...;
117// {
118// const ScopedSolveInterrupterCallback scoped_intr_cb(interrupter, [](){
119// // Do something when/if interrupter is not nullptr and is triggered.
120// }
121// ...
122// }
123// // At this point, the callback will have been removed.
124//
125// The function RemoveCallbackIfNecessary() can be used to remove the callback
126// before the destruction of this object.
128 public:
129 // Adds a callback to the interrupter if it is not nullptr. Does nothing when
130 // interrupter is nullptr.
132 const SolveInterrupter* absl_nullable interrupter,
133 absl_nonnull SolveInterrupter::Callback callback);
134
136 delete;
138 const ScopedSolveInterrupterCallback&) = delete;
139
140 // Removes the callback if necessary.
142
143 // Removes the callback from the interrupter. If it has already been removed
144 // by a previous call or if a null interrupter was passed to the constructor,
145 // this function has no effect.
147
148 // Returns the optional interrupter.
149 const SolveInterrupter* absl_nullable interrupter() const {
150 return interrupter_;
151 }
152
153 private:
154 // Optional interrupter.
155 const SolveInterrupter* absl_nullable const interrupter_;
156
157 // Unset after the callback has been reset.
158 std::optional<SolveInterrupter::CallbackId> callback_id_;
159};
160
161} // namespace operations_research
162
163#endif // ORTOOLS_UTIL_SOLVE_INTERRUPTER_H_
const SolveInterrupter *absl_nullable interrupter() const
ScopedSolveInterrupterCallback(const ScopedSolveInterrupterCallback &)=delete
ScopedSolveInterrupterCallback(const SolveInterrupter *absl_nullable interrupter, absl_nonnull SolveInterrupter::Callback callback)
ScopedSolveInterrupterCallback & operator=(const ScopedSolveInterrupterCallback &)=delete
void RemoveInterruptionCallback(CallbackId id) const
absl::AnyInvocable< void() && > Callback
SolveInterrupter & operator=(const SolveInterrupter &)=delete
CallbackId AddInterruptionCallback(absl_nonnull Callback callback) const
DEFINE_STRONG_INT_TYPE(CallbackId, int64_t)
SolveInterrupter(const SolveInterrupter &)=delete
OR-Tools root namespace.