Files
ortools-clone/ortools/util/python/py_solve_interrupter.h
2025-08-20 11:36:43 +02:00

117 lines
4.1 KiB
C++

// Copyright 2010-2025 Google LLC
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef OR_TOOLS_UTIL_PYTHON_PY_SOLVE_INTERRUPTER_H_
#define OR_TOOLS_UTIL_PYTHON_PY_SOLVE_INTERRUPTER_H_
#include <memory>
#include <vector>
#include "absl/base/nullability.h"
#include "absl/base/thread_annotations.h"
#include "absl/synchronization/mutex.h"
#include "ortools/util/solve_interrupter.h"
namespace operations_research {
// Simple class that wraps a SolveInterrupter for Clif or pybind11.
//
class PySolveInterrupter {
public:
PySolveInterrupter();
PySolveInterrupter(const PySolveInterrupter&) = delete;
PySolveInterrupter& operator=(const PySolveInterrupter&) = delete;
// Interrupts the solve as soon as possible.
void Interrupt() { interrupter_.Interrupt(); }
// Returns true if the solve interruption has been requested.
inline bool IsInterrupted() const { return interrupter_.IsInterrupted(); }
// Triggers the target when this interrupter is triggered.
//
// A std::weak_ptr is kept on the target. Expired std::weak_ptr are cleaned up
// on calls to AddTriggerTarget() and RemoveTriggerTarget().
//
// Complexity: O(num_targets).
void AddTriggerTarget(absl_nonnull std::shared_ptr<PySolveInterrupter> target)
ABSL_LOCKS_EXCLUDED(mutex_);
// Removes the target if not null and present, else do nothing.
//
// Complexity: O(num_targets).
void RemoveTriggerTarget(
absl_nonnull std::shared_ptr<PySolveInterrupter> target)
ABSL_LOCKS_EXCLUDED(mutex_);
// Add a callback on the interrupter and returns an id to use to remove it.
//
// See SolveInterrupter::AddInterruptionCallback().
int64_t AddInterruptionCallback(std::function<void()> callback) const {
return interrupter_.AddInterruptionCallback(std::move(callback)).value();
}
// Remove a callback previously registered by AddInterruptionCallback().
//
// See SolveInterrupter::RemoveInterruptionCallback().
void RemoveInterruptionCallback(int64_t callback_id) const {
interrupter_.RemoveInterruptionCallback(
SolveInterrupter::CallbackId{callback_id});
}
// Return the underlying interrupter. This method is not exposed in Python and
// only available for C++ code.
//
// The lifetime of the PySolveInterrupter is controlled by Python. To prevent
// issues where the underlying SolveInterrupter would be destroyed while still
// being pointed to by C++ code, C++ Clif/pybind11 consumer code should take
// references to PySolveInterrupter in an std::shared_ptr that outlives any
// reference.
const SolveInterrupter* absl_nonnull interrupter() const {
return &interrupter_;
}
private:
// Remove expired targets_, the optional to_remove target and return strong
// references on non-expired targets.
//
// The caller must have acquired mutex_.
//
// Complexity: O(num_targets).
std::vector<absl_nonnull std::shared_ptr<PySolveInterrupter>>
CleanupAndGetTargets(const PySolveInterrupter* absl_nullable to_remove =
nullptr) ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Triggers all non-expired targets_ interrupters.
void TriggerTargets() ABSL_LOCKS_EXCLUDED(mutex_);
SolveInterrupter interrupter_;
absl::Mutex mutex_;
// Interrupters to trigger when interrupter_ is triggered.
std::vector<std::weak_ptr<PySolveInterrupter>> targets_
ABSL_GUARDED_BY(mutex_);
// Callback that will trigger all interrupters in target_.
//
// It MUST appear after targets_ and interrupter_ as we want to make sure it
// is destroyed first!
ScopedSolveInterrupterCallback callback_;
};
} // namespace operations_research
#endif // OR_TOOLS_UTIL_PYTHON_PY_SOLVE_INTERRUPTER_H_