summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--common/threads.c158
-rw-r--r--include/threads.h22
2 files changed, 170 insertions, 10 deletions
diff --git a/common/threads.c b/common/threads.c
index b2db1682..d9c32267 100644
--- a/common/threads.c
+++ b/common/threads.c
@@ -230,6 +230,164 @@ int almtx_timedlock(almtx_t *mtx, const struct timespec *ts)
return ret;
}
+#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0600
+int alcnd_init(alcnd_t *cond)
+{
+ InitializeConditionVariable(cond);
+ return althrd_success;
+}
+
+int alcnd_signal(alcnd_t *cond)
+{
+ WakeConditionVariable(cond);
+ return althrd_success;
+}
+
+int alcnd_broadcast(alcnd_t *cond)
+{
+ WakeAllConditionVariable(cond);
+ return althrd_success;
+}
+
+int alcnd_wait(alcnd_t *cond, almtx_t *mtx)
+{
+ if(SleepConditionVariableCS(cond, mtx, INFINITE) != 0)
+ return althrd_success;
+ return althrd_error;
+}
+
+int alcnd_timedwait(alcnd_t *cond, almtx_t *mtx, const struct timespec *time_point)
+{
+ struct timespec curtime;
+ DWORD sleeptime;
+
+ if(altimespec_get(&curtime, AL_TIME_UTC) != AL_TIME_UTC)
+ return althrd_error;
+
+ sleeptime = (time_point->tv_nsec - curtime.tv_nsec + 999999)/1000000;
+ sleeptime += (time_point->tv_sec - curtime.tv_sec)*1000;
+ if(SleepConditionVariableCS(cond, mtx, sleeptime) != 0)
+ return althrd_success;
+ return (GetLastError()==ERROR_TIMEOUT) ? althrd_timedout : althrd_error;
+}
+
+void alcnd_destroy(alcnd_t* UNUSED(cond))
+{
+ /* Nothing to delete? */
+}
+
+#else
+
+/* WARNING: This is a rather poor implementation of condition variables, with
+ * known problems. However, it's simple, efficient, and good enough for now to
+ * not require Vista. Based on "Strategies for Implementing POSIX Condition
+ * Variables" by Douglas C. Schmidt and Irfan Pyarali:
+ * http://www.cs.wustl.edu/~schmidt/win32-cv-1.html
+ */
+/* A better solution may be using Wine's implementation. It requires internals
+ * (NtCreateKeyedEvent, NtReleaseKeyedEvent, and NtWaitForKeyedEvent) from
+ * ntdll, and implemention of exchange and compare-exchange for RefCounts.
+ */
+
+typedef struct {
+ RefCount wait_count;
+
+ HANDLE events[2];
+} _int_alcnd_t;
+enum {
+ SIGNAL = 0,
+ BROADCAST = 1
+};
+
+int alcnd_init(alcnd_t *cond)
+{
+ _int_alcnd_t *icond = calloc(1, sizeof(*icond));
+ if(!icond) return althrd_nomem;
+
+ InitRef(&icond->wait_count, 0);
+
+ icond->events[SIGNAL] = CreateEvent(NULL, FALSE, FALSE, NULL);
+ icond->events[BROADCAST] = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if(!icond->events[SIGNAL] || !icond->events[BROADCAST])
+ {
+ if(icond->events[SIGNAL])
+ CloseHandle(icond->events[SIGNAL]);
+ if(icond->events[BROADCAST])
+ CloseHandle(icond->events[BROADCAST]);
+ free(icond);
+ return althrd_error;
+ }
+
+ cond->Ptr = icond;
+ return althrd_success;
+}
+
+int alcnd_signal(alcnd_t *cond)
+{
+ _int_alcnd_t *icond = cond->Ptr;
+ if(ReadRef(&icond->wait_count) > 0)
+ SetEvent(icond->events[SIGNAL]);
+ return althrd_success;
+}
+
+int alcnd_broadcast(alcnd_t *cond)
+{
+ _int_alcnd_t *icond = cond->Ptr;
+ if(ReadRef(&icond->wait_count) > 0)
+ SetEvent(icond->events[BROADCAST]);
+ return althrd_success;
+}
+
+int alcnd_wait(alcnd_t *cond, almtx_t *mtx)
+{
+ _int_alcnd_t *icond = cond->Ptr;
+ int res, last;
+
+ IncrementRef(&icond->wait_count);
+ LeaveCriticalSection(mtx);
+
+ res = WaitForMultipleObjects(2, icond->events, FALSE, INFINITE);
+
+ last = DecrementRef(&icond->wait_count) == 0 && res == WAIT_OBJECT_0+BROADCAST;
+ if(last) ResetEvent(icond->events[BROADCAST]);
+ EnterCriticalSection(mtx);
+
+ return althrd_success;
+}
+
+int alcnd_timedwait(alcnd_t *cond, almtx_t *mtx, const struct timespec *time_point)
+{
+ _int_alcnd_t *icond = cond->Ptr;
+ struct timespec curtime;
+ DWORD sleeptime;
+ int res, last;
+
+ if(altimespec_get(&curtime, AL_TIME_UTC) != AL_TIME_UTC)
+ return althrd_error;
+ sleeptime = (time_point->tv_nsec - curtime.tv_nsec + 999999)/1000000;
+ sleeptime += (time_point->tv_sec - curtime.tv_sec)*1000;
+
+ IncrementRef(&icond->wait_count);
+ LeaveCriticalSection(mtx);
+
+ res = WaitForMultipleObjects(2, icond->events, FALSE, sleeptime);
+
+ last = DecrementRef(&icond->wait_count) == 0 && res == WAIT_OBJECT_0+BROADCAST;
+ if(last) ResetEvent(icond->events[BROADCAST]);
+ EnterCriticalSection(mtx);
+
+ return (res == WAIT_TIMEOUT) ? althrd_timedout : althrd_success;
+}
+
+void alcnd_destroy(alcnd_t *cond)
+{
+ _int_alcnd_t *icond = cond->Ptr;
+ CloseHandle(icond->events[SIGNAL]);
+ CloseHandle(icond->events[BROADCAST]);
+ free(icond);
+}
+#endif /* defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0600 */
+
/* An associative map of uint:void* pairs. The key is the TLS index (given by
* TlsAlloc), and the value is the altss_dtor_t callback. When a thread exits,
diff --git a/include/threads.h b/include/threads.h
index 8b633f33..4a974b53 100644
--- a/include/threads.h
+++ b/include/threads.h
@@ -39,6 +39,11 @@ struct timespec {
typedef DWORD althrd_t;
typedef CRITICAL_SECTION almtx_t;
+#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0600
+typedef CONDITION_VARIABLE alcnd_t;
+#else
+typedef struct { void *Ptr; } alcnd_t;
+#endif
typedef DWORD altss_t;
typedef LONG alonce_flag;
@@ -120,16 +125,6 @@ typedef pthread_once_t alonce_flag;
#define AL_ONCE_FLAG_INIT PTHREAD_ONCE_INIT
-/* NOTE: Condition variables are POSIX-only at the moment, as Windows requires
- * Vista or newer for them (without slow, hacky work-arounds). */
-int alcnd_init(alcnd_t *cond);
-int alcnd_signal(alcnd_t *cond);
-int alcnd_broadcast(alcnd_t *cond);
-int alcnd_wait(alcnd_t *cond, almtx_t *mtx);
-int alcnd_timedwait(alcnd_t *cond, almtx_t *mtx, const struct timespec *time_point);
-void alcnd_destroy(alcnd_t *cond);
-
-
inline althrd_t althrd_current(void)
{
return pthread_self();
@@ -218,6 +213,13 @@ int almtx_init(almtx_t *mtx, int type);
void almtx_destroy(almtx_t *mtx);
int almtx_timedlock(almtx_t *mtx, const struct timespec *ts);
+int alcnd_init(alcnd_t *cond);
+int alcnd_signal(alcnd_t *cond);
+int alcnd_broadcast(alcnd_t *cond);
+int alcnd_wait(alcnd_t *cond, almtx_t *mtx);
+int alcnd_timedwait(alcnd_t *cond, almtx_t *mtx, const struct timespec *time_point);
+void alcnd_destroy(alcnd_t *cond);
+
int altss_create(altss_t *tss_id, altss_dtor_t callback);
void altss_delete(altss_t tss_id);