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
//! The manager trait: how the pool creates, checks and moves connections.
use Future;
use crateSlotMeta;
/// Creates and validates connections for a [`Pool`](crate::Pool).
///
/// This is the trait you implement for your protocol (Postgres, Redis, an HTTP
/// client, …).
///
/// # Why there are no `Send` bounds on the futures
///
/// `compio` is a completion-based, thread-per-core runtime: its IO handles are
/// bound to the driver of the thread that created them and are `!Send`. A pool
/// built on `bb8`'s trait shape cannot hold a `compio::net::TcpStream` at all,
/// because `bb8` requires `Connection: Send`.
///
/// So `Manage` requires `Send + Sync` on the **manager** (it is shared across
/// threads inside the pool handle) but places no bounds whatsoever on
/// [`Connection`](Manage::Connection) or on the returned futures. Connections
/// live in thread-local shards and never cross a thread boundary unless you
/// additionally implement [`Detach`].
/// Opt-in marker for connections that can legally change threads.
///
/// Implementing this lets a connection be parked in the cross-thread
/// [`Reservoir`](crate::Reservoir), so an idle connection on one compio thread
/// can be picked up by a busy one instead of sitting unused.
///
/// # Only idle connections, never one with an operation in flight
///
/// Moving a handle between drivers is sound only when nothing is still
/// submitted against it. The pool upholds half of that for you: a connection
/// reaches [`detach`](Detach::detach) only after its [`Pooled`](crate::Pooled)
/// guard has been dropped, and a checkout cancelled mid-operation is poisoned
/// and destroyed rather than parked. [`detach`](Detach::detach) returning
/// `Option` is the other half — the implementation gets to *check*, rather
/// than trust, that the handle is genuinely unshared.
///
/// # Platform reality
///
/// Whether this is sound depends on the driver, and the answer differs by OS:
///
/// * **io_uring and poll (Unix)** — `Driver::attach` is a no-op. The fd table
/// is process-wide and any thread may submit ops on any fd, so moving a
/// connection between compio threads costs nothing. `Detach` is safe here.
/// * **IOCP (Windows)** — `attach` calls `CreateIoCompletionPort`, and compio
/// documents that "a handle can and only can attach once to one driver".
/// A socket bound to thread A's port keeps delivering completions to thread
/// A's port forever. **Do not implement `Detach` on Windows** for types
/// backed by an IOCP handle.
///
/// Beyond the fd itself, everything you carry in [`Parked`](Detach::Parked)
/// must be `Send`: buffers, TLS session state, prepared-statement caches. A
/// connection whose state includes an `Rc` cannot implement this trait, which
/// is precisely the point of separating it from [`Manage`].