1 /**************************************************************************
2 *
3 * Copyright 2020 Lag Free Games, LLC
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sub license, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial portions
16 * of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21 * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
22 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 *
26 **************************************************************************/
27
28 #ifndef CND_MONOTONIC_H
29 #define CND_MONOTONIC_H
30
31 #include "c11/threads.h"
32 #include "util/os_time.h"
33
34 #ifdef __cplusplus
35 extern "C" {
36 #endif
37
38 struct u_cnd_monotonic
39 {
40 #ifdef _WIN32
41 CONDITION_VARIABLE condvar;
42 #else
43 pthread_cond_t cond;
44 #endif
45 };
46
47 static inline int
u_cnd_monotonic_init(struct u_cnd_monotonic * cond)48 u_cnd_monotonic_init(struct u_cnd_monotonic *cond)
49 {
50 assert(cond != NULL);
51
52 #ifdef _WIN32
53 InitializeConditionVariable(&cond->condvar);
54 return thrd_success;
55 #else
56 int ret = thrd_error;
57 pthread_condattr_t condattr;
58 if (pthread_condattr_init(&condattr) == 0) {
59 if ((pthread_condattr_setclock(&condattr, CLOCK_MONOTONIC) == 0) &&
60 (pthread_cond_init(&cond->cond, &condattr) == 0)) {
61 ret = thrd_success;
62 }
63
64 pthread_condattr_destroy(&condattr);
65 }
66
67 return ret;
68 #endif
69 }
70
71 static inline void
u_cnd_monotonic_destroy(struct u_cnd_monotonic * cond)72 u_cnd_monotonic_destroy(struct u_cnd_monotonic *cond)
73 {
74 assert(cond != NULL);
75
76 #ifdef _WIN32
77 // Do nothing
78 #else
79 pthread_cond_destroy(&cond->cond);
80 #endif
81 }
82
83 static inline int
u_cnd_monotonic_broadcast(struct u_cnd_monotonic * cond)84 u_cnd_monotonic_broadcast(struct u_cnd_monotonic *cond)
85 {
86 assert(cond != NULL);
87
88 #ifdef _WIN32
89 WakeAllConditionVariable(&cond->condvar);
90 return thrd_success;
91 #else
92 return (pthread_cond_broadcast(&cond->cond) == 0) ? thrd_success : thrd_error;
93 #endif
94 }
95
96 static inline int
u_cnd_monotonic_signal(struct u_cnd_monotonic * cond)97 u_cnd_monotonic_signal(struct u_cnd_monotonic *cond)
98 {
99 assert(cond != NULL);
100
101 #ifdef _WIN32
102 WakeConditionVariable(&cond->condvar);
103 return thrd_success;
104 #else
105 return (pthread_cond_signal(&cond->cond) == 0) ? thrd_success : thrd_error;
106 #endif
107 }
108
109 static inline int
u_cnd_monotonic_timedwait(struct u_cnd_monotonic * cond,mtx_t * mtx,const struct timespec * abs_time)110 u_cnd_monotonic_timedwait(struct u_cnd_monotonic *cond, mtx_t *mtx, const struct timespec *abs_time)
111 {
112 assert(cond != NULL);
113 assert(mtx != NULL);
114 assert(abs_time != NULL);
115
116 #ifdef _WIN32
117 const uint64_t future = (abs_time->tv_sec * 1000) + (abs_time->tv_nsec / 1000000);
118 const uint64_t now = os_time_get_nano() / 1000000;
119 const DWORD timeout = (future > now) ? (DWORD)(future - now) : 0;
120 if (SleepConditionVariableCS(&cond->condvar, mtx, timeout))
121 return thrd_success;
122 return (GetLastError() == ERROR_TIMEOUT) ? thrd_busy : thrd_error;
123 #else
124 int rt = pthread_cond_timedwait(&cond->cond, mtx, abs_time);
125 if (rt == ETIMEDOUT)
126 return thrd_busy;
127 return (rt == 0) ? thrd_success : thrd_error;
128 #endif
129 }
130
131 static inline int
u_cnd_monotonic_wait(struct u_cnd_monotonic * cond,mtx_t * mtx)132 u_cnd_monotonic_wait(struct u_cnd_monotonic *cond, mtx_t *mtx)
133 {
134 assert(cond != NULL);
135 assert(mtx != NULL);
136
137 #ifdef _WIN32
138 SleepConditionVariableCS(&cond->condvar, mtx, INFINITE);
139 return thrd_success;
140 #else
141 return (pthread_cond_wait(&cond->cond, mtx) == 0) ? thrd_success : thrd_error;
142 #endif
143 }
144
145 #ifdef __cplusplus
146 }
147 #endif
148
149 #endif
150