OR-Tools  9.6
solve_interrupter.h
Go to the documentation of this file.
1 // Copyright 2010-2022 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 
29 namespace operations_research {
30 namespace 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 false!
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 
125  delete;
127  const ScopedSolveInterrupterCallback&) = delete;
128 
129  // Removes the callback if necessary.
131 
132  // Removes the callback from the interrupter. If it has already been removed
133  // by a previous call or if a null interrupter was passed to the constructor,
134  // this function has no effect.
136 
137  // Returns the optional interrupter.
138  SolveInterrupter* interrupter() const { return interrupter_; }
139 
140  private:
141  // Optional interrupter.
142  SolveInterrupter* const interrupter_;
143 
144  // Unset after the callback has been reset.
145  std::optional<SolveInterrupter::CallbackId> callback_id_;
146 };
147 
148 } // namespace math_opt
149 } // namespace operations_research
150 
151 #endif // OR_TOOLS_MATH_OPT_CORE_SOLVE_INTERRUPTER_H_
ScopedSolveInterrupterCallback(SolveInterrupter *interrupter, SolveInterrupter::Callback callback)
ScopedSolveInterrupterCallback(const ScopedSolveInterrupterCallback &)=delete
ScopedSolveInterrupterCallback & operator=(const ScopedSolveInterrupterCallback &)=delete
SolveInterrupter(const SolveInterrupter &)=delete
CallbackId AddInterruptionCallback(Callback callback)
SolveInterrupter & operator=(const SolveInterrupter &)=delete
MPCallback * callback
std::function< CallbackResult(const CallbackData &)> Callback
Definition: callback.h:93
Collection of objects used to extend the Constraint Solver library.