summaryrefslogtreecommitdiff
path: root/src/systhread.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/systhread.c')
-rw-r--r--src/systhread.c417
1 files changed, 417 insertions, 0 deletions
diff --git a/src/systhread.c b/src/systhread.c
new file mode 100644
index 00000000000..a2c556fd8e3
--- /dev/null
+++ b/src/systhread.c
@@ -0,0 +1,417 @@
+/* System thread definitions
+Copyright (C) 2012-2016 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+#include <setjmp.h>
+#include "lisp.h"
+
+#ifndef THREADS_ENABLED
+
+void
+sys_mutex_init (sys_mutex_t *m)
+{
+ *m = 0;
+}
+
+void
+sys_mutex_lock (sys_mutex_t *m)
+{
+}
+
+void
+sys_mutex_unlock (sys_mutex_t *m)
+{
+}
+
+void
+sys_mutex_destroy (sys_mutex_t *m)
+{
+}
+
+void
+sys_cond_init (sys_cond_t *c)
+{
+ *c = 0;
+}
+
+void
+sys_cond_wait (sys_cond_t *c, sys_mutex_t *m)
+{
+}
+
+void
+sys_cond_signal (sys_cond_t *c)
+{
+}
+
+void
+sys_cond_broadcast (sys_cond_t *c)
+{
+}
+
+void
+sys_cond_destroy (sys_cond_t *c)
+{
+}
+
+sys_thread_t
+sys_thread_self (void)
+{
+ return 0;
+}
+
+int
+sys_thread_equal (sys_thread_t x, sys_thread_t y)
+{
+ return x == y;
+}
+
+int
+sys_thread_create (sys_thread_t *t, const char *name,
+ thread_creation_function *func, void *datum)
+{
+ return 0;
+}
+
+void
+sys_thread_yield (void)
+{
+}
+
+#elif defined (HAVE_PTHREAD)
+
+#include <sched.h>
+
+#ifdef HAVE_SYS_PRCTL_H
+#include <sys/prctl.h>
+#endif
+
+void
+sys_mutex_init (sys_mutex_t *mutex)
+{
+ pthread_mutex_init (mutex, NULL);
+}
+
+void
+sys_mutex_lock (sys_mutex_t *mutex)
+{
+ pthread_mutex_lock (mutex);
+}
+
+void
+sys_mutex_unlock (sys_mutex_t *mutex)
+{
+ pthread_mutex_unlock (mutex);
+}
+
+void
+sys_mutex_destroy (sys_mutex_t *mutex)
+{
+ pthread_mutex_destroy (mutex);
+}
+
+void
+sys_cond_init (sys_cond_t *cond)
+{
+ pthread_cond_init (cond, NULL);
+}
+
+void
+sys_cond_wait (sys_cond_t *cond, sys_mutex_t *mutex)
+{
+ pthread_cond_wait (cond, mutex);
+}
+
+void
+sys_cond_signal (sys_cond_t *cond)
+{
+ pthread_cond_signal (cond);
+}
+
+void
+sys_cond_broadcast (sys_cond_t *cond)
+{
+ pthread_cond_broadcast (cond);
+}
+
+void
+sys_cond_destroy (sys_cond_t *cond)
+{
+ pthread_cond_destroy (cond);
+}
+
+sys_thread_t
+sys_thread_self (void)
+{
+ return pthread_self ();
+}
+
+int
+sys_thread_equal (sys_thread_t one, sys_thread_t two)
+{
+ return pthread_equal (one, two);
+}
+
+int
+sys_thread_create (sys_thread_t *thread_ptr, const char *name,
+ thread_creation_function *func, void *arg)
+{
+ pthread_attr_t attr;
+ int result = 0;
+
+ if (pthread_attr_init (&attr))
+ return 0;
+
+ if (!pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED))
+ {
+ result = pthread_create (thread_ptr, &attr, func, arg) == 0;
+#if defined (HAVE_SYS_PRCTL_H) && defined (HAVE_PRCTL) && defined (PR_SET_NAME)
+ if (result && name != NULL)
+ prctl (PR_SET_NAME, name);
+#endif
+ }
+
+ pthread_attr_destroy (&attr);
+
+ return result;
+}
+
+void
+sys_thread_yield (void)
+{
+ sched_yield ();
+}
+
+#elif defined (WINDOWSNT)
+
+#include <windows.h>
+
+/* Cannot include <process.h> because of the local header by the same
+ name, sigh. */
+uintptr_t _beginthread (void (__cdecl *)(void *), unsigned, void *);
+
+/* Mutexes are implemented as critical sections, because they are
+ faster than Windows mutex objects (implemented in userspace), and
+ satisfy the requirements, since we only need to synchronize within a
+ single process. */
+void
+sys_mutex_init (sys_mutex_t *mutex)
+{
+ InitializeCriticalSection ((LPCRITICAL_SECTION)mutex);
+}
+
+void
+sys_mutex_lock (sys_mutex_t *mutex)
+{
+ /* FIXME: What happens if the owning thread exits without releasing
+ the mutex? According to MSDN, the result is undefined behavior. */
+ EnterCriticalSection ((LPCRITICAL_SECTION)mutex);
+}
+
+void
+sys_mutex_unlock (sys_mutex_t *mutex)
+{
+ LeaveCriticalSection ((LPCRITICAL_SECTION)mutex);
+}
+
+void
+sys_mutex_destroy (sys_mutex_t *mutex)
+{
+ /* FIXME: According to MSDN, deleting a critical session that is
+ owned by a thread leaves the other threads waiting for the
+ critical session in an undefined state. Posix docs seem to say
+ the same about pthread_mutex_destroy. Do we need to protect
+ against such calamities? */
+ DeleteCriticalSection ((LPCRITICAL_SECTION)mutex);
+}
+
+void
+sys_cond_init (sys_cond_t *cond)
+{
+ cond->initialized = false;
+ cond->wait_count = 0;
+ /* Auto-reset event for signal. */
+ cond->events[CONDV_SIGNAL] = CreateEvent (NULL, FALSE, FALSE, NULL);
+ /* Manual-reset event for broadcast. */
+ cond->events[CONDV_BROADCAST] = CreateEvent (NULL, TRUE, FALSE, NULL);
+ if (!cond->events[CONDV_SIGNAL] || !cond->events[CONDV_BROADCAST])
+ return;
+ InitializeCriticalSection ((LPCRITICAL_SECTION)&cond->wait_count_lock);
+ cond->initialized = true;
+}
+
+void
+sys_cond_wait (sys_cond_t *cond, sys_mutex_t *mutex)
+{
+ DWORD wait_result;
+ bool last_thread_waiting;
+
+ if (!cond->initialized)
+ return;
+
+ /* Increment the wait count avoiding race conditions. */
+ EnterCriticalSection ((LPCRITICAL_SECTION)&cond->wait_count_lock);
+ cond->wait_count++;
+ LeaveCriticalSection ((LPCRITICAL_SECTION)&cond->wait_count_lock);
+
+ /* Release the mutex and wait for either the signal or the broadcast
+ event. */
+ LeaveCriticalSection ((LPCRITICAL_SECTION)mutex);
+ wait_result = WaitForMultipleObjects (2, cond->events, FALSE, INFINITE);
+
+ /* Decrement the wait count and see if we are the last thread
+ waiting on the condition variable. */
+ EnterCriticalSection ((LPCRITICAL_SECTION)&cond->wait_count_lock);
+ cond->wait_count--;
+ last_thread_waiting =
+ wait_result == WAIT_OBJECT_0 + CONDV_BROADCAST
+ && cond->wait_count == 0;
+ LeaveCriticalSection ((LPCRITICAL_SECTION)&cond->wait_count_lock);
+
+ /* Broadcast uses a manual-reset event, so when the last thread is
+ released, we must manually reset that event. */
+ if (last_thread_waiting)
+ ResetEvent (cond->events[CONDV_BROADCAST]);
+
+ /* Per the API, re-acquire the mutex. */
+ EnterCriticalSection ((LPCRITICAL_SECTION)mutex);
+}
+
+void
+sys_cond_signal (sys_cond_t *cond)
+{
+ bool threads_waiting;
+
+ if (!cond->initialized)
+ return;
+
+ EnterCriticalSection ((LPCRITICAL_SECTION)&cond->wait_count_lock);
+ threads_waiting = cond->wait_count > 0;
+ LeaveCriticalSection ((LPCRITICAL_SECTION)&cond->wait_count_lock);
+
+ if (threads_waiting)
+ SetEvent (cond->events[CONDV_SIGNAL]);
+}
+
+void
+sys_cond_broadcast (sys_cond_t *cond)
+{
+ bool threads_waiting;
+
+ if (!cond->initialized)
+ return;
+
+ EnterCriticalSection ((LPCRITICAL_SECTION)&cond->wait_count_lock);
+ threads_waiting = cond->wait_count > 0;
+ LeaveCriticalSection ((LPCRITICAL_SECTION)&cond->wait_count_lock);
+
+ if (threads_waiting)
+ SetEvent (cond->events[CONDV_BROADCAST]);
+}
+
+void
+sys_cond_destroy (sys_cond_t *cond)
+{
+ if (cond->events[CONDV_SIGNAL])
+ CloseHandle (cond->events[CONDV_SIGNAL]);
+ if (cond->events[CONDV_BROADCAST])
+ CloseHandle (cond->events[CONDV_BROADCAST]);
+
+ if (!cond->initialized)
+ return;
+
+ /* FIXME: What if wait_count is non-zero, i.e. there are still
+ threads waiting on this condition variable? */
+ DeleteCriticalSection ((LPCRITICAL_SECTION)&cond->wait_count_lock);
+}
+
+sys_thread_t
+sys_thread_self (void)
+{
+ return (sys_thread_t) GetCurrentThreadId ();
+}
+
+int
+sys_thread_equal (sys_thread_t one, sys_thread_t two)
+{
+ return one == two;
+}
+
+static thread_creation_function *thread_start_address;
+
+/* _beginthread wants a void function, while we are passed a function
+ that returns a pointer. So we use a wrapper. */
+static void
+w32_beginthread_wrapper (void *arg)
+{
+ (void)thread_start_address (arg);
+}
+
+int
+sys_thread_create (sys_thread_t *thread_ptr, const char *name,
+ thread_creation_function *func, void *arg)
+{
+ /* FIXME: Do threads that run Lisp require some minimum amount of
+ stack? Zero here means each thread will get the same amount as
+ the main program. On GNU/Linux, it seems like the stack is 2MB
+ by default, overridden by RLIMIT_STACK at program start time.
+ Not sure what to do with this. See also the comment in
+ w32proc.c:new_child. */
+ const unsigned stack_size = 0;
+ uintptr_t thandle;
+
+ thread_start_address = func;
+
+ /* We use _beginthread rather than CreateThread because the former
+ arranges for the thread handle to be automatically closed when
+ the thread exits, thus preventing handle leaks and/or the need to
+ track all the threads and close their handles when they exit.
+ Also, MSDN seems to imply that code which uses CRT _must_ call
+ _beginthread, although if that is true, we already violate that
+ rule in many places... */
+ thandle = _beginthread (w32_beginthread_wrapper, stack_size, arg);
+ if (thandle == (uintptr_t)-1L)
+ return 0;
+
+ /* Kludge alert! We use the Windows thread ID, an unsigned 32-bit
+ number, as the sys_thread_t type, because that ID is the only
+ unique identifier of a thread on Windows. But _beginthread
+ returns a handle of the thread, and there's no easy way of
+ getting the thread ID given a handle (GetThreadId is available
+ only since Vista, so we cannot use it portably). Fortunately,
+ the value returned by sys_thread_create is not used by its
+ callers; instead, run_thread, which runs in the context of the
+ new thread, calls sys_thread_self and uses its return value;
+ sys_thread_self in this implementation calls GetCurrentThreadId.
+ Therefore, we return some more or less arbitrary value of the
+ thread ID from this function. */
+ *thread_ptr = thandle & 0xFFFFFFFF;
+ return 1;
+}
+
+void
+sys_thread_yield (void)
+{
+ Sleep (0);
+}
+
+#else
+
+#error port me
+
+#endif