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
//! Confining the calling thread to one logical processor.
//!
//! Unlike the best-effort node binding a consumer may layer on top, this
//! primitive reports every failure as a typed [`BindError`], so a scheduler can
//! refuse to start a worker whose binding did not take effect.
use fmt;
/// Why the calling thread could not be confined to a logical processor.
///
/// The variants partition the failure by who can act on it: the target has no
/// backend ([`Self::Unsupported`]), the processor id is not addressable
/// ([`Self::OutOfRange`]), or the operating system refused the request
/// ([`Self::Os`]). In every case the thread keeps its previous affinity.
/// Confines the calling thread to exactly one logical processor.
///
/// `processor` uses the flattened numbering of [`crate::CpuTopology`]: on
/// Windows `group * 64 + bit`, on Linux the kernel's cpu number. The binding
/// lasts for the thread's life. There is no guard and no restore, because a
/// worker thread pins once at startup; a second call rebinds the thread.
///
/// The call succeeds only when the operating system accepted the new
/// affinity, so a caller that publishes a worker-to-processor assignment after
/// `Ok(())` publishes a fact, and a caller that receives `Err` may refuse to
/// start the worker.
///
/// # Errors
///
/// - [`BindError::Unsupported`] when the target has no backend.
/// - [`BindError::OutOfRange`] when the processor id cannot be named by the
/// target's affinity interface: a Windows processor group beyond `u16`, or a
/// Linux id at or past the crate's processor-id bound of 32 768.
/// - [`BindError::Os`] with the raw error code when the operating system
/// refuses, for example `EINVAL` on Linux for a processor that is offline or
/// outside the process's cpuset.
///
/// # Examples
///
/// A processor id no target can name is refused, and the calling thread is
/// left untouched:
///
/// ```
/// use themis::{bind_current_thread, BindError};
///
/// assert!(matches!(
/// bind_current_thread(u32::MAX),
/// Err(BindError::OutOfRange { processor: u32::MAX } | BindError::Unsupported)
/// ));
/// ```
///
/// A worker pins itself once at startup and refuses to run unbound. Which
/// processors the host permits is environmental, so the example is not run:
///
/// ```no_run
/// use themis::bind_current_thread;
///
/// let worker = std::thread::spawn(|| bind_current_thread(0));
/// worker.join().expect("worker panicked")?;
/// # Ok::<(), themis::BindError>(())
/// ```
/// Reads the error the failed call just left in the thread's error slot.
///
/// Must run immediately after the failing call, before any other system call
/// on the thread can overwrite it.