atomics.odin 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. // TODO(bill): Use assembly instead here to implement atomics
  2. // Inline vs external file?
  3. when ODIN_OS == "windows" {
  4. import win32 "core:sys/windows.odin"
  5. }
  6. _ :: compile_assert(ODIN_ARCH == "amd64"); // TODO(bill): x86 version
  7. yield_thread :: proc() { win32.mm_pause(); }
  8. mfence :: proc() { win32.read_write_barrier(); }
  9. sfence :: proc() { win32.write_barrier(); }
  10. lfence :: proc() { win32.read_barrier(); }
  11. load_i32 :: proc(a: ^i32) -> i32 {
  12. return a^;
  13. }
  14. store_i32 :: proc(a: ^i32, value: i32) {
  15. a^ = value;
  16. }
  17. compare_exchange_i32 :: proc(a: ^i32, expected, desired: i32) -> i32 {
  18. return win32.interlocked_compare_exchange(a, desired, expected);
  19. }
  20. exchanged_i32 :: proc(a: ^i32, desired: i32) -> i32 {
  21. return win32.interlocked_exchange(a, desired);
  22. }
  23. fetch_add_i32 :: proc(a: ^i32, operand: i32) -> i32 {
  24. return win32.interlocked_exchange_add(a, operand);
  25. }
  26. fetch_and_i32 :: proc(a: ^i32, operand: i32) -> i32 {
  27. return win32.interlocked_and(a, operand);
  28. }
  29. fetch_or_i32 :: proc(a: ^i32, operand: i32) -> i32 {
  30. return win32.interlocked_or(a, operand);
  31. }
  32. spin_lock_i32 :: proc(a: ^i32, time_out: int) -> bool { // NOTE(bill) time_out = -1 as default
  33. old_value := compare_exchange_i32(a, 1, 0);
  34. counter := 0;
  35. for old_value != 0 && (time_out < 0 || counter < time_out) {
  36. counter += 1;
  37. yield_thread();
  38. old_value = compare_exchange_i32(a, 1, 0);
  39. mfence();
  40. }
  41. return old_value == 0;
  42. }
  43. spin_unlock_i32 :: proc(a: ^i32) {
  44. store_i32(a, 0);
  45. mfence();
  46. }
  47. try_acquire_lock_i32 :: proc(a: ^i32) -> bool {
  48. yield_thread();
  49. old_value := compare_exchange_i32(a, 1, 0);
  50. mfence();
  51. return old_value == 0;
  52. }
  53. load_i64 :: proc(a: ^i64) -> i64 {
  54. return a^;
  55. }
  56. store_i64 :: proc(a: ^i64, value: i64) {
  57. a^ = value;
  58. }
  59. compare_exchange_i64 :: proc(a: ^i64, expected, desired: i64) -> i64 {
  60. return win32.interlocked_compare_exchange64(a, desired, expected);
  61. }
  62. exchanged_i64 :: proc(a: ^i64, desired: i64) -> i64 {
  63. return win32.interlocked_exchange64(a, desired);
  64. }
  65. fetch_add_i64 :: proc(a: ^i64, operand: i64) -> i64 {
  66. return win32.interlocked_exchange_add64(a, operand);
  67. }
  68. fetch_and_i64 :: proc(a: ^i64, operand: i64) -> i64 {
  69. return win32.interlocked_and64(a, operand);
  70. }
  71. fetch_or_i64 :: proc(a: ^i64, operand: i64) -> i64 {
  72. return win32.interlocked_or64(a, operand);
  73. }
  74. spin_lock_i64 :: proc(a: ^i64, time_out: int) -> bool { // NOTE(bill) time_out = -1 as default
  75. old_value := compare_exchange_i64(a, 1, 0);
  76. counter := 0;
  77. for old_value != 0 && (time_out < 0 || counter < time_out) {
  78. counter += 1;
  79. yield_thread();
  80. old_value = compare_exchange_i64(a, 1, 0);
  81. mfence();
  82. }
  83. return old_value == 0;
  84. }
  85. spin_unlock_i64 :: proc(a: ^i64) {
  86. store_i64(a, 0);
  87. mfence();
  88. }
  89. try_acquire_lock_i64 :: proc(a: ^i64) -> bool {
  90. yield_thread();
  91. old_value := compare_exchange_i64(a, 1, 0);
  92. mfence();
  93. return old_value == 0;
  94. }