1#![feature(core_intrinsics)]
4#![feature(link_llvm_intrinsics)]
5#![feature(test)]
6#![warn(missing_docs)]
7
8extern crate test;
9
10#[cfg_attr(
11 all(target_arch = "powerpc64", feature = "htm"),
12 path = "./powerpc64.rs"
13)]
14#[cfg_attr(all(target_arch = "x86_64", feature = "rtm"), path = "./x86_64.rs")]
15#[cfg_attr(
16 not(any(
17 all(target_arch = "x86_64", feature = "rtm"),
18 all(target_arch = "powerpc64", feature = "htm")
19 )),
20 path = "./unsupported.rs"
21)]
22pub mod back;
23
24use std::{
25 cell::UnsafeCell,
26 marker::PhantomData,
27 ops::{Deref, DerefMut},
28 sync::atomic::AtomicUsize,
29};
30
31#[inline]
33pub const fn htm_supported() -> bool {
34 back::htm_supported()
35}
36
37#[inline]
39pub fn htm_supported_runtime() -> bool {
40 back::htm_supported_runtime()
41}
42
43#[inline]
53pub unsafe fn begin() -> BeginCode {
54 BeginCode(back::begin())
55}
56
57#[inline]
63pub unsafe fn abort() -> ! {
64 back::abort()
65}
66
67#[inline]
69pub unsafe fn test() -> TestCode {
70 TestCode(back::test())
71}
72
73#[inline]
79pub unsafe fn end() {
80 back::end()
81}
82
83#[repr(transparent)]
85#[derive(PartialEq, Eq, Ord, PartialOrd, Copy, Clone, Debug, Hash)]
86pub struct BeginCode(back::BeginCode);
87
88impl BeginCode {
89 #[inline]
91 pub fn is_started(&self) -> bool {
92 self.0.is_started()
93 }
94
95 #[inline]
97 pub fn is_explicit_abort(&self) -> bool {
98 self.0.is_explicit_abort()
99 }
100
101 #[inline]
103 pub fn is_retry(&self) -> bool {
104 self.0.is_retry()
105 }
106
107 #[inline]
109 pub fn is_conflict(&self) -> bool {
110 self.0.is_conflict()
111 }
112
113 #[inline]
117 pub fn is_capacity(&self) -> bool {
118 self.0.is_capacity()
119 }
120}
121
122#[repr(transparent)]
124#[derive(PartialEq, Eq, Ord, PartialOrd, Copy, Clone, Debug, Hash)]
125pub struct TestCode(back::TestCode);
126
127impl TestCode {
128 #[inline]
130 pub fn in_transaction(&self) -> bool {
131 self.0.in_transaction()
132 }
133
134 #[inline]
136 pub fn is_suspended(&self) -> bool {
137 self.0.is_suspended()
138 }
139}
140
141#[derive(Debug)]
145pub struct HardwareTx {
146 _private: PhantomData<*mut ()>,
147}
148
149impl Drop for HardwareTx {
150 #[inline]
151 fn drop(&mut self) {
152 unsafe { end() }
153 }
154}
155
156impl HardwareTx {
157 #[inline]
170 pub unsafe fn new<F, E>(mut retry_handler: F) -> Result<Self, E>
171 where
172 F: FnMut(BeginCode) -> Result<(), E>,
173 {
174 loop {
175 let b = begin();
176 if std::intrinsics::likely(b.is_started()) {
177 return Ok(HardwareTx {
178 _private: PhantomData,
179 });
180 } else {
181 retry_handler(b)?
182 }
183 }
184 }
185
186 #[inline(always)]
193 pub fn abort(&self) -> ! {
194 unsafe { abort() }
195 }
196}
197
198#[derive(Debug)]
200#[repr(transparent)]
201pub struct HtmUsize {
202 inner: UnsafeCell<AtomicUsize>,
203}
204
205unsafe impl Send for HtmUsize {}
206unsafe impl Sync for HtmUsize {}
207
208impl HtmUsize {
209 #[inline]
211 pub const fn new(value: usize) -> Self {
212 HtmUsize {
213 inner: UnsafeCell::new(AtomicUsize::new(value)),
214 }
215 }
216
217 #[inline(always)]
222 unsafe fn as_raw(&self, _: &HardwareTx) -> &mut AtomicUsize {
223 &mut *self.inner.get()
224 }
225
226 #[inline(always)]
228 pub fn get(&self, htx: &HardwareTx) -> usize {
229 unsafe { *self.as_raw(htx).get_mut() }
230 }
231
232 #[inline(always)]
234 pub fn set(&self, htx: &HardwareTx, value: usize) {
235 unsafe { *self.as_raw(htx).get_mut() = value }
236 }
237}
238
239impl Deref for HtmUsize {
240 type Target = AtomicUsize;
241
242 #[inline(always)]
243 fn deref(&self) -> &Self::Target {
244 unsafe { &*self.inner.get() }
245 }
246}
247
248impl DerefMut for HtmUsize {
249 #[inline(always)]
250 fn deref_mut(&mut self) -> &mut Self::Target {
251 unsafe { &mut *self.inner.get() }
252 }
253}
254
255macro_rules! bench_tx {
256 ($name:ident, $count:expr) => {
257 #[bench]
258 fn $name(bench: &mut test::Bencher) {
259 const ITER_COUNT: usize = 1_000_000;
260 const WORDS_WRITTEN: usize = $count;
261
262 #[repr(align(4096))]
263 struct AlignedArr([usize; WORDS_WRITTEN]);
264
265 let mut arr = AlignedArr([0usize; WORDS_WRITTEN]);
266
267 for (i, elem) in arr.0.iter_mut().enumerate() {
268 unsafe { std::ptr::write_volatile(elem, test::black_box(elem.wrapping_add(i))) };
269 test::black_box(elem);
270 }
271
272 bench.iter(move || {
273 for _ in 0..ITER_COUNT {
274 unsafe {
275 let tx = HardwareTx::new(|_| -> Result<(), ()> { Err(()) });
276 for i in 0..arr.0.len() {
277 *arr.0.get_unchecked_mut(i) =
278 arr.0.get_unchecked_mut(i).wrapping_add(1);
279 }
280 drop(tx);
281 }
282 }
283 });
284 }
285 };
286}
287
288bench_tx! {bench_tx0000, 0}
289bench_tx! {bench_tx0001, 1}
290bench_tx! {bench_tx0002, 2}
291bench_tx! {bench_tx0004, 4}
292bench_tx! {bench_tx0008, 8}
293bench_tx! {bench_tx0016, 16}
294bench_tx! {bench_tx0024, 24}
295bench_tx! {bench_tx0032, 32}
296bench_tx! {bench_tx0040, 40}
297bench_tx! {bench_tx0048, 48}
298bench_tx! {bench_tx0056, 56}
299bench_tx! {bench_tx0064, 64}
300bench_tx! {bench_tx0072, 72}
301bench_tx! {bench_tx0080, 80}
302bench_tx! {bench_tx0112, 112}
303bench_tx! {bench_tx0120, 120}
304bench_tx! {bench_tx0128, 128}
305bench_tx! {bench_tx0256, 256}
306
307#[bench]
308fn bench_abort(bench: &mut test::Bencher) {
309 const ITER_COUNT: usize = 1_000_000;
310
311 bench.iter(|| {
312 for _ in 0..ITER_COUNT {
313 unsafe {
314 let tx = HardwareTx::new(|code| -> Result<(), ()> {
315 if code.is_explicit_abort() {
316 Err(())
317 } else {
318 Ok(())
319 }
320 });
321 drop(tx.map(|tx| tx.abort()));
322 }
323 }
324 });
325}
326
327#[test]
328fn begin_end() {
329 const ITER_COUNT: usize = 1_000_000;
330
331 let mut fails = 0;
332 for _ in 0..ITER_COUNT {
333 unsafe {
334 let _tx = HardwareTx::new(|_| -> Result<(), ()> {
335 fails += 1;
336 Ok(())
337 })
338 .unwrap();
339 }
340 }
341 println!(
342 "fail rate {:.4}%",
343 fails as f64 * 100.0 / (ITER_COUNT + fails) as f64
344 );
345}
346
347#[test]
348fn test_in_transaction() {
349 for _ in 0..1000000 {
350 unsafe {
351 assert!(!test().in_transaction());
352 let _tx = HardwareTx::new(|_| -> Result<(), ()> { Ok(()) }).unwrap();
353 assert!(test().in_transaction());
354 }
355 }
356}
357
358#[test]
359fn begin_abort() {
360 let mut i = 0i32;
361 let mut abort_count = 0;
362 loop {
363 let i = &mut i;
364 *i += 1;
365 unsafe {
366 let tx = HardwareTx::new(|code| -> Result<(), ()> {
367 if code.is_explicit_abort() {
368 abort_count += 1;
369 *i += 1;
370 }
371 Ok(())
372 })
373 .unwrap();
374
375 if *i % 128 != 0 && *i != 1_000_000 {
376 tx.abort();
377 }
378 }
379 if *i == 1_000_000 {
380 break;
381 }
382 }
383 assert_eq!(abort_count, 992187);
384}
385
386#[test]
387fn capacity_check() {
388 use std::mem;
389
390 const CACHE_LINE_SIZE: usize = 64 / mem::size_of::<usize>();
391
392 let mut data = vec![0usize; 1000000];
393 let mut capacity = 0;
394 let end = data.len() / CACHE_LINE_SIZE;
395 for i in (0..end).rev() {
396 data[i * CACHE_LINE_SIZE] = data[i * CACHE_LINE_SIZE].wrapping_add(1);
397 test::black_box(&mut data[i * CACHE_LINE_SIZE]);
398 }
399 for max in 0..end {
400 let mut fail_count = 0;
401 unsafe {
402 let tx = HardwareTx::new(|code| {
403 let cap = code.is_capacity();
404 if cap {
405 fail_count += 1;
406 }
407 if !cap || fail_count < 1000 {
408 Ok(())
409 } else {
410 Err(())
411 }
412 });
413 let tx = match tx {
414 Ok(tx) => tx,
415 Err(()) => break,
416 };
417 for i in 0..max {
418 let elem = data.get_unchecked_mut(i * CACHE_LINE_SIZE);
419 *elem = elem.wrapping_add(1);
420 }
421 drop(tx);
422 }
423 capacity = max;
424 }
425 test::black_box(&mut data);
426 println!("sum: {}", data.iter().sum::<usize>());
427 println!(
429 "Capacity: {}",
430 capacity * mem::size_of::<usize>() * CACHE_LINE_SIZE
431 );
432}
433
434#[test]
435fn supported() {
436 let compile = htm_supported();
437 let runtime = htm_supported_runtime();
438 if compile {
439 assert!(runtime);
440 }
441
442 println!("");
443 println!("compile time support check: {}", compile);
444 println!(" runtime support check: {}", runtime);
445}