OR-Tools  9.2
solve_interrupter.cc
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
15
16#include <atomic>
17#include <functional>
18#include <memory>
19#include <optional>
20#include <utility>
21
23#include "absl/synchronization/mutex.h"
26
27namespace operations_research {
28namespace 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
55SolveInterrupter::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
#define CHECK(condition)
Definition: base/logging.h:495
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:702
#define CHECK_NE(val1, val2)
Definition: base/logging.h:703
ScopedSolveInterrupterCallback(SolveInterrupter *interrupter, SolveInterrupter::Callback callback)
CallbackId AddInterruptionCallback(Callback callback)
MPCallback * callback
Collection of objects used to extend the Constraint Solver library.
STL namespace.