2025-06-03 23:10:15 -07:00
|
|
|
/* Copyright (C) 2025 Wildfire Games.
|
2009-04-18 10:00:33 -07:00
|
|
|
*
|
2010-02-08 08:23:39 -08:00
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining
|
|
|
|
|
* a copy of this software and associated documentation files (the
|
|
|
|
|
* "Software"), to deal in the Software without restriction, including
|
|
|
|
|
* without limitation the rights to use, copy, modify, merge, publish,
|
|
|
|
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
|
|
|
|
* permit persons to whom the Software is furnished to do so, subject to
|
|
|
|
|
* the following conditions:
|
2016-11-23 05:02:58 -08:00
|
|
|
*
|
2010-02-08 08:23:39 -08:00
|
|
|
* The above copyright notice and this permission notice shall be included
|
|
|
|
|
* in all copies or substantial portions of the Software.
|
2016-11-23 05:02:58 -08:00
|
|
|
*
|
2010-02-08 08:23:39 -08:00
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
|
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
|
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
|
|
|
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
|
|
|
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
|
|
|
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
|
|
|
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
2009-04-18 10:00:33 -07:00
|
|
|
*/
|
|
|
|
|
|
2009-04-18 10:51:05 -07:00
|
|
|
/*
|
|
|
|
|
* emulate pthreads on Windows.
|
2006-04-23 16:14:18 -07:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "precompiled.h"
|
2010-03-01 06:52:58 -08:00
|
|
|
#include "lib/sysdep/os/win/wposix/wpthread.h"
|
2006-04-23 16:14:18 -07:00
|
|
|
|
|
|
|
|
#include <new>
|
|
|
|
|
#include <process.h>
|
|
|
|
|
|
2011-03-21 10:53:13 -07:00
|
|
|
#include "lib/posix/posix_filesystem.h" // O_CREAT
|
2010-03-01 06:52:58 -08:00
|
|
|
#include "lib/sysdep/os/win/wposix/wposix_internal.h"
|
|
|
|
|
#include "lib/sysdep/os/win/wseh.h" // wseh_ExceptionFilter
|
2007-01-01 13:25:47 -08:00
|
|
|
|
2025-07-15 10:24:51 -07:00
|
|
|
#include <ctime>
|
|
|
|
|
|
2023-07-02 13:28:50 -07:00
|
|
|
namespace
|
|
|
|
|
{
|
2006-04-23 16:14:18 -07:00
|
|
|
|
2023-07-02 13:28:50 -07:00
|
|
|
HANDLE HANDLE_from_pthread(pthread_t p)
|
2006-04-23 16:14:18 -07:00
|
|
|
{
|
|
|
|
|
return (HANDLE)((char*)0 + p);
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-02 13:28:50 -07:00
|
|
|
pthread_t pthread_from_HANDLE(HANDLE h)
|
2006-04-23 16:14:18 -07:00
|
|
|
{
|
|
|
|
|
return (pthread_t)(uintptr_t)h;
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-02 13:28:50 -07:00
|
|
|
HANDLE GenerateCurrentThreadUniqueHandle()
|
2011-10-15 13:12:59 -07:00
|
|
|
{
|
2023-07-02 13:28:50 -07:00
|
|
|
// Non-pseudo handle so that pthread_self value is unique for each thread.
|
|
|
|
|
// We can't use GetCurrentThread because it returns the same special
|
|
|
|
|
// constant for each thread. According to:
|
|
|
|
|
// https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getcurrentthread
|
|
|
|
|
// The function cannot be used by one thread to create a handle that can
|
|
|
|
|
// be used by other threads to refer to the first thread. The handle is
|
|
|
|
|
// always interpreted as referring to the thread that is using it. A
|
|
|
|
|
// thread can create a "real" handle to itself that can be used by other
|
|
|
|
|
// threads, or inherited by other processes, by specifying the pseudo
|
|
|
|
|
// handle as the source handle in a call to the DuplicateHandle function.
|
|
|
|
|
HANDLE handle;
|
2011-10-15 13:12:59 -07:00
|
|
|
// (we leave it to the OS to clean these up at process exit - threads are not created often)
|
2023-07-02 13:28:50 -07:00
|
|
|
WARN_IF_FALSE(DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &handle, 0, FALSE, DUPLICATE_SAME_ACCESS));
|
|
|
|
|
return handle;
|
2011-10-15 13:12:59 -07:00
|
|
|
}
|
|
|
|
|
|
2023-07-02 13:28:50 -07:00
|
|
|
HANDLE GetCurrentThreadUniqueHandle()
|
|
|
|
|
{
|
|
|
|
|
thread_local HANDLE handle = GenerateCurrentThreadUniqueHandle();
|
|
|
|
|
return handle;
|
|
|
|
|
}
|
2011-10-15 13:12:59 -07:00
|
|
|
|
2023-07-02 13:28:50 -07:00
|
|
|
} // anonymous namespace
|
2011-10-15 13:12:59 -07:00
|
|
|
|
2010-10-31 15:26:41 -07:00
|
|
|
int pthread_equal(pthread_t t1, pthread_t t2)
|
|
|
|
|
{
|
|
|
|
|
return t1 == t2;
|
|
|
|
|
}
|
|
|
|
|
|
2009-11-03 13:46:35 -08:00
|
|
|
pthread_t pthread_self()
|
2006-04-23 16:14:18 -07:00
|
|
|
{
|
2023-07-02 13:28:50 -07:00
|
|
|
return pthread_from_HANDLE(GetCurrentThreadUniqueHandle());
|
2006-04-23 16:14:18 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int pthread_getschedparam(pthread_t thread, int* policy, struct sched_param* param)
|
|
|
|
|
{
|
|
|
|
|
if(policy)
|
|
|
|
|
{
|
|
|
|
|
DWORD pc = GetPriorityClass(GetCurrentProcess());
|
|
|
|
|
*policy = (pc >= HIGH_PRIORITY_CLASS)? SCHED_FIFO : SCHED_RR;
|
|
|
|
|
}
|
|
|
|
|
if(param)
|
|
|
|
|
{
|
|
|
|
|
const HANDLE hThread = HANDLE_from_pthread(thread);
|
|
|
|
|
param->sched_priority = GetThreadPriority(hThread);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int pthread_setschedparam(pthread_t thread, int policy, const struct sched_param* param)
|
|
|
|
|
{
|
|
|
|
|
const int pri = param->sched_priority;
|
|
|
|
|
|
|
|
|
|
// additional boost for policy == SCHED_FIFO
|
|
|
|
|
DWORD pri_class = NORMAL_PRIORITY_CLASS;
|
|
|
|
|
if(policy == SCHED_FIFO)
|
|
|
|
|
{
|
|
|
|
|
pri_class = HIGH_PRIORITY_CLASS;
|
|
|
|
|
if(pri == 2)
|
|
|
|
|
pri_class = REALTIME_PRIORITY_CLASS;
|
|
|
|
|
}
|
|
|
|
|
SetPriorityClass(GetCurrentProcess(), pri_class);
|
|
|
|
|
|
|
|
|
|
// choose fixed Windows values from pri
|
|
|
|
|
const HANDLE hThread = HANDLE_from_pthread(thread);
|
|
|
|
|
SetThreadPriority(hThread, pri);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2006-06-11 10:16:24 -07:00
|
|
|
//-----------------------------------------------------------------------------
|
2006-04-23 16:14:18 -07:00
|
|
|
// thread-local storage
|
2006-06-11 10:16:24 -07:00
|
|
|
//-----------------------------------------------------------------------------
|
2006-04-23 16:14:18 -07:00
|
|
|
|
|
|
|
|
// minimum amount of TLS slots every Windows version provides;
|
|
|
|
|
// used to validate indices.
|
had to remove uint and ulong from lib/types.h due to conflict with other library.
this snowballed into a massive search+destroy of the hodgepodge of
mostly equivalent types we had in use (int, uint, unsigned, unsigned
int, i32, u32, ulong, uintN).
it is more efficient to use 64-bit types in 64-bit mode, so the
preferred default is size_t (for anything remotely resembling a size or
index). tile coordinates are ssize_t to allow more efficient conversion
to/from floating point. flags are int because we almost never need more
than 15 distinct bits, bit test/set is not slower and int is fastest to
type. finally, some data that is pretty much directly passed to OpenGL
is now typed accordingly.
after several hours, the code now requires fewer casts and less
guesswork.
other changes:
- unit and player IDs now have an "invalid id" constant in the
respective class to avoid casting and -1
- fix some endian/64-bit bugs in the map (un)packing. added a
convenience function to write/read a size_t.
- ia32: change CPUID interface to allow passing in ecx (required for
cache topology detection, which I need at work). remove some unneeded
functions from asm, replace with intrinsics where possible.
This was SVN commit r5942.
2008-05-11 11:48:32 -07:00
|
|
|
static const size_t TLS_LIMIT = 64;
|
2006-04-23 16:14:18 -07:00
|
|
|
|
|
|
|
|
// rationale: don't use an array of dtors for every possible TLS slot.
|
|
|
|
|
// other DLLs may allocate any number of them in their DllMain, so the
|
|
|
|
|
// array would have to be quite large. instead, store both key and dtor -
|
|
|
|
|
// we are thus limited only by pthread_key_create calls (which we control).
|
had to remove uint and ulong from lib/types.h due to conflict with other library.
this snowballed into a massive search+destroy of the hodgepodge of
mostly equivalent types we had in use (int, uint, unsigned, unsigned
int, i32, u32, ulong, uintN).
it is more efficient to use 64-bit types in 64-bit mode, so the
preferred default is size_t (for anything remotely resembling a size or
index). tile coordinates are ssize_t to allow more efficient conversion
to/from floating point. flags are int because we almost never need more
than 15 distinct bits, bit test/set is not slower and int is fastest to
type. finally, some data that is pretty much directly passed to OpenGL
is now typed accordingly.
after several hours, the code now requires fewer casts and less
guesswork.
other changes:
- unit and player IDs now have an "invalid id" constant in the
respective class to avoid casting and -1
- fix some endian/64-bit bugs in the map (un)packing. added a
convenience function to write/read a size_t.
- ia32: change CPUID interface to allow passing in ecx (required for
cache topology detection, which I need at work). remove some unneeded
functions from asm, replace with intrinsics where possible.
This was SVN commit r5942.
2008-05-11 11:48:32 -07:00
|
|
|
static const size_t MAX_DTORS = 4;
|
2006-04-23 16:14:18 -07:00
|
|
|
static struct
|
|
|
|
|
{
|
|
|
|
|
pthread_key_t key;
|
2024-09-16 14:02:32 -07:00
|
|
|
using dtortype = void (*)(void*);
|
|
|
|
|
std::atomic<dtortype> dtor{ nullptr };
|
2006-04-23 16:14:18 -07:00
|
|
|
}
|
|
|
|
|
dtors[MAX_DTORS];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int pthread_key_create(pthread_key_t* key, void (*dtor)(void*))
|
|
|
|
|
{
|
|
|
|
|
DWORD idx = TlsAlloc();
|
|
|
|
|
if(idx == TLS_OUT_OF_INDEXES)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
2011-04-30 06:01:45 -07:00
|
|
|
ENSURE(idx < TLS_LIMIT);
|
2006-04-23 16:14:18 -07:00
|
|
|
*key = (pthread_key_t)idx;
|
|
|
|
|
|
|
|
|
|
// acquire a free dtor slot
|
had to remove uint and ulong from lib/types.h due to conflict with other library.
this snowballed into a massive search+destroy of the hodgepodge of
mostly equivalent types we had in use (int, uint, unsigned, unsigned
int, i32, u32, ulong, uintN).
it is more efficient to use 64-bit types in 64-bit mode, so the
preferred default is size_t (for anything remotely resembling a size or
index). tile coordinates are ssize_t to allow more efficient conversion
to/from floating point. flags are int because we almost never need more
than 15 distinct bits, bit test/set is not slower and int is fastest to
type. finally, some data that is pretty much directly passed to OpenGL
is now typed accordingly.
after several hours, the code now requires fewer casts and less
guesswork.
other changes:
- unit and player IDs now have an "invalid id" constant in the
respective class to avoid casting and -1
- fix some endian/64-bit bugs in the map (un)packing. added a
convenience function to write/read a size_t.
- ia32: change CPUID interface to allow passing in ecx (required for
cache topology detection, which I need at work). remove some unneeded
functions from asm, replace with intrinsics where possible.
This was SVN commit r5942.
2008-05-11 11:48:32 -07:00
|
|
|
size_t i;
|
2006-04-23 16:14:18 -07:00
|
|
|
for(i = 0; i < MAX_DTORS; i++)
|
|
|
|
|
{
|
2024-09-16 14:02:32 -07:00
|
|
|
void (*zero)(void*) { nullptr };
|
|
|
|
|
if(dtors[i].dtor.compare_exchange_strong(zero, dtor))
|
2006-04-23 16:14:18 -07:00
|
|
|
goto have_slot;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// not enough slots; we have a valid key, but its dtor won't be called.
|
2011-05-03 05:38:42 -07:00
|
|
|
WARN_IF_ERR(ERR::LIMIT);
|
2006-04-23 16:14:18 -07:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
have_slot:
|
|
|
|
|
dtors[i].key = *key;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int pthread_key_delete(pthread_key_t key)
|
|
|
|
|
{
|
|
|
|
|
DWORD idx = (DWORD)key;
|
2011-04-30 06:01:45 -07:00
|
|
|
ENSURE(idx < TLS_LIMIT);
|
2006-04-23 16:14:18 -07:00
|
|
|
|
|
|
|
|
BOOL ret = TlsFree(idx);
|
2011-04-30 06:01:45 -07:00
|
|
|
ENSURE(ret != 0);
|
2006-04-23 16:14:18 -07:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void* pthread_getspecific(pthread_key_t key)
|
|
|
|
|
{
|
|
|
|
|
DWORD idx = (DWORD)key;
|
2011-04-30 06:01:45 -07:00
|
|
|
ENSURE(idx < TLS_LIMIT);
|
2006-04-23 16:14:18 -07:00
|
|
|
|
|
|
|
|
// TlsGetValue sets last error to 0 on success (boo).
|
|
|
|
|
// we don't want this to hide previous errors, so it's restored below.
|
|
|
|
|
DWORD last_err = GetLastError();
|
|
|
|
|
|
|
|
|
|
void* data = TlsGetValue(idx);
|
|
|
|
|
|
|
|
|
|
// no error
|
|
|
|
|
if(GetLastError() == 0)
|
|
|
|
|
{
|
|
|
|
|
// we care about performance here. SetLastError is low overhead,
|
|
|
|
|
// but last error = 0 is expected.
|
|
|
|
|
if(last_err != 0)
|
|
|
|
|
SetLastError(last_err);
|
|
|
|
|
}
|
|
|
|
|
else
|
2011-05-03 05:38:42 -07:00
|
|
|
WARN_IF_ERR(ERR::FAIL);
|
2006-04-23 16:14:18 -07:00
|
|
|
|
|
|
|
|
return data;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int pthread_setspecific(pthread_key_t key, const void* value)
|
|
|
|
|
{
|
|
|
|
|
DWORD idx = (DWORD)key;
|
2011-04-30 06:01:45 -07:00
|
|
|
ENSURE(idx < TLS_LIMIT);
|
2006-04-23 16:14:18 -07:00
|
|
|
|
|
|
|
|
BOOL ret = TlsSetValue(idx, (void*)value);
|
2011-04-30 06:01:45 -07:00
|
|
|
ENSURE(ret != 0);
|
2006-04-23 16:14:18 -07:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void call_tls_dtors()
|
|
|
|
|
{
|
|
|
|
|
again:
|
|
|
|
|
bool had_valid_tls = false;
|
|
|
|
|
|
|
|
|
|
// for each registered dtor: (call order unspecified by SUSv3)
|
had to remove uint and ulong from lib/types.h due to conflict with other library.
this snowballed into a massive search+destroy of the hodgepodge of
mostly equivalent types we had in use (int, uint, unsigned, unsigned
int, i32, u32, ulong, uintN).
it is more efficient to use 64-bit types in 64-bit mode, so the
preferred default is size_t (for anything remotely resembling a size or
index). tile coordinates are ssize_t to allow more efficient conversion
to/from floating point. flags are int because we almost never need more
than 15 distinct bits, bit test/set is not slower and int is fastest to
type. finally, some data that is pretty much directly passed to OpenGL
is now typed accordingly.
after several hours, the code now requires fewer casts and less
guesswork.
other changes:
- unit and player IDs now have an "invalid id" constant in the
respective class to avoid casting and -1
- fix some endian/64-bit bugs in the map (un)packing. added a
convenience function to write/read a size_t.
- ia32: change CPUID interface to allow passing in ecx (required for
cache topology detection, which I need at work). remove some unneeded
functions from asm, replace with intrinsics where possible.
This was SVN commit r5942.
2008-05-11 11:48:32 -07:00
|
|
|
for(size_t i = 0; i < MAX_DTORS; i++)
|
2006-04-23 16:14:18 -07:00
|
|
|
{
|
|
|
|
|
// is slot #i in use?
|
|
|
|
|
void (*dtor)(void*) = dtors[i].dtor;
|
|
|
|
|
if(!dtor)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
// clear slot and call dtor with its previous value.
|
|
|
|
|
const pthread_key_t key = dtors[i].key;
|
|
|
|
|
void* tls = pthread_getspecific(key);
|
|
|
|
|
if(tls)
|
|
|
|
|
{
|
2011-05-03 05:38:42 -07:00
|
|
|
WARN_IF_ERR(pthread_setspecific(key, 0));
|
2006-04-23 16:14:18 -07:00
|
|
|
|
|
|
|
|
dtor(tls);
|
|
|
|
|
had_valid_tls = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// rationale: SUSv3 says we're allowed to loop infinitely. we do so to
|
|
|
|
|
// expose any dtor bugs - this shouldn't normally happen.
|
|
|
|
|
if(had_valid_tls)
|
|
|
|
|
goto again;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2006-06-11 10:16:24 -07:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// mutex
|
|
|
|
|
//-----------------------------------------------------------------------------
|
2006-04-23 16:14:18 -07:00
|
|
|
|
|
|
|
|
// rationale: CRITICAL_SECTIONS have less overhead than Win32 Mutex.
|
|
|
|
|
// disadvantage is that pthread_mutex_timedlock isn't supported, but
|
|
|
|
|
// the user can switch to semaphores if this facility is important.
|
|
|
|
|
|
|
|
|
|
// DeleteCriticalSection currently doesn't complain if we double-free
|
|
|
|
|
// (e.g. user calls destroy() and static initializer atexit runs),
|
|
|
|
|
// and dox are ambiguous.
|
|
|
|
|
|
|
|
|
|
// note: pthread_mutex_t must not be an opaque struct, because the
|
|
|
|
|
// initializer returns pthread_mutex_t directly and CRITICAL_SECTIONS
|
|
|
|
|
// shouldn't be copied.
|
|
|
|
|
//
|
2010-07-08 03:18:42 -07:00
|
|
|
// note: we use wutil_Allocate instead of new because the (no longer extant)
|
2008-01-19 03:33:11 -08:00
|
|
|
// memory manager used a pthread_mutex.
|
2006-04-23 16:14:18 -07:00
|
|
|
|
2011-09-10 15:51:51 -07:00
|
|
|
|
2025-06-03 23:10:15 -07:00
|
|
|
int pthread_mutexattr_init(pthread_mutexattr_t*)
|
2011-09-10 15:51:51 -07:00
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-03 23:10:15 -07:00
|
|
|
int pthread_mutexattr_destroy(pthread_mutexattr_t*)
|
2011-09-10 15:51:51 -07:00
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-03 23:10:15 -07:00
|
|
|
int pthread_mutexattr_gettype(const pthread_mutexattr_t*, int* type)
|
2011-09-10 15:51:51 -07:00
|
|
|
{
|
|
|
|
|
*type = PTHREAD_MUTEX_RECURSIVE;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-03 23:10:15 -07:00
|
|
|
int pthread_mutexattr_settype(pthread_mutexattr_t*, int type)
|
2011-09-10 15:51:51 -07:00
|
|
|
{
|
|
|
|
|
return (type == PTHREAD_MUTEX_RECURSIVE)? 0 : -ENOSYS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2008-03-25 14:20:43 -07:00
|
|
|
static CRITICAL_SECTION* CRITICAL_SECTION_from_pthread_mutex_t(pthread_mutex_t* m)
|
|
|
|
|
{
|
|
|
|
|
if(!m)
|
|
|
|
|
{
|
2011-05-04 05:10:17 -07:00
|
|
|
DEBUG_WARN_ERR(ERR::LOGIC);
|
2008-03-25 14:20:43 -07:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
return (CRITICAL_SECTION*)*m;
|
|
|
|
|
}
|
|
|
|
|
|
2006-04-23 16:14:18 -07:00
|
|
|
pthread_mutex_t pthread_mutex_initializer()
|
|
|
|
|
{
|
2010-07-08 03:18:42 -07:00
|
|
|
CRITICAL_SECTION* cs = (CRITICAL_SECTION*)wutil_Allocate(sizeof(CRITICAL_SECTION));
|
2006-04-23 16:14:18 -07:00
|
|
|
InitializeCriticalSection(cs);
|
|
|
|
|
return (pthread_mutex_t)cs;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int pthread_mutex_destroy(pthread_mutex_t* m)
|
|
|
|
|
{
|
2008-03-25 14:20:43 -07:00
|
|
|
CRITICAL_SECTION* cs = CRITICAL_SECTION_from_pthread_mutex_t(m);
|
|
|
|
|
if(!cs)
|
|
|
|
|
return -1;
|
2006-04-23 16:14:18 -07:00
|
|
|
DeleteCriticalSection(cs);
|
2010-07-08 03:18:42 -07:00
|
|
|
wutil_Free(cs);
|
2008-03-25 14:20:43 -07:00
|
|
|
*m = 0; // cause double-frees to be noticed
|
2006-04-23 16:14:18 -07:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int pthread_mutex_init(pthread_mutex_t* m, const pthread_mutexattr_t*)
|
|
|
|
|
{
|
|
|
|
|
*m = pthread_mutex_initializer();
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int pthread_mutex_lock(pthread_mutex_t* m)
|
|
|
|
|
{
|
2008-03-25 14:20:43 -07:00
|
|
|
CRITICAL_SECTION* cs = CRITICAL_SECTION_from_pthread_mutex_t(m);
|
|
|
|
|
if(!cs)
|
|
|
|
|
return -1;
|
2006-04-23 16:14:18 -07:00
|
|
|
EnterCriticalSection(cs);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int pthread_mutex_trylock(pthread_mutex_t* m)
|
|
|
|
|
{
|
2008-03-25 14:20:43 -07:00
|
|
|
CRITICAL_SECTION* cs = CRITICAL_SECTION_from_pthread_mutex_t(m);
|
|
|
|
|
if(!cs)
|
|
|
|
|
return -1;
|
|
|
|
|
const BOOL successfullyEnteredOrAlreadyOwns = TryEnterCriticalSection(cs);
|
|
|
|
|
return successfullyEnteredOrAlreadyOwns? 0 : -1;
|
2006-04-23 16:14:18 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int pthread_mutex_unlock(pthread_mutex_t* m)
|
|
|
|
|
{
|
2008-03-25 14:20:43 -07:00
|
|
|
CRITICAL_SECTION* cs = CRITICAL_SECTION_from_pthread_mutex_t(m);
|
|
|
|
|
if(!cs)
|
|
|
|
|
return -1;
|
2006-04-23 16:14:18 -07:00
|
|
|
LeaveCriticalSection(cs);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// not implemented - pthread_mutex is based on CRITICAL_SECTION,
|
|
|
|
|
// which doesn't support timeouts. use sem_timedwait instead.
|
2025-06-03 05:13:41 -07:00
|
|
|
int pthread_mutex_timedlock(pthread_mutex_t*, const struct timespec* /*abs_timeout*/)
|
2006-04-23 16:14:18 -07:00
|
|
|
{
|
|
|
|
|
return -ENOSYS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2006-06-11 10:16:24 -07:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// semaphore
|
|
|
|
|
//-----------------------------------------------------------------------------
|
2006-04-23 16:14:18 -07:00
|
|
|
|
|
|
|
|
static HANDLE HANDLE_from_sem_t(sem_t* sem)
|
|
|
|
|
{
|
|
|
|
|
return (HANDLE)*sem;
|
|
|
|
|
}
|
|
|
|
|
|
2007-09-02 14:44:56 -07:00
|
|
|
sem_t* sem_open(const char* name, int oflag, ...)
|
|
|
|
|
{
|
2007-12-23 04:20:37 -08:00
|
|
|
WinScopedPreserveLastError s;
|
|
|
|
|
|
2007-09-02 14:44:56 -07:00
|
|
|
const bool create = (oflag & O_CREAT) != 0;
|
|
|
|
|
const bool exclusive = (oflag & O_EXCL) != 0;
|
|
|
|
|
|
|
|
|
|
// SUSv3 parameter requirements:
|
2011-04-30 06:01:45 -07:00
|
|
|
ENSURE(name[0] == '/');
|
|
|
|
|
ENSURE((oflag & ~(O_CREAT|O_EXCL)) == 0); // no other bits
|
|
|
|
|
ENSURE(!exclusive || create); // excl implies creat
|
2007-09-02 14:44:56 -07:00
|
|
|
|
|
|
|
|
// if creating, get additional parameters
|
|
|
|
|
unsigned initialValue = 0;
|
|
|
|
|
if(create)
|
|
|
|
|
{
|
|
|
|
|
va_list args;
|
|
|
|
|
va_start(args, oflag);
|
|
|
|
|
const mode_t mode = va_arg(args, mode_t);
|
|
|
|
|
initialValue = va_arg(args, unsigned);
|
|
|
|
|
va_end(args);
|
2011-04-30 06:01:45 -07:00
|
|
|
ENSURE(mode == 0700 && "this implementation ignores mode_t");
|
2007-09-02 14:44:56 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// create or open
|
|
|
|
|
SetLastError(0);
|
|
|
|
|
const LONG maxValue = 0x7fffffff;
|
|
|
|
|
const HANDLE hSemaphore = CreateSemaphore(0, (LONG)initialValue, maxValue, 0);
|
|
|
|
|
if(hSemaphore == 0)
|
|
|
|
|
return SEM_FAILED;
|
|
|
|
|
const bool existed = (GetLastError() == ERROR_ALREADY_EXISTS);
|
|
|
|
|
|
|
|
|
|
// caller insisted on creating anew, but it already existed
|
|
|
|
|
if(exclusive && existed)
|
|
|
|
|
{
|
|
|
|
|
CloseHandle(hSemaphore);
|
|
|
|
|
errno = EEXIST;
|
|
|
|
|
return SEM_FAILED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// caller wanted to open semaphore, but it didn't exist
|
|
|
|
|
if(!create && !existed)
|
|
|
|
|
{
|
|
|
|
|
CloseHandle(hSemaphore);
|
|
|
|
|
errno = ENOENT;
|
|
|
|
|
return SEM_FAILED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// we have to return a pointer to sem_t, and the sem_init interface
|
|
|
|
|
// requires sem_t to be usable as the handle, so we'll have to
|
|
|
|
|
// allocate memory here (ugh).
|
|
|
|
|
sem_t* sem = new sem_t;
|
|
|
|
|
*sem = (sem_t)hSemaphore;
|
|
|
|
|
return sem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int sem_close(sem_t* sem)
|
|
|
|
|
{
|
|
|
|
|
// jw: not sure if this is correct according to SUSv3, but it's
|
|
|
|
|
// certainly enough for MessagePasserImpl's needs.
|
|
|
|
|
sem_destroy(sem);
|
|
|
|
|
delete sem;
|
|
|
|
|
return 0; // success
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-03 05:13:41 -07:00
|
|
|
int sem_unlink(const char* /*name*/)
|
2007-09-02 14:44:56 -07:00
|
|
|
{
|
|
|
|
|
// see sem_close
|
|
|
|
|
return 0; // success
|
|
|
|
|
}
|
|
|
|
|
|
2006-04-23 16:14:18 -07:00
|
|
|
int sem_init(sem_t* sem, int pshared, unsigned value)
|
|
|
|
|
{
|
|
|
|
|
SECURITY_ATTRIBUTES sec = { sizeof(SECURITY_ATTRIBUTES) };
|
|
|
|
|
sec.bInheritHandle = (BOOL)pshared;
|
|
|
|
|
HANDLE h = CreateSemaphore(&sec, (LONG)value, 0x7fffffff, 0);
|
|
|
|
|
WARN_IF_FALSE(h);
|
2007-09-02 14:44:56 -07:00
|
|
|
*sem = (sem_t)h;
|
2006-04-23 16:14:18 -07:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2007-09-02 14:44:56 -07:00
|
|
|
int sem_destroy(sem_t* sem)
|
2006-04-23 16:14:18 -07:00
|
|
|
{
|
|
|
|
|
HANDLE h = HANDLE_from_sem_t(sem);
|
2007-09-02 14:44:56 -07:00
|
|
|
WARN_IF_FALSE(CloseHandle(h));
|
2006-04-23 16:14:18 -07:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2007-09-02 14:44:56 -07:00
|
|
|
int sem_post(sem_t* sem)
|
2006-04-23 16:14:18 -07:00
|
|
|
{
|
|
|
|
|
HANDLE h = HANDLE_from_sem_t(sem);
|
2007-09-02 14:44:56 -07:00
|
|
|
WARN_IF_FALSE(ReleaseSemaphore(h, 1, 0));
|
2006-04-23 16:14:18 -07:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2007-09-02 14:44:56 -07:00
|
|
|
int sem_wait(sem_t* sem)
|
2006-04-23 16:14:18 -07:00
|
|
|
{
|
|
|
|
|
HANDLE h = HANDLE_from_sem_t(sem);
|
2007-09-02 14:44:56 -07:00
|
|
|
DWORD ret = WaitForSingleObject(h, INFINITE);
|
2011-04-30 06:01:45 -07:00
|
|
|
ENSURE(ret == WAIT_OBJECT_0);
|
2006-04-23 16:14:18 -07:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// wait until semaphore is locked or a message arrives. non-portable.
|
|
|
|
|
//
|
|
|
|
|
// background: on Win32, UI threads must periodically pump messages, or
|
|
|
|
|
// else deadlock may result (see WaitForSingleObject docs). that entails
|
|
|
|
|
// avoiding any blocking functions. when event waiting is needed,
|
|
|
|
|
// one cheap workaround would be to time out periodically and pump messages.
|
|
|
|
|
// that would work, but either wastes CPU time waiting, or introduces
|
|
|
|
|
// message latency. to avoid this, we provide an API similar to sem_wait and
|
|
|
|
|
// sem_timedwait that gives MsgWaitForMultipleObjects functionality.
|
|
|
|
|
//
|
|
|
|
|
// return value: 0 if the semaphore has been locked (SUS terminology),
|
|
|
|
|
// -1 otherwise. errno differentiates what happened: ETIMEDOUT if a
|
|
|
|
|
// message arrived (this is to ease switching between message waiting and
|
|
|
|
|
// periodic timeout), or an error indication.
|
|
|
|
|
int sem_msgwait_np(sem_t* sem)
|
|
|
|
|
{
|
|
|
|
|
HANDLE h = HANDLE_from_sem_t(sem);
|
|
|
|
|
DWORD ret = MsgWaitForMultipleObjects(1, &h, FALSE, INFINITE, QS_ALLEVENTS);
|
|
|
|
|
// semaphore is signalled
|
|
|
|
|
if(ret == WAIT_OBJECT_0)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
// something else:
|
|
|
|
|
// .. message came up
|
|
|
|
|
if(ret == WAIT_OBJECT_0+1)
|
|
|
|
|
errno = ETIMEDOUT;
|
|
|
|
|
// .. error
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
errno = EINVAL;
|
2011-05-03 05:38:42 -07:00
|
|
|
WARN_IF_ERR(ERR::FAIL);
|
2006-04-23 16:14:18 -07:00
|
|
|
}
|
|
|
|
|
return -1;
|
2006-06-11 10:16:24 -07:00
|
|
|
}
|
2006-04-23 16:14:18 -07:00
|
|
|
|
2006-06-11 10:16:24 -07:00
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// threads
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
// _beginthreadex cannot call the user's thread function directly due to
|
|
|
|
|
// differences in calling convention; we need to pass its address and
|
|
|
|
|
// the user-specified data pointer to our trampoline.
|
|
|
|
|
//
|
|
|
|
|
// rationale:
|
|
|
|
|
// - a local variable in pthread_create isn't safe because the
|
|
|
|
|
// new thread might not start before pthread_create returns.
|
|
|
|
|
// - using one static FuncAndArg protected by critical section doesn't
|
2010-07-08 03:18:42 -07:00
|
|
|
// work. wutil_Lock allows recursive locking, so if creating 2 threads,
|
2006-06-11 10:16:24 -07:00
|
|
|
// the parent thread may create both without being stopped and thus
|
|
|
|
|
// stomp on the first thread's func_and_arg.
|
|
|
|
|
// - blocking pthread_create until the trampoline has latched func_and_arg
|
2007-01-07 08:50:36 -08:00
|
|
|
// would work. this is a bit easier to understand than nonrecursive CS.
|
|
|
|
|
// deadlock is impossible because thread_start allows the parent to
|
|
|
|
|
// continue before doing anything dangerous. however, this requires
|
|
|
|
|
// initializing a semaphore, which leads to init order problems.
|
|
|
|
|
// - stashing func and arg in TLS would work, but it is a very limited
|
|
|
|
|
// resource and __declspec(thread) is documented as possibly
|
|
|
|
|
// interfering with delay loading.
|
|
|
|
|
// - heap allocations are the obvious safe solution. we'd like to
|
|
|
|
|
// minimize them, but it's the least painful way.
|
2006-06-11 10:16:24 -07:00
|
|
|
|
|
|
|
|
struct FuncAndArg
|
|
|
|
|
{
|
|
|
|
|
void* (*func)(void*);
|
|
|
|
|
void* arg;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// bridge calling conventions required by _beginthreadex and POSIX.
|
|
|
|
|
static unsigned __stdcall thread_start(void* param)
|
|
|
|
|
{
|
|
|
|
|
const FuncAndArg* func_and_arg = (const FuncAndArg*)param;
|
|
|
|
|
void* (*func)(void*) = func_and_arg->func;
|
|
|
|
|
void* arg = func_and_arg->arg;
|
2010-07-08 03:18:42 -07:00
|
|
|
wutil_Free(param);
|
2006-06-11 10:16:24 -07:00
|
|
|
|
2007-06-08 10:44:24 -07:00
|
|
|
void* ret = 0;
|
|
|
|
|
__try
|
|
|
|
|
{
|
|
|
|
|
ret = func(arg);
|
|
|
|
|
call_tls_dtors();
|
|
|
|
|
}
|
|
|
|
|
__except(wseh_ExceptionFilter(GetExceptionInformation()))
|
|
|
|
|
{
|
|
|
|
|
ret = 0;
|
|
|
|
|
}
|
2006-06-11 10:16:24 -07:00
|
|
|
|
|
|
|
|
return (unsigned)(uintptr_t)ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2025-06-03 05:13:41 -07:00
|
|
|
int pthread_create(pthread_t* thread_id, const void* /*attr*/, void* (*func)(void*), void* arg)
|
2006-06-11 10:16:24 -07:00
|
|
|
{
|
2007-01-07 08:50:36 -08:00
|
|
|
// notes:
|
2010-07-08 03:18:42 -07:00
|
|
|
// - use wutil_Allocate instead of the normal heap because we /might/
|
2007-01-07 08:50:36 -08:00
|
|
|
// potentially be called before _cinit.
|
|
|
|
|
// - placement new is more trouble than it's worth
|
|
|
|
|
// (see REDEFINED_NEW), so we don't bother with a ctor.
|
2010-07-08 03:18:42 -07:00
|
|
|
FuncAndArg* func_and_arg = (FuncAndArg*)wutil_Allocate(sizeof(FuncAndArg));
|
2007-01-07 08:50:36 -08:00
|
|
|
if(!func_and_arg)
|
|
|
|
|
{
|
2011-05-03 05:38:42 -07:00
|
|
|
WARN_IF_ERR(ERR::NO_MEM);
|
2007-01-07 08:50:36 -08:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
func_and_arg->func = func;
|
|
|
|
|
func_and_arg->arg = arg;
|
2006-06-11 10:16:24 -07:00
|
|
|
|
|
|
|
|
// _beginthreadex has more overhead and no value added vs.
|
|
|
|
|
// CreateThread, but it avoids small memory leaks in
|
|
|
|
|
// ExitThread when using the statically-linked CRT (-> MSDN).
|
2011-10-15 13:12:59 -07:00
|
|
|
HANDLE hThread = (HANDLE)_beginthreadex(0, 0, thread_start, func_and_arg, 0, 0);
|
|
|
|
|
if(!hThread)
|
2006-06-11 10:16:24 -07:00
|
|
|
{
|
2011-05-03 05:38:42 -07:00
|
|
|
WARN_IF_ERR(ERR::FAIL);
|
2006-06-11 10:16:24 -07:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SUSv3 doesn't specify whether this is optional - go the safe route.
|
|
|
|
|
if(thread_id)
|
2011-10-15 13:12:59 -07:00
|
|
|
*thread_id = pthread_from_HANDLE(hThread);
|
2006-06-11 10:16:24 -07:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int pthread_cancel(pthread_t thread)
|
|
|
|
|
{
|
|
|
|
|
HANDLE hThread = HANDLE_from_pthread(thread);
|
|
|
|
|
TerminateThread(hThread, 0);
|
2015-02-13 17:45:13 -08:00
|
|
|
debug_printf("WARNING: pthread_cancel is unsafe\n");
|
2006-06-11 10:16:24 -07:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int pthread_join(pthread_t thread, void** value_ptr)
|
|
|
|
|
{
|
|
|
|
|
HANDLE hThread = HANDLE_from_pthread(thread);
|
|
|
|
|
|
|
|
|
|
// note: pthread_join doesn't call for a timeout. if this wait
|
|
|
|
|
// locks up the process, at least it'll be easy to see why.
|
|
|
|
|
DWORD ret = WaitForSingleObject(hThread, INFINITE);
|
|
|
|
|
if(ret != WAIT_OBJECT_0)
|
|
|
|
|
{
|
2011-05-03 05:38:42 -07:00
|
|
|
WARN_IF_ERR(ERR::FAIL);
|
2006-06-11 10:16:24 -07:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// pass back the code that was passed to pthread_exit.
|
|
|
|
|
// SUS says <*value_ptr> need only be set on success!
|
|
|
|
|
if(value_ptr)
|
|
|
|
|
GetExitCodeThread(hThread, (LPDWORD)value_ptr);
|
|
|
|
|
|
|
|
|
|
CloseHandle(hThread);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|