Skip to content

Commit 6845f4a

Browse files
userFRMclaude
andcommitted
fix(v9.1.0): non-blocking prev_drained_is_set + truthful flatfiles doc
- `ThetaDataDx::prev_drained_is_set()` now uses `try_lock()` instead of `lock()`. Returning `true` on contention is the institutionally safe choice: the FFI `_free` path uses this signal to decide whether to wait on the drain barrier, and "wait" is the correct fail-safe when a stop is actively in flight. The contention window is microseconds (the lock is held only across the `Option<Arc<AtomicBool>>` swap), so the false-positive cost is negligible. Poison is recovered the same way the previous `lock()` path did, so behaviour on a poisoned mutex is unchanged. - `flatfile_request_raw` rustdoc no longer claims the INDEX walker and FIT decoder are tracked as TODOs — both shipped in the flat-files wave and live behind `flatfile_request_decoded`. The raw-bytes path documents that it returns the unparsed vendor format for callers that want to keep the on-disk layout. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 80a8fb1 commit 6845f4a

2 files changed

Lines changed: 24 additions & 7 deletions

File tree

crates/thetadatadx/src/client.rs

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -470,12 +470,28 @@ impl ThetaDataDx {
470470
/// FFI free paths use this to disambiguate the two `false` returns
471471
/// from `await_drain` (timeout vs. nothing-to-wait-on); only the
472472
/// former is a real concern worth surfacing in operator logs.
473+
///
474+
/// **Non-blocking.** Uses `try_lock` on the internal slot mutex.
475+
/// If the mutex is contended (another thread is mid-`stop_streaming`
476+
/// or `reconnect_streaming` swapping the slot), this returns `true`
477+
/// — the institutionally-safe answer, because the FFI `_free` path
478+
/// uses this signal to decide whether to wait on the drain barrier,
479+
/// and "wait" is the correct fail-safe when a stop is actively in
480+
/// flight. The contention window is microseconds (the lock is held
481+
/// only across the `Option<Arc<AtomicBool>>` swap), so the false-
482+
/// positive cost is negligible.
473483
#[must_use]
474484
pub fn prev_drained_is_set(&self) -> bool {
475-
self.prev_drained
476-
.lock()
477-
.unwrap_or_else(std::sync::PoisonError::into_inner)
478-
.is_some()
485+
match self.prev_drained.try_lock() {
486+
Ok(guard) => guard.is_some(),
487+
Err(std::sync::TryLockError::Poisoned(p)) => p.into_inner().is_some(),
488+
Err(std::sync::TryLockError::WouldBlock) => {
489+
// Mutex contended — another thread is mid-stop. Treat
490+
// as "drain in progress" (true) so the FFI free path
491+
// waits for the drain barrier rather than skipping it.
492+
true
493+
}
494+
}
479495
}
480496

481497
#[must_use]

crates/thetadatadx/src/flatfiles/request.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,9 +91,10 @@ fn validate_date(date: &str) -> Result<(), Error> {
9191
/// **Output format**: a raw concatenation of every FLAT_FILE chunk
9292
/// payload, in receive order, **without** the framing headers. This is the
9393
/// same byte sequence the vendor jar accumulates internally before walking
94-
/// the index — to convert it to CSV one must implement the INDEX walker
95-
/// and per-data-type FIT decoder. Both are tracked as TODOs in
96-
/// [`crate::flatfiles`].
94+
/// the index. The INDEX walker and per-`(SecType, ReqType)` FIT decoder
95+
/// are exposed via [`crate::flatfiles::flatfile_request_decoded`];
96+
/// this function returns the raw bytes for callers that want to keep the
97+
/// on-disk vendor format unchanged.
9798
pub async fn flatfile_request_raw(
9899
creds: &Credentials,
99100
sec: SecType,

0 commit comments

Comments
 (0)