OR-Tools  9.3
solve_interrupter.h
Go to the documentation of this file.
1// Copyright 2010-2021 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_MATH_OPT_CORE_SOLVE_INTERRUPTER_H_
15#define OR_TOOLS_MATH_OPT_CORE_SOLVE_INTERRUPTER_H_
16
17#include <atomic>
18#include <cstdint>
19#include <functional>
20#include <memory>
21#include <optional>
22
23#include "absl/base/thread_annotations.h"
24#include "absl/strings/string_view.h"
25#include "absl/synchronization/mutex.h"
28
29namespace operations_research {
30namespace math_opt {
31
32// Interrupter used by solvers to know if/when they should interrupt the solve.
33//
34// Once triggered with Interrupt(), an interrupter can't be reset. It can be
35// triggered from any thread.
36//
37// Thread-safety: APIs on this class are safe to call concurrently from multiple
38// threads.
40 public:
41 // Id used to identify a callback.
42 DEFINE_STRONG_INT_TYPE(CallbackId, int64_t);
43
44 using Callback = std::function<void()>;
45
46 SolveInterrupter() = default;
47
50
51 // Interrupts the solve as soon as possible.
52 //
53 // Once requested the interruption can't be reset. The user should use a new
54 // SolveInterrupter for later solves.
55 //
56 // It is safe to call this function multiple times. Only the first call will
57 // have visible effects; other calls will be ignored.
58 void Interrupt();
59
60 // Returns true if the solve interruption has been requested.
61 //
62 // This API is fast; it costs the read of an atomic.
63 inline bool IsInterrupted() const { return interrupted_.load(); }
64
65 // Registers a callback to be called when the interruption is requested.
66 //
67 // The callback is immediately called if the interrupter has already been
68 // triggered or if it is triggered during the registration. This is typically
69 // useful for a solver implementation so that it does not have to test
70 // IsInterrupted() to do the same thing it does in the callback. Simply
71 // registering the callback is enough.
72 //
73 // The callback function can't make calls to AddInterruptionCallback(),
74 // RemoveInterruptionCallback() and Interrupt(). This would result is a
75 // deadlock. Calling IsInterrupted() is fine though.
77
78 // Unregisters a callback previously registered. It fails (with a CHECK) if
79 // the callback was already unregistered or unkonwn. After this calls returns,
80 // the caller can assume the callback won't be called.
81 //
82 // This function can't be called from a callback since this would result in a
83 // deadlock.
84 void RemoveInterruptionCallback(CallbackId id);
85
86 private:
87 // This atomic must never be reset to true!
88 //
89 // The mutex_ should be held when setting it to true.
90 std::atomic<bool> interrupted_ = false;
91
92 absl::Mutex mutex_;
93
94 // The id to use for the next registered callback.
95 CallbackId next_callback_id_ ABSL_GUARDED_BY(mutex_) = {};
96
97 // The list of callbacks. We use a linked_hash_map to make sure the order of
98 // calls to callback when the interrupter is triggered is stable.
99 gtl::linked_hash_map<CallbackId, Callback> callbacks_ ABSL_GUARDED_BY(mutex_);
100};
101
102// Class implementing RAII for interruption callbacks.
103//
104// Usage:
105//
106// SolveInterrupter* const interrupter = ...;
107// {
108// const ScopedSolveInterrupterCallback scoped_intr_cb(interrupter, [](){
109// // Do something when/if interrupter is not nullptr and is triggered.
110// }
111// ...
112// }
113// // At this point, the callback will have been removed.
114//
115// The function RemoveCallbackIfNecessary() can be used to remove the callback
116// before the destruction of this object.
118 public:
119 // Adds a callback to the interrupter if it is not nullptr. Does nothing when
120 // interrupter is nullptr.
123
124 // Removes the callback if necessary.
126
127 // Removes the callback from the interrupter. If it has already been removed
128 // by a previous call or if a null interrupter was passed to the constructor,
129 // this function has no effect.
131
132 private:
133 // Optional interrupter.
134 SolveInterrupter* const interrupter_;
135
136 // Unset after the callback has been reset.
137 std::optional<SolveInterrupter::CallbackId> callback_id_;
138};
139
140} // namespace math_opt
141} // namespace operations_research
142
143#endif // OR_TOOLS_MATH_OPT_CORE_SOLVE_INTERRUPTER_H_
ScopedSolveInterrupterCallback(SolveInterrupter *interrupter, SolveInterrupter::Callback callback)
SolveInterrupter & operator=(const SolveInterrupter &)=delete
SolveInterrupter(const SolveInterrupter &)=delete
CallbackId AddInterruptionCallback(Callback callback)
MPCallback * callback
std::function< CallbackResult(const CallbackData &)> Callback
Definition: callback.h:89
Collection of objects used to extend the Constraint Solver library.