sema.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661
  1. /*++
  2. Copyright (c) 2015 Minoca Corp.
  3. This file is licensed under the terms of the GNU General Public License
  4. version 3. Alternative licensing terms are available. Contact
  5. info@minocacorp.com for details. See the LICENSE file at the root of this
  6. project for complete licensing information.
  7. Module Name:
  8. sema.c
  9. Abstract:
  10. This module implements support for POSIX semaphores.
  11. Author:
  12. Evan Green 4-May-2015
  13. Environment:
  14. User Mode C Library
  15. --*/
  16. //
  17. // ------------------------------------------------------------------- Includes
  18. //
  19. #include "pthreadp.h"
  20. #include <limits.h>
  21. #include <semaphore.h>
  22. //
  23. // --------------------------------------------------------------------- Macros
  24. //
  25. //
  26. // These macros manipulate the count inside the semaphore state.
  27. //
  28. #define PTHREAD_SEMAPHORE_GET_COUNT(_State) \
  29. ((INT)(_State) >> PTHREAD_SEMAPHORE_VALUE_SHIFT)
  30. //
  31. // This macro returns the semaphore state for a given count.
  32. //
  33. #define PTHREAD_SEMAPHORE_SET_COUNT(_Count) \
  34. (((ULONG)(_Count) << PTHREAD_SEMAPHORE_VALUE_SHIFT) & \
  35. PTHREAD_SEMAPHORE_VALUE_MASK)
  36. //
  37. // These macros increment and decrement the count in the given state.
  38. //
  39. #define PTHREAD_SEMAPHORE_DECREMENT_STATE(_State) \
  40. (((_State) - (1U << PTHREAD_SEMAPHORE_VALUE_SHIFT)) & \
  41. PTHREAD_SEMAPHORE_VALUE_MASK)
  42. #define PTHREAD_SEMAPHORE_INCREMENT_STATE(_State) \
  43. (((_State) + (1U << PTHREAD_SEMAPHORE_VALUE_SHIFT)) & \
  44. PTHREAD_SEMAPHORE_VALUE_MASK)
  45. //
  46. // ---------------------------------------------------------------- Definitions
  47. //
  48. #define PTHREAD_SEMAPHORE_SHARED 0x00000001
  49. #define PTHREAD_SEMAPHORE_VALUE_SHIFT 1
  50. #define PTHREAD_SEMAPHORE_VALUE_MASK 0xFFFFFFFE
  51. #define PTHREAD_SEMAPHORE_WAITED_ON PTHREAD_SEMAPHORE_SET_COUNT(-1)
  52. //
  53. // ------------------------------------------------------ Data Type Definitions
  54. //
  55. //
  56. // ----------------------------------------------- Internal Function Prototypes
  57. //
  58. INT
  59. ClpSemaphoreDecrement (
  60. PPTHREAD_SEMAPHORE Semaphore
  61. );
  62. INT
  63. ClpSemaphoreTryToDecrement (
  64. PPTHREAD_SEMAPHORE Semaphore
  65. );
  66. INT
  67. ClpSemaphoreIncrement (
  68. PPTHREAD_SEMAPHORE Semaphore
  69. );
  70. //
  71. // -------------------------------------------------------------------- Globals
  72. //
  73. //
  74. // ------------------------------------------------------------------ Functions
  75. //
  76. PTHREAD_API
  77. int
  78. sem_init (
  79. sem_t *Semaphore,
  80. int Shared,
  81. unsigned int Value
  82. )
  83. /*++
  84. Routine Description:
  85. This routine initializes a semaphore object.
  86. Arguments:
  87. Semaphore - Supplies a pointer to the semaphore to initialize.
  88. Shared - Supplies a boolean indicating whether the semaphore should be
  89. shared across processes (non-zero) or private to a particular process
  90. (zero).
  91. Value - Supplies the initial value to set in the semaphore.
  92. Return Value:
  93. 0 on success.
  94. -1 on failure, and errno will be set to contain more information.
  95. --*/
  96. {
  97. PPTHREAD_SEMAPHORE SemaphoreInternal;
  98. ULONG State;
  99. if (Value > SEM_VALUE_MAX) {
  100. errno = EINVAL;
  101. return -1;
  102. }
  103. State = PTHREAD_SEMAPHORE_SET_COUNT(Value);
  104. if (Shared != 0) {
  105. State |= PTHREAD_SEMAPHORE_SHARED;
  106. }
  107. SemaphoreInternal = (PPTHREAD_SEMAPHORE)Semaphore;
  108. SemaphoreInternal->State = State;
  109. return 0;
  110. }
  111. PTHREAD_API
  112. int
  113. sem_destroy (
  114. sem_t *Semaphore
  115. )
  116. /*++
  117. Routine Description:
  118. This routine releases all resources associated with a POSIX semaphore.
  119. Arguments:
  120. Semaphore - Supplies a pointer to the semaphore to destroy.
  121. Return Value:
  122. 0 on success.
  123. -1 on failure, and errno will be set to contain more information.
  124. --*/
  125. {
  126. return 0;
  127. }
  128. PTHREAD_API
  129. int
  130. sem_wait (
  131. sem_t *Semaphore
  132. )
  133. /*++
  134. Routine Description:
  135. This routine blocks until the given semaphore can be decremented to zero or
  136. above. On success, the semaphore value will be decremented.
  137. Arguments:
  138. Semaphore - Supplies a pointer to the semaphore to wait on.
  139. Return Value:
  140. 0 on success.
  141. -1 on failure, and errno will be set to contain more information.
  142. --*/
  143. {
  144. ULONG Operation;
  145. PPTHREAD_SEMAPHORE SemaphoreInternal;
  146. ULONG Shared;
  147. KSTATUS Status;
  148. ULONG Value;
  149. SemaphoreInternal = (PPTHREAD_SEMAPHORE)Semaphore;
  150. Shared = SemaphoreInternal->State & PTHREAD_SEMAPHORE_SHARED;
  151. while (TRUE) {
  152. if (ClpSemaphoreDecrement(SemaphoreInternal) > 0) {
  153. return 0;
  154. }
  155. Operation = UserLockWait;
  156. if (Shared == 0) {
  157. Operation |= USER_LOCK_PRIVATE;
  158. }
  159. Value = PTHREAD_SEMAPHORE_WAITED_ON | Shared;
  160. Status = OsUserLock(&(SemaphoreInternal->State),
  161. Operation,
  162. &Value,
  163. SYS_WAIT_TIME_INDEFINITE);
  164. if (Status == STATUS_INTERRUPTED) {
  165. errno = EINTR;
  166. return -1;
  167. }
  168. }
  169. return 0;
  170. }
  171. PTHREAD_API
  172. int
  173. sem_timedwait (
  174. sem_t *Semaphore,
  175. const struct timespec *AbsoluteTimeout
  176. )
  177. /*++
  178. Routine Description:
  179. This routine blocks until the given semaphore can be decremented to zero or
  180. above. This routine may time out after the specified deadline.
  181. Arguments:
  182. Semaphore - Supplies a pointer to the semaphore to wait on.
  183. AbsoluteTimeout - Supplies the deadline as an absolute time after which
  184. the operation should fail and return ETIMEDOUT.
  185. Return Value:
  186. 0 on success.
  187. -1 on failure, and errno will be set to contain more information.
  188. --*/
  189. {
  190. ULONG Operation;
  191. PPTHREAD_SEMAPHORE SemaphoreInternal;
  192. ULONG Shared;
  193. KSTATUS Status;
  194. ULONG TimeoutInMilliseconds;
  195. ULONG Value;
  196. SemaphoreInternal = (PPTHREAD_SEMAPHORE)Semaphore;
  197. //
  198. // Try to decrement the semaphore before checking the timeout.
  199. //
  200. if (ClpSemaphoreTryToDecrement(SemaphoreInternal) > 0) {
  201. return 0;
  202. }
  203. if ((AbsoluteTimeout == NULL) ||
  204. (AbsoluteTimeout->tv_sec < 0) ||
  205. (AbsoluteTimeout->tv_nsec < 0) ||
  206. (AbsoluteTimeout->tv_nsec > NANOSECONDS_PER_SECOND)) {
  207. errno = EINVAL;
  208. return -1;
  209. }
  210. Shared = SemaphoreInternal->State & PTHREAD_SEMAPHORE_SHARED;
  211. Operation = UserLockWait;
  212. if (Shared == 0) {
  213. Operation |= USER_LOCK_PRIVATE;
  214. }
  215. while (TRUE) {
  216. TimeoutInMilliseconds =
  217. ClpConvertAbsoluteTimespecToRelativeMilliseconds(AbsoluteTimeout,
  218. CLOCK_REALTIME);
  219. //
  220. // Try to grab the semaphore.
  221. //
  222. if (ClpSemaphoreDecrement(SemaphoreInternal) > 0) {
  223. break;
  224. }
  225. Value = PTHREAD_SEMAPHORE_WAITED_ON | Shared;
  226. Status = OsUserLock(&(SemaphoreInternal->State),
  227. Operation,
  228. &Value,
  229. TimeoutInMilliseconds);
  230. if (Status == STATUS_TIMEOUT) {
  231. errno = ETIMEDOUT;
  232. return -1;
  233. } else if (Status == STATUS_INTERRUPTED) {
  234. errno = EINTR;
  235. return -1;
  236. }
  237. }
  238. return 0;
  239. }
  240. PTHREAD_API
  241. int
  242. sem_trywait (
  243. sem_t *Semaphore
  244. )
  245. /*++
  246. Routine Description:
  247. This routine blocks until the given semaphore can be decremented to zero or
  248. above. This routine may time out after the specified deadline.
  249. Arguments:
  250. Semaphore - Supplies a pointer to the semaphore to wait on.
  251. Return Value:
  252. 0 on success.
  253. -1 on failure, and errno will be set to contain more information.
  254. --*/
  255. {
  256. if (ClpSemaphoreTryToDecrement((PPTHREAD_SEMAPHORE)Semaphore) > 0) {
  257. return 0;
  258. }
  259. errno = EAGAIN;
  260. return -1;
  261. }
  262. PTHREAD_API
  263. int
  264. sem_getvalue (
  265. sem_t *Semaphore,
  266. int *SemaphoreValue
  267. )
  268. /*++
  269. Routine Description:
  270. This routine returns the current count of the semaphore.
  271. Arguments:
  272. Semaphore - Supplies a pointer to the semaphore.
  273. SemaphoreValue - Supplies a pointer where the semaphore value will be
  274. returned.
  275. Return Value:
  276. 0 on success.
  277. -1 on failure, and errno will be set to contain more information.
  278. --*/
  279. {
  280. PPTHREAD_SEMAPHORE SemaphoreInternal;
  281. INT Value;
  282. SemaphoreInternal = (PPTHREAD_SEMAPHORE)Semaphore;
  283. Value = PTHREAD_SEMAPHORE_GET_COUNT(SemaphoreInternal->State);
  284. if (Value < 0) {
  285. Value = 0;
  286. }
  287. *SemaphoreValue = Value;
  288. return 0;
  289. }
  290. PTHREAD_API
  291. int
  292. sem_post (
  293. sem_t *Semaphore
  294. )
  295. /*++
  296. Routine Description:
  297. This routine increments the semaphore value. If the value is incremented
  298. above zero, then threads waiting on the semaphore will be released to
  299. try and acquire it.
  300. Arguments:
  301. Semaphore - Supplies a pointer to the semaphore.
  302. Return Value:
  303. 0 on success.
  304. -1 on failure, and errno will be set to contain more information.
  305. --*/
  306. {
  307. ULONG Count;
  308. ULONG Operation;
  309. PPTHREAD_SEMAPHORE SemaphoreInternal;
  310. INT Value;
  311. SemaphoreInternal = (PPTHREAD_SEMAPHORE)Semaphore;
  312. Value = ClpSemaphoreIncrement(SemaphoreInternal);
  313. //
  314. // If there were waiters, wake everyone up.
  315. //
  316. if (Value < 0) {
  317. Operation = UserLockWake;
  318. if ((SemaphoreInternal->State & PTHREAD_SEMAPHORE_SHARED) == 0) {
  319. Operation |= USER_LOCK_PRIVATE;
  320. }
  321. Count = MAX_ULONG;
  322. OsUserLock(&(SemaphoreInternal->State), Operation, &Count, 0);
  323. } else if (Value == SEM_VALUE_MAX) {
  324. errno = EOVERFLOW;
  325. return -1;
  326. }
  327. return 0;
  328. }
  329. //
  330. // --------------------------------------------------------- Internal Functions
  331. //
  332. INT
  333. ClpSemaphoreDecrement (
  334. PPTHREAD_SEMAPHORE Semaphore
  335. )
  336. /*++
  337. Routine Description:
  338. This routine decrements the semaphore value and returns the old value,
  339. honoring the value of negative one, which means there are waiters.
  340. Arguments:
  341. Semaphore - Supplies a pointer to the semaphore to decrement.
  342. Return Value:
  343. Returns the previous count.
  344. --*/
  345. {
  346. ULONG NewValue;
  347. ULONG OldValue;
  348. ULONG SetValue;
  349. ULONG Shared;
  350. OldValue = Semaphore->State;
  351. Shared = OldValue & PTHREAD_SEMAPHORE_SHARED;
  352. //
  353. // Loop trying to perform a sane decrement.
  354. //
  355. while (TRUE) {
  356. if (PTHREAD_SEMAPHORE_GET_COUNT(OldValue) < 0) {
  357. break;
  358. }
  359. SetValue = PTHREAD_SEMAPHORE_DECREMENT_STATE(OldValue) | Shared;
  360. NewValue = RtlAtomicCompareExchange32(&(Semaphore->State),
  361. SetValue,
  362. OldValue);
  363. if (NewValue == OldValue) {
  364. break;
  365. }
  366. OldValue = NewValue;
  367. }
  368. return PTHREAD_SEMAPHORE_GET_COUNT(OldValue);
  369. }
  370. INT
  371. ClpSemaphoreTryToDecrement (
  372. PPTHREAD_SEMAPHORE Semaphore
  373. )
  374. /*++
  375. Routine Description:
  376. This routine attempts to decrement the semaphore value and returns the old
  377. value. It will not change the value if it was zero or "waited on".
  378. Arguments:
  379. Semaphore - Supplies a pointer to the semaphore to attempt to decrement.
  380. Return Value:
  381. Returns the previous count.
  382. --*/
  383. {
  384. ULONG NewValue;
  385. ULONG OldValue;
  386. ULONG SetValue;
  387. ULONG Shared;
  388. OldValue = Semaphore->State;
  389. Shared = OldValue & PTHREAD_SEMAPHORE_SHARED;
  390. //
  391. // Loop trying to perform a sane decrement.
  392. //
  393. while (TRUE) {
  394. if (PTHREAD_SEMAPHORE_GET_COUNT(OldValue) <= 0) {
  395. break;
  396. }
  397. SetValue = PTHREAD_SEMAPHORE_DECREMENT_STATE(OldValue) | Shared;
  398. NewValue = RtlAtomicCompareExchange32(&(Semaphore->State),
  399. SetValue,
  400. OldValue);
  401. if (NewValue == OldValue) {
  402. break;
  403. }
  404. OldValue = NewValue;
  405. }
  406. return PTHREAD_SEMAPHORE_GET_COUNT(OldValue);
  407. }
  408. INT
  409. ClpSemaphoreIncrement (
  410. PPTHREAD_SEMAPHORE Semaphore
  411. )
  412. /*++
  413. Routine Description:
  414. This routine increments the count in the semaphore and returns the old
  415. value. The count of -1 is treated equal to the count of 0 (as in, -1 goes
  416. directly to 1).
  417. Arguments:
  418. Semaphore - Supplies a pointer to the semaphore to increment.
  419. Return Value:
  420. Returns the previous count.
  421. --*/
  422. {
  423. ULONG NewValue;
  424. ULONG OldValue;
  425. ULONG SetValue;
  426. ULONG Shared;
  427. OldValue = Semaphore->State;
  428. Shared = OldValue & PTHREAD_SEMAPHORE_SHARED;
  429. //
  430. // Loop trying to perform a sane decrement.
  431. //
  432. while (TRUE) {
  433. if (PTHREAD_SEMAPHORE_GET_COUNT(OldValue) == SEM_VALUE_MAX) {
  434. break;
  435. }
  436. //
  437. // Negative values to straight to one.
  438. //
  439. if (PTHREAD_SEMAPHORE_GET_COUNT(OldValue) < 0) {
  440. SetValue = PTHREAD_SEMAPHORE_SET_COUNT(1) | Shared;
  441. } else {
  442. SetValue = PTHREAD_SEMAPHORE_INCREMENT_STATE(OldValue) | Shared;
  443. }
  444. NewValue = RtlAtomicCompareExchange32(&(Semaphore->State),
  445. SetValue,
  446. OldValue);
  447. if (NewValue == OldValue) {
  448. break;
  449. }
  450. OldValue = NewValue;
  451. }
  452. return PTHREAD_SEMAPHORE_GET_COUNT(OldValue);
  453. }