1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
//! Shared effect channel between async state machines and FFI bridges.
//!
//! An [`EffectSlot`] mediates the sans-IO effect protocol: async code
//! _requests_ services from the host (timestamps, logging, I/O) by
//! writing an effect, and the host _fulfills_ it by writing a response.
//!
//! ```text
//! Async code Host (via FFI bridge)
//! │ │
//! │ request(&effect) │
//! │──────────────────────────> │
//! │ Poll::Pending │
//! │ │
//! │ take_effect() │
//! │ <──────────────────────────│
//! │ Some(effect) │
//! │ │
//! │ fulfill(resp) │
//! │ <──────────────────────────│
//! │ │
//! │ request(&effect) │
//! │──────────────────────────> │
//! │ Poll::Ready(response) │
//! │ │
//! ```
//!
//! # Example
//!
//! ```rust
//! use core::task::Poll;
//! use future_form_ffi::effect_slot::EffectSlot;
//!
//! #[derive(Debug, Clone, PartialEq)]
//! enum Effect { GetTimestamp }
//!
//! #[derive(Debug, PartialEq)]
//! enum Response { Timestamp(u64) }
//!
//! let slot: EffectSlot<Effect, Response> = EffectSlot::new();
//!
//! // Async code requests an effect (returns Pending on first call).
//! assert_eq!(slot.request(&Effect::GetTimestamp), Poll::Pending);
//!
//! // Host reads the pending effect.
//! assert_eq!(slot.take_effect(), Some(Effect::GetTimestamp));
//!
//! // Host fulfills the effect.
//! slot.fulfill(Response::Timestamp(1234567890));
//!
//! // Async code retries — now gets the response.
//! assert_eq!(
//! slot.request(&Effect::GetTimestamp),
//! Poll::Ready(Response::Timestamp(1234567890)),
//! );
//! ```
use Poll;
use crateAtomicSlot;
/// Shared-state channel for the effect protocol between async code and
/// an FFI host.
///
/// `EffectSlot` is single-slot: one pending effect, one response. This
/// matches the [`Future::poll`] model naturally — one poll yields at most
/// one effect. If you need batching, compose multiple `EffectSlot`s or
/// build a queue on top.
///
/// # Slot ownership
///
/// Each `EffectSlot` is a single conversation channel. If multiple
/// futures share one slot, they will overwrite each other's pending
/// effects and receive responses meant for other futures. This is
/// memory-safe but semantically incorrect.
///
/// For **one future at a time**, embed the slot in the struct.
///
/// For **unlimited concurrent futures**, create a fresh
/// `Arc<EffectSlot>` per future, sharing it between the async closure
/// and the [`EffectHandle`](crate::effect_handle::EffectHandle).
/// The host stores handles however its concurrency model demands
/// (a map, inline in goroutines/threads, or a vec in an event loop).
/// See the [crate-level docs](crate#slot-ownership) for ownership
/// examples and [handle storage](crate#handle-storage) for host-side
/// patterns.
///
/// # Implementation
///
/// Thread safety is provided by [`AtomicSlot`], which uses lock-free
/// [`AtomicPtr`](core::sync::atomic::AtomicPtr) swaps internally.
/// This avoids mutex poisoning, spinlocks, global lock contention,
/// and external dependencies.
///
/// The per-value allocation cost is negligible in FFI contexts where
/// the future itself is already heap-allocated via [`Box::pin`](alloc::boxed::Box::pin).