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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
// This file is part of cpu-affinity. It is subject to the license terms in the COPYRIGHT file found in the top-level directory of this distribution and at https://raw.githubusercontent.com/lemonrock/cpu-affinity/master/COPYRIGHT. No part of cpu-affinity, including this file, may be copied, modified, propagated, or distributed except according to the terms contained in the COPYRIGHT file.
// Copyright © 2018 The developers of cpu-affinity. See the COPYRIGHT file in the top-level directory of this distribution and at https://raw.githubusercontent.com/lemonrock/cpu-affinity/master/COPYRIGHT.


/// Logical cores to use for process and thread affinity, abstracting away platform, library and Operating System differences.
///
/// Provides the following definitions:-
///
/// * Constant `LogicalCores::IsSettingProcessAffinitySupported`: Boolean that is true if the current platform contains a way to attempt to set process affinity.
/// * Constant `LogicalCores::IsSettingThreadAffinitySupported`: Boolean that is true if the current platform contains a way to attempt to set thread affinity. Note that this is true for Fuschia and Empscripten, but that setting thread affinity always fails as unsupported as of 3rd December 2018.
///
/// * Method `set_current_process_affinity(&self) -> io::Result<()>`: Tries to set the current process' affinity; may fail (eg may not be supported in particular revisions of an OS, particularly so on Fuschia and Emscripten).
/// * Method `set_process_affinity(&self, process_identifier) -> io::Result<()>`: As above, but for a particular process. Usually fails with permission denied if not root.
/// * Method `set_current_thread_affinity(&self) -> io::Result<()>`: Tries to set the thread process' affinity; may fail (eg may not be supported in particular revisions of an OS, particularly so on Fuschia and Emscripten).
/// * Method `set_thread_affinity(&self, thread_identifier) -> io::Result<()>`: As above, but for a thread process. Usually fails with permission denied if not root.
///
/// If support for a platform has not been explicitly added to this library then it will not fail on that platform but it will not change any process or thread affinities, either.
///
/// At this time, there is no support for Solaris.
/// If you're interested in adding support, see <https://stackoverflow.com/questions/14085515/obtain-lwp-id-from-a-pthread-t-on-solaris-to-use-with-processor-bind>.
///
/// DragonFlyBSD and FreeBSD currently (as of 3rd December 2018) support 256 cores.
///
/// Windows is a bit squiffy with more than 64 cores.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LogicalCores(HashSet<usize>);

impl From<usize> for LogicalCores
{
	#[inline(always)]
	fn from(core_index: usize) -> Self
	{
		let mut logical_cores = HashSet::with_capacity(1);
		logical_cores.insert(core_index);
		LogicalCores(logical_cores)
	}
}

impl From<HashSet<usize>> for LogicalCores
{
	#[inline(always)]
	fn from(logical_cores: HashSet<usize>) -> Self
	{
		debug_assert_ne!(logical_cores.len(), 0, "There must be at least one logical core specified");

		LogicalCores(logical_cores)
	}
}

impl Into<HashSet<usize>> for LogicalCores
{
	#[inline(always)]
	fn into(self) -> HashSet<usize>
	{
		self.0
	}
}

impl LogicalCores
{
	/// Is setting process affinity is supported?
	///
	/// Note that on emscripten and fuschia an error (`ENOSYS`) by the platform will always be returned as of the 3rd December 2018.
	///
	/// The following do not support setting process affinity:-
	///
	/// * iOS
	/// * Mac OS
	/// * OpenBSD
	/// * BitRig
	pub const IsSettingProcessAffinitySupported: bool = Self::_IsSettingProcessAffinitySupported;

	/// Is setting thread affinity is supported?
	///
	/// Note that on emscripten and fuschia an error (`ENOSYS`) by the platform will always be returned as of the 3rd December 2018.
	///
	/// The following do not support setting thread affinity:-
	///
	/// * Android
	/// * iOS
	/// * Mac OS
	/// * OpenBSD
	/// * BitRig
	pub const IsSettingThreadAffinitySupported: bool = Self::_IsSettingThreadAffinitySupported;

	/// Sets the current process' logical core affinity.
	///
	/// This is not efficiently implemented (it uses a loop).
	///
	/// Failure occurs if a CPU in the set does not exist, is offline or in some other way is unavailable to the `process_identifier` (`EINVAL`).
	#[inline(always)]
	pub fn set_current_process_affinity(&self) -> io::Result<()>
	{
		self._set_process_affinity(Self::current_process_identifier())
	}

	/// Sets the process' logical core affinity.
	///
	/// This is not efficiently implemented (it uses a loop).
	///
	/// Failure occurs if:-
	///
	/// * Permission is denied to change the process affinity for `process_identifier` (`EPERM`) (for example, the process isn't a child of this process);
	/// * A CPU in the set does not exist, is offline or in some other way is unavailable to the `process_identifier` (`EINVAL`);
	/// * `process_identifier` does not exist (`ESRCH`)
	/// * Operating system is not yet implemented (`ENOSYS`) - typical of Emscripten and Fuschia.
	#[inline(always)]
	pub fn set_process_affinity(&self, process_identifier: ProcessIdentifier) -> io::Result<()>
	{
		self._set_process_affinity(process_identifier)
	}

	/// Sets the thread's logical core affinity.
	///
	/// Threads are never resident on just on core, and hence a lot of thread local opimizations (eg with clever non-blocking alogorithms) are useless.
	#[inline(always)]
	pub fn set_current_thread_affinity(&self) -> io::Result<()>
	{
		self._set_thread_affinity(Self::current_thread_identifier())
	}

	/// Sets the thread's logical core affinity.
	///
	/// This is only used as a *hint* on iOS and MacOS; it is near useless.
	///
	/// Threads are never resident on just on core, and hence a lot of thread local opimizations (eg with clever non-blocking alogorithms) are useless.
	///
	/// * Permission is denied to change the process affinity for `process_identifier` (`EPERM`) (for example, the process isn't a child of this process);
	/// * A CPU in the set does not exist, is offline or in some other way is unavailable to the `process_identifier` (`EINVAL`);
	/// * `process_identifier` does not exist (`ESRCH`)
	/// * Operating system is not yet implemented (`ENOSYS`) - typical of Emscripten and Fuschia.
	/// * `ERANGE` - on FreeBSD, the cpu set was far too large.
	/// * `EDEADLK` - on FreeBSD, the cpu set could not be honoured.
	#[inline(always)]
	pub fn set_thread_affinity(&self, thread_identifier: ThreadIdentifier) -> io::Result<()>
	{
		self._set_thread_affinity(thread_identifier)
	}

	#[cfg(unix)]
	const fn current_process_identifier() -> ProcessIdentifier
	{
		0
	}

	#[cfg(windows)]
	fn current_process_identifier() -> ProcessIdentifier
	{
		unsafe { ::kernel32::GetCurrentProcess() }
	}

	#[cfg(unix)]
	fn current_thread_identifier() -> ThreadIdentifier
	{
		unsafe { pthread_self() }
	}

	#[cfg(windows)]
	fn current_thread_identifier() -> ThreadIdentifier
	{
		unsafe { ::kernel32::GetCurrentThread() }
	}

	#[allow(dead_code)]
	#[inline(always)]
	fn last_os_error() -> io::Error
	{
		io::Error::last_os_error()
	}
}

#[cfg(any(target_os = "android", target_os = "emscripten", target_os = "fuschia", target_os = "linux", target_env = "uclibc"))] include!("LogicalCores.android-emscripten-fuschia-linux-uclibc.rs");
#[cfg(target_os = "dragonfly")] include!("LogicalCores.dragonfly.rs");
#[cfg(target_os = "freebsd")] include!("LogicalCores.freebsd.rs");
#[cfg(any(target_os = "ios", target_os = "macos"))] include!("LogicalCores.ios_macos.rs");
#[cfg(target_os = "netbsd")] include!("LogicalCores.netbsd.rs");
#[cfg(not(any(target_os = "android", target_os = "dragonfly", target_os = "emscripten", target_os = "freebsd", target_os = "fuschia", target_os = "ios", target_os = "linux", target_os = "macos", target_os = "netbsd", target_env = "uclibc", windows)))] include!("LogicalCores.others.rs");
#[cfg(windows)] include!("LogicalCores.windows.rs");