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 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 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 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 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 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 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