1/* 2 Title: Mutex and Condition Variable library. 3 4 Copyright (c) 2007, 2012, 2015, 2019 David C. J. Matthews 5 6 This library is free software; you can redistribute it and/or 7 modify it under the terms of the GNU Lesser General Public 8 License version 2.1 as published by the Free Software Foundation. 9 10 This library is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 Lesser General Public License for more details. 14 15 You should have received a copy of the GNU Lesser General Public 16 License along with this library; if not, write to the Free Software 17 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 18 19*/ 20 21#ifdef HAVE_CONFIG_H 22#include "config.h" 23#elif defined(_WIN32) 24#include "winconfig.h" 25#else 26#error "No configuration file" 27#endif 28 29#if (!defined(_WIN32)) 30// Configure requires pthread unless this is native Windows. 31#include <pthread.h> 32#else 33#include <windows.h> 34#endif 35 36#ifdef HAVE_ERRNO_H 37#include <errno.h> 38#endif 39 40#ifdef HAVE_SYS_TIME_H 41#include <sys/time.h> 42#endif 43 44#ifdef HAVE_TIME_H 45#include <time.h> 46#endif 47 48#if (defined(HAVE_SEMAPHORE_H) && !defined(_WIN32)) 49// Don't include semaphore.h on Mingw. It's provided but doesn't compile. 50#include <semaphore.h> 51#endif 52 53#ifdef HAVE_SYS_TYPES_H 54#include <sys/types.h> 55#endif 56 57#ifdef HAVE_SYS_STAT_H 58#include <sys/stat.h> 59#endif 60 61#ifdef HAVE_FCNTL_H 62#include <fcntl.h> 63#endif 64 65#ifdef HAVE_UNISTD_H 66#include <unistd.h> 67#endif 68 69#ifdef HAVE_STDIO_H 70#include <stdio.h> 71#endif 72 73#include "locking.h" 74#include "diagnostics.h" 75 76// Report contended locks after this many attempts 77#define LOCK_REPORT_COUNT 50 78 79PLock::PLock(const char *n): lockName(n), lockCount(0) 80{ 81#if (!defined(_WIN32)) 82 pthread_mutex_init(&lock, 0); 83#else 84 InitializeCriticalSection(&lock); 85#endif 86} 87 88PLock::~PLock() 89{ 90#if (!defined(_WIN32)) 91 pthread_mutex_destroy(&lock); 92#else 93 DeleteCriticalSection(&lock); 94#endif 95} 96 97void PLock::Lock(void) 98{ 99 if (debugOptions & DEBUG_CONTENTION) 100 { 101 // Report a heavily contended lock. 102 if (Trylock()) 103 return; 104 if (++lockCount > LOCK_REPORT_COUNT) 105 { 106 if (lockName != 0) 107 Log("Lock: contention on lock: %s\n", lockName); 108 else 109 Log("Lock: contention on lock at %p\n", &lock); 110 lockCount = 0; 111 } 112 // Drop through to a normal lock 113 } 114#if (!defined(_WIN32)) 115 pthread_mutex_lock(&lock); 116#else 117 EnterCriticalSection(&lock); 118#endif 119} 120 121void PLock::Unlock(void) 122{ 123#if (!defined(_WIN32)) 124 pthread_mutex_unlock(&lock); 125#else 126 LeaveCriticalSection(&lock); 127#endif 128} 129 130bool PLock::Trylock(void) 131{ 132#if (!defined(_WIN32)) 133 // Since we use normal mutexes this returns EBUSY if the 134 // current thread owns the mutex. 135 return pthread_mutex_trylock(&lock) != EBUSY; 136#else 137 // This is not implemented properly in Windows. There is 138 // TryEnterCriticalSection in Win NT and later but that 139 // returns TRUE if the current thread owns the mutex. 140 return TryEnterCriticalSection(&lock) == TRUE; 141#endif 142} 143 144PCondVar::PCondVar() 145{ 146#if (!defined(_WIN32)) 147 pthread_cond_init(&cond, NULL); 148#else 149 InitializeConditionVariable(&cond); 150#endif 151} 152 153PCondVar::~PCondVar() 154{ 155#if (!defined(_WIN32)) 156 pthread_cond_destroy(&cond); 157#endif 158} 159 160// Wait indefinitely. Drops the lock and reaquires it. 161void PCondVar::Wait(PLock *pLock) 162{ 163#if (!defined(_WIN32)) 164 pthread_cond_wait(&cond, &pLock->lock); 165#else 166 SleepConditionVariableCS(&cond, &pLock->lock, INFINITE); 167#endif 168} 169 170// Wait until a specified absolute time. Drops the lock and reaquires it. 171#if (defined(_WIN32)) 172// Windows with Windows-style times 173void PCondVar::WaitUntil(PLock *pLock, const FILETIME *time) 174{ 175 FILETIME now; 176 GetSystemTimeAsFileTime(&now); 177 LARGE_INTEGER liNow, liTime; 178 liNow.HighPart = now.dwHighDateTime; 179 liNow.LowPart = now.dwLowDateTime; 180 liTime.HighPart = time->dwHighDateTime; 181 liTime.LowPart = time->dwLowDateTime; 182 if (liNow.QuadPart >= liTime.QuadPart) // Already past the time 183 return; 184 DWORD toWait = (DWORD)((liTime.QuadPart - liNow.QuadPart) / (LONGLONG)10000); 185 (void)WaitFor(pLock, toWait); 186} 187#else 188// Unix-style times 189void PCondVar::WaitUntil(PLock *pLock, const timespec *time) 190{ 191 pthread_cond_timedwait(&cond, &pLock->lock, time); 192} 193#endif 194 195// Wait for a number of milliseconds. Used within the RTS. Drops the lock and reaquires it. 196// Returns true if the return was because the condition variable had been signalled. 197// Returns false if the timeout expired or there was an error. 198bool PCondVar::WaitFor(PLock *pLock, unsigned milliseconds) 199{ 200#if (!defined(_WIN32)) 201 struct timespec waitTime; 202 struct timeval tv; 203 if (gettimeofday(&tv, NULL) != 0) 204 return false; 205 waitTime.tv_sec = tv.tv_sec + milliseconds / 1000; 206 waitTime.tv_nsec = (tv.tv_usec + (milliseconds % 1000) * 1000) * 1000; 207 if (waitTime.tv_nsec >= 1000*1000*1000) 208 { 209 waitTime.tv_nsec -= 1000*1000*1000; 210 waitTime.tv_sec += 1; 211 } 212 return pthread_cond_timedwait(&cond, &pLock->lock, &waitTime) == 0; 213#else 214 // SleepConditionVariableCS returns zero on error or timeout. 215 return SleepConditionVariableCS(&cond, &pLock->lock, milliseconds) != 0; 216#endif 217} 218 219// Wake up all the waiting threads. 220void PCondVar::Signal(void) 221{ 222#if (!defined(_WIN32)) 223 pthread_cond_broadcast(&cond); 224#else 225 WakeAllConditionVariable(&cond); 226#endif 227} 228 229 230// Initialise a semphore. Tries to create an unnamed semaphore if 231// it can but tries a named semaphore if it can't. Mac OS X only 232// supports named semaphores. 233// The semaphore is initialised with a count of zero. 234PSemaphore::PSemaphore() 235{ 236#if (!defined(_WIN32)) 237 sema = 0; 238 isLocal = true; 239#else 240 sema = NULL; 241#endif 242} 243 244PSemaphore::~PSemaphore() 245{ 246#if (!defined(_WIN32)) 247 if (sema && isLocal) sem_destroy(sema); 248 else if (sema && !isLocal) sem_close(sema); 249#else 250 if (sema != NULL) CloseHandle(sema); 251#endif 252} 253 254bool PSemaphore::Init(unsigned init, unsigned max) 255{ 256#if (!defined(_WIN32)) 257 isLocal = true; 258 if (sem_init(&localSema, 0, init) == 0) { 259 sema = &localSema; 260 return true; 261 } 262#if (defined(__CYGWIN__)) 263 // Cygwin doesn't define sem_unlink but that doesn't matter 264 // since sem_init works. 265 sema = 0; 266 return false; 267#else 268 isLocal = false; 269 char semname[30]; 270 static int count=0; 271 sprintf(semname, "poly%0d-%0d", (int)getpid(), count++); 272 sema = sem_open(semname, O_CREAT|O_EXCL, 00666, init); 273 if (sema == (sem_t*)SEM_FAILED) { 274 sema = 0; 275 return false; 276 } 277 sem_unlink(semname); 278 return true; 279#endif 280#else 281 sema = CreateSemaphore(NULL, init, max, NULL); 282 return sema != NULL; 283#endif 284} 285 286bool PSemaphore::Wait(void) 287{ 288#if (!defined(_WIN32)) 289 // Wait until the semaphore is signalled. A Unix signal may interrupt 290 // it so we need to retry in that case. 291 while (sem_wait(sema) == -1) 292 { 293 if (errno != EINTR) 294 return false; 295 } 296 return true; 297#else 298 return WaitForSingleObject(sema, INFINITE) == WAIT_OBJECT_0; 299#endif 300} 301 302void PSemaphore::Signal(void) 303{ 304#if (!defined(_WIN32)) 305 sem_post(sema); 306#else 307 ReleaseSemaphore(sema, 1, NULL); 308#endif 309} 310 311 312 313