Indentations break the feed.
[SquirrelJME.git] / nanocoat / lib / base / multithread.c
blob042ccd34b9e84433b427ba7e287bd4130863798f
1 /* -*- Mode: C; indent-tabs-mode: t; tab-width: 4 -*-
2 // ---------------------------------------------------------------------------
3 // SquirrelJME
4 // Copyright (C) Stephanie Gawroriski <xer@multiphasicapps.net>
5 // ---------------------------------------------------------------------------
6 // SquirrelJME is under the Mozilla Public License Version 2.0.
7 // See license.mkd for licensing and copyright information.
8 // -------------------------------------------------------------------------*/
10 #include "sjme/config.h"
11 #include "sjme/multithread.h"
13 #if defined(SJME_CONFIG_HAS_LINUX)
14 #include <sched.h>
15 #elif defined(SJME_CONFIG_HAS_WINDOWS)
16 #include <processthreadsapi.h>
17 #endif
19 #include "sjme/debug.h"
21 sjme_errorCode sjme_thread_current(
22 sjme_attrInOutNotNull sjme_thread* outThread)
24 sjme_thread result;
26 if (outThread == NULL)
27 return SJME_ERROR_NULL_ARGUMENTS;
29 /* Clear. */
30 result = SJME_THREAD_NULL;
32 #if defined(SJME_CONFIG_HAS_THREADS_PTHREAD)
33 /* Query. */
34 result = pthread_self();
35 if (result == 0 || result == SJME_THREAD_NULL)
36 return SJME_ERROR_ILLEGAL_STATE;
37 #elif defined(SJME_CONFIG_HAS_THREADS_WIN32)
38 /* Query. */
39 result = GetCurrentThread();
40 if (result == NULL || result == SJME_THREAD_NULL)
41 return SJME_ERROR_ILLEGAL_STATE;
42 #else
43 sjme_todo("Impl?");
44 return SJME_ERROR_NOT_IMPLEMENTED;
45 #endif
47 /* Use given result. */
48 *outThread = result;
49 return SJME_ERROR_NONE;
52 sjme_jboolean sjme_thread_equal(
53 sjme_attrInNullable sjme_thread aThread,
54 sjme_attrInNullable sjme_thread bThread)
56 #if defined(SJME_CONFIG_HAS_THREADS_PTHREAD)
57 #elif defined(SJME_CONFIG_HAS_THREADS_WIN32)
58 HMODULE kernel;
59 DWORD (*getThreadIdFunc)(HANDLE);
60 #endif
62 if ((aThread == SJME_THREAD_NULL) != (bThread == SJME_THREAD_NULL))
63 return SJME_JNI_FALSE;
65 else if (aThread == SJME_THREAD_NULL && bThread == SJME_THREAD_NULL)
66 return SJME_JNI_TRUE;
68 #if defined(SJME_CONFIG_HAS_THREADS_PTHREAD)
69 return pthread_equal(aThread, bThread);
70 #elif defined(SJME_CONFIG_HAS_THREADS_WIN32)
71 /* Obtain the kernel library. */
72 kernel = GetModuleHandle("kernel32.dll");
73 if (kernel == NULL)
74 return aThread == bThread;
76 /* Is there GetThreadId(), which is base 2003/Vista? */
77 getThreadIdFunc = ((void*)GetProcAddress(kernel, "GetThreadId"));
78 if (getThreadIdFunc == NULL)
79 return aThread == bThread;
81 /* Use that function instead! */
82 return getThreadIdFunc(aThread) == getThreadIdFunc(bThread);
83 #else
84 return aThread == bThread;
85 #endif
88 sjme_errorCode sjme_thread_new(
89 sjme_attrInOutNotNull sjme_thread* outThread,
90 sjme_attrInNullable sjme_intPointer* outThreadId,
91 sjme_attrInNotNull sjme_thread_mainFunc inMain,
92 sjme_attrInNullable sjme_thread_parameter anything)
94 #if defined(SJME_CONFIG_HAS_THREADS_PTHREAD)
95 #elif defined(SJME_CONFIG_HAS_THREADS_WIN32)
96 DWORD winThreadId;
97 #endif
98 sjme_thread result;
99 sjme_intPointer threadId;
101 if (outThread == NULL || inMain == NULL)
102 return SJME_ERROR_NULL_ARGUMENTS;
104 /* Clear first. */
105 result = SJME_THREAD_NULL;
106 threadId = 0;
108 #if defined(SJME_CONFIG_HAS_THREADS_PTHREAD)
109 /* Setup new thread. */
110 if (0 != pthread_create(&result, NULL,
111 inMain, anything))
112 return SJME_ERROR_CANNOT_CREATE;
113 threadId = (sjme_intPointer)result;
114 #elif defined(SJME_CONFIG_HAS_THREADS_WIN32)
115 /* Setup new thread. */
116 threadId = 0;
117 result = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)inMain,
118 anything, 0, &winThreadId);
119 if (result == NULL || result == SJME_THREAD_NULL)
120 return SJME_ERROR_CANNOT_CREATE;
121 threadId = winThreadId;
122 #else
123 sjme_todo("Impl?");
124 return SJME_ERROR_NOT_IMPLEMENTED;
125 #endif
127 /* Success! */
128 *outThread = result;
129 if (outThreadId != NULL)
130 *outThreadId = threadId;
131 return SJME_ERROR_NONE;
134 sjme_errorCode sjme_thread_spinLockGrab(sjme_thread_spinLock* inLock)
136 sjme_errorCode error;
137 sjme_thread current;
138 sjme_jboolean keepSpinning;
140 if (inLock == NULL)
141 return SJME_ERROR_NULL_ARGUMENTS;
143 /* We need the current thread. */
144 current = SJME_THREAD_NULL;
145 if (sjme_error_is(error = sjme_thread_current(
146 &current)) || current == SJME_THREAD_NULL)
147 return sjme_error_defaultOr(error,
148 SJME_ERROR_INVALID_THREAD_STATE);
150 /* This is done in a loop until we own the lock. */
151 for (keepSpinning = SJME_JNI_TRUE; keepSpinning;)
153 /* Grab the peek lock. */
154 while (SJME_JNI_FALSE == sjme_atomic_sjme_thread_compareSet(
155 &inLock->poke, SJME_THREAD_NULL, current))
157 sjme_atomic_barrier();
158 sjme_thread_yield();
159 sjme_atomic_barrier();
162 /* We own the lock already, or we just owned it, so count up. */
163 if (sjme_atomic_sjme_thread_compareSet(&inLock->owner,
164 current, current) ||
165 sjme_atomic_sjme_thread_compareSet(&inLock->owner,
166 SJME_THREAD_NULL, current))
168 sjme_atomic_sjme_jint_getAdd(&inLock->count, 1);
170 keepSpinning = SJME_JNI_FALSE;
173 /* Clear the peek lock. */
174 sjme_atomic_sjme_thread_compareSet(&inLock->poke,
175 current, SJME_THREAD_NULL);
178 /* Do this just for good measure for the wierd CPUs. */
179 sjme_atomic_barrier();
180 sjme_thread_yield();
181 sjme_atomic_barrier();
183 /* Success! */
184 return SJME_ERROR_NONE;
187 sjme_errorCode sjme_thread_spinLockRelease(
188 sjme_attrInNotNull sjme_thread_spinLock* inLock,
189 sjme_attrOutNullable sjme_jint* outCount)
191 sjme_errorCode error;
192 sjme_thread current;
193 sjme_jboolean owned;
194 sjme_jint count;
196 if (inLock == NULL)
197 return SJME_ERROR_NULL_ARGUMENTS;
199 /* We need the current thread. */
200 current = SJME_THREAD_NULL;
201 if (sjme_error_is(error = sjme_thread_current(
202 &current)) || current == SJME_THREAD_NULL)
203 return sjme_error_defaultOr(error,
204 SJME_ERROR_INVALID_THREAD_STATE);
206 /* Grab the peek lock. */
207 while (SJME_JNI_FALSE == sjme_atomic_sjme_thread_compareSet(
208 &inLock->poke, SJME_THREAD_NULL, current))
210 sjme_atomic_barrier();
211 sjme_thread_yield();
212 sjme_atomic_barrier();
215 /* We own the lock hopefully, so count down. */
216 if ((owned = sjme_atomic_sjme_thread_compareSet(&inLock->owner,
217 current, current)))
219 /* If we count down to zero, then we no longer own the lock. */
220 if ((count = sjme_atomic_sjme_jint_getAdd(&inLock->count,
221 -1)) <= 1)
223 sjme_atomic_sjme_thread_set(&inLock->owner,
224 SJME_THREAD_NULL);
225 sjme_atomic_sjme_jint_set(&inLock->count,
226 (count = 0));
230 /* Clear the peek lock. */
231 sjme_atomic_sjme_thread_compareSet(&inLock->poke,
232 current, SJME_THREAD_NULL);
234 /* Do this just for good measure for the wierd CPUs. */
235 sjme_atomic_barrier();
236 sjme_thread_yield();
237 sjme_atomic_barrier();
239 /* Do we not own the lock? */
240 if (!owned)
241 return sjme_error_fatal(SJME_ERROR_NOT_LOCK_OWNER);
243 /* Give the lock count that is left. */
244 if (outCount != NULL)
246 if (count > 0)
247 *outCount = count - 1;
248 else
249 *outCount = 0;
252 return SJME_ERROR_NONE;
255 void sjme_thread_yield(void)
257 #if defined(SJME_CONFIG_HAS_LINUX)
258 sched_yield();
259 #elif defined(SJME_CONFIG_HAS_THREADS_PTHREAD_MACOS)
260 /* macOS has none. */
261 #elif defined(SJME_CONFIG_HAS_THREADS_PTHREAD_BSD)
262 pthread_yield();
263 #elif defined(SJME_CONFIG_HAS_THREADS_WIN32)
264 SwitchToThread();
265 #endif