/
Thread.c
225 lines (204 loc) · 4.49 KB
/
Thread.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
/** vim: set noet ci pi sts=0 sw=4 ts=4
* @file Thread.c
* @brief Threading functions and synchronization primitives.
*/
#include "Thread.h"
#ifdef WINDOWS
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#elif defined POSIX
#include <errno.h>
#endif
/**
* Create a thread.
*
* @param startAddress The function the thread will execute
* @param startParameter The parameter the function will be called with
*
* @returns An opaque thread id object.
*/
SAL_Thread SAL_Thread_Create(SAL_Thread_StartAddress startAddress, void* startArgument) {
#ifdef WINDOWS
return CreateThread(NULL, 0, startAddress, startArgument, 0, NULL);
#elif defined POSIX
pthread_t threadId;
pthread_create(&threadId, NULL, startAddress, startArgument);
return threadId;
#endif
}
/**
* Wait for @a thread to exit.
*
* @returns 0 on success, non-0 on failure.
*/
uint64 SAL_Thread_Join(SAL_Thread thread) {
#ifdef WINDOWS
DWORD result;
result = WaitForSingleObject(thread, INFINITE);
CloseHandle(thread);
return (uint64)result;
#elif defined POSIX
return (uint64)pthread_join(thread, NULL);
#endif
}
/**
* Yields execution of @a thread, allowing another thread to execute.
*/
void SAL_Thread_Yield(void) {
#ifdef WINDOWS
Sleep(0);
#elif defined POSIX
pthread_yield();
#endif
}
/**
* Suspend execution of current thread.
*
* @param duration Length of time to sleep, in milliseconds
*/
void SAL_Thread_Sleep(uint32 duration) {
#ifdef WINDOWS
Sleep(duration);
#elif defined POSIX
sleep(duration);
#endif
}
/**
* Exit the current running thread.
*
* @param exitCode Return code of the thread
*/
void SAL_Thread_Exit(uint32 exitCode) {
#ifdef WINDOWS
ExitThread(exitCode);
#elif defined POSIX
pthread_exit((void*)&exitCode);
#endif
}
/**
* Create a new mutex.
*
* The created mutex should be freed with @ref SAL_Mutex_Free as to not leak
* memory. Do not free it with @a free.
*
* @returns a new mutex
*/
SAL_Mutex SAL_Mutex_Create(void) {
#ifdef WINDOWS
CRITICAL_SECTION* criticalSection;
criticalSection = Allocate(CRITICAL_SECTION);
InitializeCriticalSection(criticalSection);
return (SAL_Mutex)criticalSection;
#elif defined POSIX
pthread_mutex_t *mutex = Allocate(pthread_mutex_t);
pthread_mutex_init(mutex, NULL);
return mutex;
#endif
}
/**
* Frees @a mutex.
*
* @param mutex mutex to free
*
* @returns 0 if the mutex is sucessfully freed, 1 if the mutex is still being
* used.
*
* @warning On windows, you can free a mutex that is still being used. Be
* careful not to free mutexes that are still in use. Only on POSIX will you
* have a safety net.
*/
uint8 SAL_Mutex_Free(SAL_Mutex mutex) {
#ifdef WINDOWS
DeleteCriticalSection((CRITICAL_SECTION*)mutex);
return 0;
#elif defined POSIX
int status;
status = pthread_mutex_destroy(mutex);
if (status == EBUSY) {
return 1;
}
return 0;
#endif
}
/**
* Acquire a lock.
*
* @param mutex mutex to lock.
*
* @warning This function will block until the mutex is acquired.
*/
void SAL_Mutex_Acquire(SAL_Mutex mutex) {
#ifdef WINDOWS
EnterCriticalSection((CRITICAL_SECTION*)mutex);
#elif defined POSIX
pthread_mutex_lock(mutex);
#endif
}
/**
* Release a lock.
*
* @param mutex mutex to unlock
*/
void SAL_Mutex_Release(SAL_Mutex mutex) {
#ifdef WINDOWS
LeaveCriticalSection((CRITICAL_SECTION*)mutex);
#elif defined POSIX
pthread_mutex_unlock(mutex);
#endif
}
/**
* Create a new semaphore.
*
* @returns a new semaphore, NULL on failure
*/
SAL_Semaphore SAL_Semaphore_Create(void) {
#ifdef WINDOWS
return CreateSemaphore(NULL, 0, 4294967295, NULL);
#elif defined POSIX
sem_t *sem = Allocate(sem_t);
if (sem_init(sem, 0 /* shared between threads */, 0) != 0) {
return NULL;
}
return sem;
#endif
}
/**
* Free a semaphore.
*
* @param semaphore to free
* @warning Don't free a semaphor that is still in use (being waited on)
*/
void SAL_Semaphore_Free(SAL_Semaphore semaphore) {
#ifdef WINDOWS
CloseHandle(semaphore);
#elif defined POSIX
sem_destroy(semaphore);
Free(semaphore);
#endif
}
/**
* Decrement a semaphore.
*
* @param semaphore to decrement
*
* @warning This function will block if the semaphore count is zero.
*/
void SAL_Semaphore_Decrement(SAL_Semaphore semaphore) {
#ifdef WINDOWS
WaitForSingleObject(semaphore, INFINITE);
#elif defined POSIX
while (sem_wait(semaphore) == -1 && errno == EINTR);
#endif
}
/**
* Increment a semaphore.
*
* @param semaphore to increment
*/
void SAL_Semaphore_Increment(SAL_Semaphore semaphore) {
#ifdef WINDOWS
ReleaseSemaphore(semaphore, 1, NULL);
#elif defined POSIX
sem_post(semaphore);
#endif
}