OR-Tools  9.6
solve_interrupter.cc
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 
15 
16 #include <atomic>
17 #include <functional>
18 #include <memory>
19 #include <optional>
20 #include <utility>
21 
22 #include "absl/synchronization/mutex.h"
24 #include "ortools/base/logging.h"
26 
27 namespace operations_research {
28 namespace math_opt {
29 
31  const absl::MutexLock lock(&mutex_);
32 
33  // Here we don't use compare_exchange_strong since we need to hold the lock
34  // before changing the value of interrupted_ anyway. So there is no need to
35  // use this complex function.
36  if (interrupted_.load()) {
37  // We must not call the callbacks more than once.
38  return;
39  }
40 
41  // We need to change this value while holding the lock since in
42  // AddInterruptionCallback() we must know if we need to call the new callback
43  // of if this function has called it.
44  interrupted_ = true;
45 
46  // We are holding the lock while calling callbacks. This make it impossible to
47  // call Interrupt(), AddInterruptionCallback(), or
48  // RemoveInterruptionCallback() from a callback but it ensures that external
49  // code that can modify callbacks_ will wait the end of Interrupt.
50  for (const auto& [callback_id, callback] : callbacks_) {
51  callback();
52  }
53 }
54 
55 SolveInterrupter::CallbackId SolveInterrupter::AddInterruptionCallback(
57  const absl::MutexLock lock(&mutex_);
58 
59  // We must make this call while holding the lock since we want to be sure that
60  // the calls to the callbacks_ won't occur before we registered the new
61  // one. If we were not holding the lock, this could return false and before we
62  // could add the new callback to callbacks_, the Interrupt() function may
63  // still have called them.
64  //
65  // We make the call before putting the callback in the map to since we need to
66  // move it in place.
67  if (interrupted_.load()) {
68  callback();
69  }
70 
71  const CallbackId id = next_callback_id_;
72  ++next_callback_id_;
73  CHECK(callbacks_.try_emplace(id, std::move(callback)).second);
74  return id;
75 }
76 
78  const absl::MutexLock lock(&mutex_);
79  CHECK_EQ(callbacks_.erase(id), 1) << "unregistered callback id: " << id;
80 }
81 
84  : interrupter_(interrupter),
85  callback_id_(
86  interrupter != nullptr
87  ? std::make_optional(
88  interrupter->AddInterruptionCallback(std::move(callback)))
89  : std::nullopt) {}
90 
93 }
94 
96  if (callback_id_) {
97  CHECK_NE(interrupter_, nullptr);
98  interrupter_->RemoveInterruptionCallback(*callback_id_);
99  callback_id_.reset();
100  }
101 }
102 
103 } // namespace math_opt
104 } // namespace operations_research
ScopedSolveInterrupterCallback(SolveInterrupter *interrupter, SolveInterrupter::Callback callback)
CallbackId AddInterruptionCallback(Callback callback)
MPCallback * callback
Collection of objects used to extend the Constraint Solver library.