1 /* -*- Mode: C; indent-tabs-mode: t; tab-width: 4 -*-
2 // ---------------------------------------------------------------------------
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)
15 #elif defined(SJME_CONFIG_HAS_WINDOWS)
16 #include <processthreadsapi.h>
19 #include "sjme/debug.h"
21 sjme_errorCode
sjme_thread_current(
22 sjme_attrInOutNotNull sjme_thread
* outThread
)
26 if (outThread
== NULL
)
27 return SJME_ERROR_NULL_ARGUMENTS
;
30 result
= SJME_THREAD_NULL
;
32 #if defined(SJME_CONFIG_HAS_THREADS_PTHREAD)
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)
39 result
= GetCurrentThread();
40 if (result
== NULL
|| result
== SJME_THREAD_NULL
)
41 return SJME_ERROR_ILLEGAL_STATE
;
44 return SJME_ERROR_NOT_IMPLEMENTED
;
47 /* Use given 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)
59 DWORD (*getThreadIdFunc
)(HANDLE
);
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
)
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");
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
);
84 return aThread
== bThread
;
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)
99 sjme_intPointer threadId
;
101 if (outThread
== NULL
|| inMain
== NULL
)
102 return SJME_ERROR_NULL_ARGUMENTS
;
105 result
= SJME_THREAD_NULL
;
108 #if defined(SJME_CONFIG_HAS_THREADS_PTHREAD)
109 /* Setup new thread. */
110 if (0 != pthread_create(&result
, NULL
,
112 return SJME_ERROR_CANNOT_CREATE
;
113 threadId
= (sjme_intPointer
)result
;
114 #elif defined(SJME_CONFIG_HAS_THREADS_WIN32)
115 /* Setup new thread. */
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
;
124 return SJME_ERROR_NOT_IMPLEMENTED
;
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
;
138 sjme_jboolean keepSpinning
;
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 ¤t
)) || 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();
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
,
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();
181 sjme_atomic_barrier();
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
;
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 ¤t
)) || 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();
212 sjme_atomic_barrier();
215 /* We own the lock hopefully, so count down. */
216 if ((owned
= sjme_atomic_sjme_thread_compareSet(&inLock
->owner
,
219 /* If we count down to zero, then we no longer own the lock. */
220 if ((count
= sjme_atomic_sjme_jint_getAdd(&inLock
->count
,
223 sjme_atomic_sjme_thread_set(&inLock
->owner
,
225 sjme_atomic_sjme_jint_set(&inLock
->count
,
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();
237 sjme_atomic_barrier();
239 /* Do we not own the lock? */
241 return sjme_error_fatal(SJME_ERROR_NOT_LOCK_OWNER
);
243 /* Give the lock count that is left. */
244 if (outCount
!= NULL
)
247 *outCount
= count
- 1;
252 return SJME_ERROR_NONE
;
255 void sjme_thread_yield(void)
257 #if defined(SJME_CONFIG_HAS_LINUX)
259 #elif defined(SJME_CONFIG_HAS_THREADS_PTHREAD_MACOS)
260 /* macOS has none. */
261 #elif defined(SJME_CONFIG_HAS_THREADS_PTHREAD_BSD)
263 #elif defined(SJME_CONFIG_HAS_THREADS_WIN32)