Google OR-Tools v9.15
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
solve_interrupter.cc
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
15
16#include <atomic>
17#include <optional>
18#include <utility>
19
20#include "absl/base/nullability.h"
21#include "absl/functional/any_invocable.h"
22#include "absl/synchronization/mutex.h"
25
26namespace operations_research {
27
29 const absl::MutexLock lock(mutex_);
30
31 // Here we don't use compare_exchange_strong since we need to hold the lock
32 // before changing the value of interrupted_ anyway. So there is no need to
33 // use this complex function.
34 if (interrupted_.load()) {
35 // We must not call the callbacks more than once.
36 return;
37 }
38
39 // We need to change this value while holding the lock since in
40 // AddInterruptionCallback() we must know if we need to call the new callback
41 // of if this function has called it.
42 interrupted_ = true;
43
44 // We are holding the lock while calling callbacks. This makes it impossible
45 // to call Interrupt(), AddInterruptionCallback(), or
46 // RemoveInterruptionCallback() from a callback but it ensures that external
47 // code that can modify callbacks_ will wait until the end of Interrupt.
48 for (auto& [callback_id, callback] : callbacks_) {
49 // We can only have nullptr if:
50 // * interrupted_ was true when AddInterruptionCallback() was called,
51 // * or a previous Interrupt() has set the pointer to null.
52 // In these two cases we should not reach this code.
53 CHECK(callback != nullptr);
54 std::move(callback)();
55 callback = nullptr;
56 }
57}
58
59SolveInterrupter::CallbackId SolveInterrupter::AddInterruptionCallback(
60 absl_nonnull Callback callback) const {
61 const absl::MutexLock lock(mutex_);
62
63 const CallbackId id = next_callback_id_;
64 ++next_callback_id_;
65
66 const auto [it, inserted] = callbacks_.try_emplace(id, std::move(callback));
67 CHECK(inserted);
68
69 // We must make this call while holding the lock since we want to be sure that
70 // the calls to the callbacks_ won't occur before we registered the new
71 // one. If we were not holding the lock, this could return false and before we
72 // could add the new callback to callbacks_, the Interrupt() function may
73 // still have called them.
74 if (interrupted_.load()) {
75 std::move(it->second)();
76 it->second = nullptr;
77 }
78
79 return id;
80}
81
83 const absl::MutexLock lock(mutex_);
84 CHECK_EQ(callbacks_.erase(id), 1) << "unregistered callback id: " << id;
85}
86
88 const SolveInterrupter* absl_nullable const interrupter,
89 absl_nonnull SolveInterrupter::Callback callback)
90 : interrupter_(interrupter),
91 callback_id_(
92 interrupter != nullptr
93 ? std::make_optional(
94 interrupter->AddInterruptionCallback(std::move(callback)))
95 : std::nullopt) {}
96
100
102 if (callback_id_) {
103 CHECK_NE(interrupter_, nullptr);
104 interrupter_->RemoveInterruptionCallback(*callback_id_);
105 callback_id_.reset();
106 }
107}
108
109} // namespace operations_research
const SolveInterrupter *absl_nullable interrupter() const
ScopedSolveInterrupterCallback(const SolveInterrupter *absl_nullable interrupter, absl_nonnull SolveInterrupter::Callback callback)
void RemoveInterruptionCallback(CallbackId id) const
absl::AnyInvocable< void() && > Callback
CallbackId AddInterruptionCallback(absl_nonnull Callback callback) const
OR-Tools root namespace.
STL namespace.