Skip to main content

pounce_algorithm/
intermediate.rs

1//! Per-iteration "intermediate" context shared with downstream
2//! callers (notably the C-API inspector functions
3//! `GetIpoptCurrentIterate` / `GetIpoptCurrentViolations`).
4//!
5//! Mirrors upstream Ipopt's `OrigIpoptNLP::GetIpoptCurrent*` flow: the
6//! main loop installs a snapshot of the algorithm-side state into
7//! thread-local storage immediately before invoking the user's
8//! intermediate callback, and clears it on return. Inspector functions
9//! consult the TLS slot; outside the callback window every accessor
10//! reports "not available".
11//!
12//! The snapshot is intentionally cheap to assemble — we stash `Rc`
13//! handles to `IpoptData`, `IpoptCq`, and the algorithm-side `IpoptNlp`
14//! rather than precomputing every field, so callers that read just one
15//! quantity pay only for what they look at.
16
17use crate::ipopt_cq::IpoptCqHandle;
18use crate::ipopt_data::IpoptDataHandle;
19use crate::ipopt_nlp::IpoptNlp;
20use std::cell::RefCell;
21use std::rc::Rc;
22
23/// Snapshot stashed in TLS for the duration of one
24/// `TNLP::intermediate_callback` invocation.
25#[derive(Clone)]
26pub struct IntermediateContext {
27    pub data: IpoptDataHandle,
28    pub cq: IpoptCqHandle,
29    pub nlp: Rc<RefCell<dyn IpoptNlp>>,
30}
31
32thread_local! {
33    static CURRENT_CTX: RefCell<Option<IntermediateContext>> = const { RefCell::new(None) };
34}
35
36/// RAII guard — installs `ctx` on construction, clears on drop. Used
37/// by the algorithm to scope visibility of live iterate state to one
38/// callback fire.
39pub struct CtxGuard {
40    _private: (),
41}
42
43impl CtxGuard {
44    pub fn install(ctx: IntermediateContext) -> Self {
45        CURRENT_CTX.with(|c| *c.borrow_mut() = Some(ctx));
46        Self { _private: () }
47    }
48}
49
50impl Drop for CtxGuard {
51    fn drop(&mut self) {
52        CURRENT_CTX.with(|c| *c.borrow_mut() = None);
53    }
54}
55
56/// Read access to the currently installed context. Returns `None`
57/// outside the intermediate-callback window.
58pub fn with_current<F, R>(f: F) -> Option<R>
59where
60    F: FnOnce(&IntermediateContext) -> R,
61{
62    CURRENT_CTX.with(|c| c.borrow().as_ref().map(f))
63}
64
65/// Whether a context is currently installed.
66pub fn is_active() -> bool {
67    CURRENT_CTX.with(|c| c.borrow().is_some())
68}