wasmsem.pas 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2025 by Michael Van Canneyt
  4. This unit contains a webassembly-specific semaphore implementation.
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. {$IFNDEF FPC_DOTTEDUNITS}
  12. unit WasmSem;
  13. {$ENDIF}
  14. {$mode objfpc}{$H+}
  15. {$codepage utf8}
  16. interface
  17. uses
  18. {$IFDEF FPC_DOTTEDUNITS}
  19. Wasm.Api;
  20. {$ELSE}
  21. WebAssembly;
  22. {$ENDIF}
  23. type
  24. PWasmSemaphore = ^TWasmSemaphore;
  25. TWasmSemaphore = packed record
  26. counter: longint; // Current counter value
  27. max_count: longint; // Maximum count allowed
  28. end;
  29. // Initialize a semaphore with initial count and maximum count
  30. procedure semaphore_init(var sem: TWasmSemaphore; initial_count, max_count: longint);
  31. // Wait (acquire) operation with timeout - decrements counter, blocks if counter would go negative
  32. // timeout_ms: timeout in milliseconds (-1 for infinite timeout)
  33. // Returns true if acquired successfully, false if timeout occurred
  34. function semaphore_wait(var sem: TWasmSemaphore; timeout_ms: int64): boolean;
  35. // Wait (acquire) operation without timeout - blocks indefinitely until semaphore is available
  36. // Returns true when acquired successfully
  37. function semaphore_wait_infinite(var sem: TWasmSemaphore): boolean;
  38. // Signal (release) operation - increments counter up to max and notifies waiters
  39. // Returns true if signaled successfully, false if at max count
  40. function semaphore_signal(var sem: TWasmSemaphore): boolean;
  41. // Get current semaphore count (read-only)
  42. function semaphore_count(var sem: TWasmSemaphore): longint;
  43. // Get maximum semaphore count (read-only)
  44. function semaphore_max_count(var sem: TWasmSemaphore): longint;
  45. implementation
  46. procedure semaphore_init(var sem: TWasmSemaphore; initial_count, max_count: longint);
  47. begin
  48. AtomicStore(sem.counter, initial_count);
  49. AtomicStore(sem.max_count, max_count);
  50. end;
  51. function semaphore_wait(var sem: TWasmSemaphore; timeout_ms: int64): boolean;
  52. var
  53. current_count: longint;
  54. new_count: longint;
  55. expected: longint;
  56. wait_result: longint;
  57. timeout_ns: int64;
  58. begin
  59. if timeout_ms = -1 then
  60. timeout_ns := awtInfiniteTimeout
  61. else
  62. timeout_ns := timeout_ms * 1000000;
  63. repeat
  64. current_count := AtomicLoad(sem.counter);
  65. if current_count > 0 then
  66. begin
  67. new_count := current_count - 1;
  68. expected := current_count;
  69. if AtomicCompareExchange(sem.counter, expected, new_count) = expected then
  70. exit(true);
  71. // Failed CAS, retry immediately
  72. end
  73. else
  74. begin
  75. wait_result := AtomicWait(sem.counter, current_count, timeout_ns);
  76. if wait_result = awrTimedOut then
  77. exit(false);
  78. // Either woke up (awrOk) or not-equal (awrNotEqual), retry the acquisition
  79. end;
  80. until false;
  81. // Should never reach here
  82. result := false;
  83. end;
  84. function semaphore_wait_infinite(var sem: TWasmSemaphore): boolean;
  85. begin
  86. result := semaphore_wait(sem, -1);
  87. end;
  88. function semaphore_signal(var sem: TWasmSemaphore): boolean;
  89. var
  90. current_count: longint;
  91. max_count: longint;
  92. new_count: longint;
  93. expected: longint;
  94. woken_count: longword;
  95. begin
  96. max_count := AtomicLoad(sem.max_count);
  97. repeat
  98. current_count := AtomicLoad(sem.counter);
  99. if current_count >= max_count then
  100. exit(false);
  101. new_count := current_count + 1;
  102. expected := current_count;
  103. // Try atomic compare-and-swap
  104. if AtomicCompareExchange(sem.counter, expected, new_count) = expected then
  105. begin
  106. woken_count := AtomicNotify(sem.counter, 1);
  107. exit(true);
  108. end;
  109. // Failed CAS, retry
  110. until false;
  111. // Should never reach here
  112. result := false;
  113. end;
  114. function semaphore_count(var sem: TWasmSemaphore): longint;
  115. begin
  116. result := AtomicLoad(sem.counter);
  117. end;
  118. function semaphore_max_count(var sem: TWasmSemaphore): longint;
  119. begin
  120. result := AtomicLoad(sem.max_count);
  121. end;
  122. end.