History

player: add threaded decoder WIP

lieff committed 8 years ago

Commit 29f6dca1 parent f3efc076 files · +717 −2Browse files

Unified · Split

6 files
  1. player/build.sh +1 −1
  2. player/decode.c +122 −0
  3. player/decode.h +14 −0
  4. player/player.cpp +0 −1
  5. player/system.cpp +473 −0
  6. player/system.h +107 −0
player/build.sh+1−1
@@ -1 +1 @@
1−gcc -O2 -o player player.cpp glad.c -lstdc++ -lglfw -lGL -lm -ldl
1+gcc -O2 -o player *.cpp *.c -lstdc++ -lglfw -lGL -lpthread -lm -ldl
player/decode.c+122−0
@@ -0,0 +1,122 @@
1+#include <string.h>
2+#include <errno.h>
3+#include <sys/mman.h>
4+#include <sys/types.h>
5+#include <sys/stat.h>
6+#include <unistd.h>
7+#include <fcntl.h>
8+#include <stdlib.h>
9+#define MINIMP3_IMPLEMENTATION
10+#include "../minimp3.h"
11+#include "decode.h"
12+
13+static void add_audio(decoder *dec, short *buf, int bytes)
14+{
15+ char *buf_new = 0;
16+ if (dec->mp3_allocated < (dec->mp3_size + bytes))
17+ {
18+ int allocated = dec->mp3_allocated;
19+ dec->mp3_allocated = dec->mp3_allocated ? dec->mp3_allocated*2 : 4*1024*1024;
20+ buf_new = (char *)malloc(dec->mp3_allocated);
21+ memcpy(buf_new, dec->mp3_buf, allocated);
22+ }
23+ EnterCriticalSection(&dec->mp3_lock);
24+ if (buf_new)
25+ {
26+ if (dec->mp3_buf)
27+ free(dec->mp3_buf);
28+ dec->mp3_buf = (short *)buf_new;
29+ dec->mp3_size += bytes;
30+ }
31+ LeaveCriticalSection(&dec->mp3_lock);
32+}
33+
34+static void *load_mp3_thread(void *lpThreadParameter)
35+{
36+ static mp3dec_t mp3d;
37+ mp3dec_frame_info_t info = { 0, };
38+ struct stat st;
39+ decoder *dec = (decoder *)lpThreadParameter;
40+ unsigned char *inputBuffer, *buf_mp3;
41+ int file, mp3_size, samples;
42+retry_open:
43+ file = open(dec->file_name, O_RDONLY);
44+ if (file < 0 && (errno == EAGAIN || errno == EINTR))
45+ goto retry_open;
46+ free((void*)dec->file_name);
47+ dec->file_name = 0;
48+ if (file < 0 || fstat(file, &st) < 0)
49+ return (void*)(size_t)errno;
50+
51+ dec->mp3_file_size = st.st_size;
52+retry_mmap:
53+ inputBuffer = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE | MAP_POPULATE, file, 0);
54+ if (MAP_FAILED == inputBuffer && (errno == EAGAIN || errno == EINTR))
55+ goto retry_mmap;
56+ if (MAP_FAILED == inputBuffer)
57+ {
58+ close(file);
59+ return (void*)(size_t)errno;
60+ }
61+ buf_mp3 = inputBuffer;
62+ mp3_size = dec->mp3_file_size;
63+ mp3dec_init(&mp3d);
64+ do
65+ {
66+ short pcm[MINIMP3_MAX_SAMPLES_PER_FRAME];
67+ samples = mp3dec_decode_frame(&mp3d, buf_mp3, mp3_size, pcm, &info);
68+ if (samples)
69+ {
70+ add_audio(dec, pcm, samples*2*info.channels);
71+ if (!dec->mp3_rate)
72+ dec->mp3_rate = info.hz;
73+ if (!dec->mp3_channels)
74+ dec->mp3_channels = info.channels;
75+ if (dec->mp3_rate != info.hz || dec->mp3_channels != info.channels)
76+ break;
77+ }
78+ buf_mp3 += info.frame_bytes;
79+ mp3_size -= info.frame_bytes;
80+ } while (info.frame_bytes);
81+
82+ munmap(inputBuffer, st.st_size);
83+ close(file);
84+ return 0;
85+}
86+
87+int preload_mp3(decoder *dec, const char *file_name)
88+{
89+ if (dec->mp3_open_thread)
90+ {
91+ thread_wait(dec->mp3_open_thread);
92+ thread_close(dec->mp3_open_thread);
93+ dec->mp3_open_thread = 0;
94+ }
95+ EnterCriticalSection(&dec->mp3_lock);
96+ if (dec->mp3_buf)
97+ {
98+ free(dec->mp3_buf);
99+ dec->mp3_buf = 0;
100+ dec->mp3_size = 0;
101+ dec->mp3_allocated = 0;
102+ dec->mp3_rate = 0;
103+ dec->mp3_channels = 0;
104+ dec->mp3_duration = 0;
105+ }
106+ LeaveCriticalSection(&dec->mp3_lock);
107+
108+ if (!file_name || !*file_name)
109+ return 0;
110+
111+ dec->file_name = strdup(file_name);
112+ if (!dec->file_name)
113+ return 0;
114+ dec->mp3_open_thread = thread_create(load_mp3_thread, dec);
115+ if (!dec->mp3_open_thread)
116+ {
117+ free((void*)dec->file_name);
118+ dec->file_name = 0;
119+ return 0;
120+ }
121+ return 1;
122+}
player/decode.h+14−0
@@ -0,0 +1,14 @@
1+#pragma once
2+#include "system.h"
3+
4+typedef struct decoder
5+{
6+ CRITICAL_SECTION mp3_lock;
7+ const char *file_name;
8+ short *mp3_buf;
9+ int mp3_size, mp3_allocated, mp3_file_size, mp3_rate, mp3_channels;
10+ float mp3_duration;
11+ HANDLE mp3_open_thread;
12+} decoder;
13+
14+int preload_mp3(decoder *dec, const char *file_name);
player/player.cpp+0−1
@@ -10,7 +10,6 @@ · 9 unmodified lines
1010 #include <OpenGL/gl3.h>
1111 #endif
1212 #include "FreeSans.h"
13−#include "../minimp3.h"
1413
1514 #define STB_IMAGE_IMPLEMENTATION
1615 #include "stb_image.h"
player/system.cpp+473−0
@@ -0,0 +1,473 @@
1+#include "system.h"
2+
3+#ifndef _WIN32
4+
5+#include <stdlib.h>
6+#include <time.h>
7+#include <errno.h>
8+#include <unistd.h>
9+#if defined(__linux) || defined(__linux__)
10+#include <sys/prctl.h>
11+#endif
12+
13+#define nullptr 0
14+
15+struct Event
16+{
17+ Event * volatile pMultipleCond;
18+ pthread_mutex_t mutex;
19+ pthread_cond_t cond;
20+ volatile bool signaled;
21+ bool manual_reset;
22+};
23+
24+static bool InitEvent(Event *e)
25+{
26+#if (defined(ANDROID) && !defined(__LP64__)) || defined(__APPLE__)
27+ if (pthread_cond_init(&e->cond, NULL))
28+ return false;
29+#else
30+ pthread_condattr_t attr;
31+ if (pthread_condattr_init(&attr))
32+ return false;
33+ if (pthread_condattr_setclock(&attr, CLOCK_MONOTONIC))
34+ {
35+ pthread_condattr_destroy(&attr);
36+ return false;
37+ }
38+ if (pthread_cond_init(&e->cond, &attr))
39+ {
40+ pthread_condattr_destroy(&attr);
41+ return false;
42+ }
43+ pthread_condattr_destroy(&attr);
44+#endif
45+ if (pthread_mutex_init(&e->mutex, NULL))
46+ {
47+ pthread_cond_destroy(&e->cond);
48+ return false;
49+ }
50+ e->pMultipleCond = NULL;
51+ return true;
52+}
53+
54+#ifdef __APPLE__
55+#include <mach/mach_time.h>
56+static inline uint64_t GetAbsTimeInNanoseconds()
57+{
58+ static mach_timebase_info_data_t g_timebase_info;
59+ if (g_timebase_info.denom == 0)
60+ mach_timebase_info(&g_timebase_info);
61+ return mach_absolute_time()*g_timebase_info.numer/g_timebase_info.denom;
62+}
63+#endif
64+
65+static inline void GetAbsTime(timespec *ts, uint32_t timeout)
66+{
67+#if defined(__APPLE__)
68+ uint64_t cur_time = GetAbsTimeInNanoseconds();
69+ ts->tv_sec = cur_time/1000000000u + timeout/1000u;
70+ ts->tv_nsec = (cur_time % 1000000000u) + (timeout % 1000u)*1000000u;
71+#else
72+ clock_gettime(CLOCK_MONOTONIC, ts);
73+ ts->tv_sec += timeout/1000u;
74+ ts->tv_nsec += (timeout % 1000u)*1000000u;
75+#endif
76+ if (ts->tv_nsec >= 1000000000)
77+ {
78+ ts->tv_nsec -= 1000000000;
79+ ts->tv_sec++;
80+ }
81+}
82+
83+static inline int CondTimedWait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime)
84+{
85+#if defined(ANDROID) && !defined(__LP64__)
86+ return pthread_cond_timedwait_monotonic_np(cond, mutex, abstime);
87+#elif defined(__APPLE__)
88+ timespec reltime;
89+ uint64_t cur_time = GetAbsTimeInNanoseconds();
90+ reltime.tv_sec = abstime->tv_sec - cur_time/1000000000u;
91+ reltime.tv_nsec = abstime->tv_nsec - (cur_time % 1000000000u);
92+ if (reltime.tv_nsec < 0)
93+ {
94+ reltime.tv_nsec += 1000000000;
95+ reltime.tv_sec--;
96+ }
97+ if ((reltime.tv_sec < 0) || ((reltime.tv_sec == 0) && (reltime.tv_nsec == 0)))
98+ return ETIMEDOUT;
99+ return pthread_cond_timedwait_relative_np(cond, mutex, &reltime);
100+#else
101+ return pthread_cond_timedwait(cond, mutex, abstime);
102+#endif
103+}
104+
105+static bool WaitForEvent(Event *e, uint32_t timeout, bool *signaled)
106+{
107+ if (pthread_mutex_lock(&e->mutex))
108+ return false;
109+
110+ if (timeout == INFINITE)
111+ {
112+ while (!e->signaled)
113+ pthread_cond_wait(&e->cond, &e->mutex);
114+ } else if (timeout != 0)
115+ {
116+ timespec t;
117+ GetAbsTime(&t, timeout);
118+ while (!e->signaled)
119+ {
120+ if (CondTimedWait(&e->cond, &e->mutex, &t))
121+ break;
122+ }
123+ }
124+ *signaled = e->signaled;
125+ if (!e->manual_reset)
126+ e->signaled = false;
127+
128+ if (pthread_mutex_unlock(&e->mutex))
129+ return false;
130+ return true;
131+}
132+
133+static bool WaitForMultipleEvents(Event **e, uint32_t count, uint32_t timeout, bool waitAll, int *signaled_num)
134+{
135+ uint32_t i;
136+#define PTHR(func, num) for (i = num; i < count; i++)\
137+ if (func(&e[i]->mutex))\
138+ return false;
139+ PTHR(pthread_mutex_lock, 0);
140+
141+ int sig_num = -1;
142+ if (timeout == 0)
143+ {
144+#define CHECK_SIGNALED \
145+ if (waitAll)\
146+ {\
147+ for (i = 0; i < count; i++)\
148+ if (!e[i]->signaled)\
149+ break;\
150+ if (i == count)\
151+ for (i = 0; i < count; i++)\
152+ {\
153+ if (sig_num < 0 && e[i]->signaled)\
154+ sig_num = (int)i;\
155+ if (!e[i]->manual_reset)\
156+ e[i]->signaled = false;\
157+ }\
158+ } else\
159+ {\
160+ for (i = 0; i < count; i++)\
161+ if (e[i]->signaled)\
162+ {\
163+ sig_num = (int)i;\
164+ if (!e[i]->manual_reset)\
165+ e[i]->signaled = false;\
166+ break;\
167+ }\
168+ }
169+ CHECK_SIGNALED;
170+ } else
171+ if (timeout == INFINITE)
172+ {
173+#define SET_MULTIPLE(val) for (i = 1; i < count; i++)\
174+ e[i]->pMultipleCond = val;
175+ SET_MULTIPLE(e[0]);
176+ for (;;)
177+ {
178+ CHECK_SIGNALED;
179+ if (sig_num >= 0)
180+ break;
181+ PTHR(pthread_mutex_unlock, 1);
182+ pthread_cond_wait(&e[0]->cond, &e[0]->mutex);
183+ PTHR(pthread_mutex_lock, 1);
184+ }
185+ SET_MULTIPLE(0);
186+ } else
187+ {
188+ SET_MULTIPLE(e[0]);
189+ timespec t;
190+ GetAbsTime(&t, timeout);
191+ for (;;)
192+ {
193+ CHECK_SIGNALED;
194+ if (sig_num >= 0)
195+ break;
196+ PTHR(pthread_mutex_unlock, 1);
197+ int res = CondTimedWait(&e[0]->cond, &e[0]->mutex, &t);
198+ PTHR(pthread_mutex_lock, 1);
199+ if (res)
200+ break;
201+ }
202+ SET_MULTIPLE(0);
203+ }
204+ PTHR(pthread_mutex_unlock, 0);
205+ *signaled_num = sig_num;
206+ return true;
207+}
208+
209+static bool SignalEvent(Event *e)
210+{
211+ if (pthread_mutex_lock(&e->mutex))
212+ return false;
213+
214+ Event *pMultipleCond = e->pMultipleCond;
215+ e->signaled = true;
216+ if (pthread_cond_signal(&e->cond))
217+ return false;
218+
219+ if (pthread_mutex_unlock(&e->mutex))
220+ return false;
221+
222+ if (pMultipleCond && pMultipleCond != e)
223+ {
224+ if (pthread_mutex_lock(&pMultipleCond->mutex))
225+ return false;
226+ if (pthread_cond_signal(&pMultipleCond->cond))
227+ return false;
228+ if (pthread_mutex_unlock(&pMultipleCond->mutex))
229+ return false;
230+ }
231+ return true;
232+}
233+
234+static bool ResetEvent(Event *e)
235+{
236+ if (pthread_mutex_lock(&e->mutex))
237+ return false;
238+ e->signaled = false;
239+ if (pthread_mutex_unlock(&e->mutex))
240+ return false;
241+ return true;
242+}
243+
244+HANDLE event_create(bool manualReset, bool initialState)
245+{
246+ Event *e = (Event *)malloc(sizeof(*e));
247+ if (!e)
248+ return NULL;
249+ if (!InitEvent(e))
250+ {
251+ free(e);
252+ return NULL;
253+ }
254+ e->manual_reset = manualReset;
255+ e->signaled = initialState;
256+ return (HANDLE)e;
257+}
258+
259+bool event_destroy(HANDLE event)
260+{
261+ Event *e = (Event *)event;
262+ if (!e)
263+ return false;
264+ if (pthread_cond_destroy(&e->cond))
265+ return false;
266+ if (pthread_mutex_destroy(&e->mutex))
267+ return false;
268+ free((void *)e);
269+ return true;
270+}
271+
272+bool event_set(HANDLE event)
273+{
274+ return SignalEvent((Event *)event);
275+}
276+
277+bool event_reset(HANDLE event)
278+{
279+ return ResetEvent((Event *)event);
280+}
281+
282+int event_wait(HANDLE event, uint32_t milliseconds)
283+{
284+ bool signaled;
285+ if (!WaitForEvent((Event *)event, milliseconds, &signaled))
286+ return WAIT_FAILED;
287+ return signaled ? WAIT_OBJECT : WAIT_TIMEOUT;
288+}
289+
290+int event_wait_multiple(uint32_t count, const HANDLE *events, bool waitAll, uint32_t milliseconds)
291+{
292+ if (count == 1)
293+ return event_wait(events[0], milliseconds);
294+ int signaled_num = -1;
295+ if (!WaitForMultipleEvents((Event **)events, count, milliseconds, waitAll, &signaled_num))
296+ return WAIT_FAILED;
297+ return (signaled_num < 0) ? WAIT_TIMEOUT : (WAIT_OBJECT_0 + signaled_num);
298+}
299+
300+bool InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection)
301+{
302+ pthread_mutexattr_t ma;
303+ if (pthread_mutexattr_init(&ma))
304+ return false;
305+ if (pthread_mutexattr_settype(&ma, PTHREAD_MUTEX_RECURSIVE))
306+ {
307+ pthread_mutexattr_destroy(&ma);
308+ return false;
309+ }
310+ if (pthread_mutex_init((pthread_mutex_t *)lpCriticalSection, &ma))
311+ {
312+ pthread_mutexattr_destroy(&ma);
313+ return false;
314+ }
315+ if (pthread_mutexattr_destroy(&ma))
316+ return false;
317+ return true;
318+}
319+
320+bool DeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection)
321+{
322+ if (pthread_mutex_destroy((pthread_mutex_t *)lpCriticalSection))
323+ return false;
324+ return true;
325+}
326+
327+bool EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection)
328+{
329+ if (pthread_mutex_lock((pthread_mutex_t *)lpCriticalSection))
330+ return false;
331+ return true;
332+}
333+
334+bool LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection)
335+{
336+ if (pthread_mutex_unlock((pthread_mutex_t *)lpCriticalSection))
337+ return false;
338+ return true;
339+}
340+
341+HANDLE thread_create(LPTHREAD_START_ROUTINE lpStartAddress, void *lpParameter)
342+{
343+ pthread_t *t = (pthread_t *)malloc(sizeof(*t));
344+ if (!t)
345+ return nullptr;
346+ if (pthread_create(t, 0, lpStartAddress, lpParameter))
347+ {
348+ free(t);
349+ return nullptr;
350+ }
351+ //if (lpThreadId)
352+ // *lpThreadId = (uint32_t)*t;
353+ return (HANDLE)t;
354+}
355+
356+bool thread_close(HANDLE thread)
357+{
358+ if (!thread)
359+ return false;
360+ pthread_t *t = (pthread_t *)thread;
361+ if (*t)
362+ pthread_detach(*t);
363+ free(t);
364+ return true;
365+}
366+
367+void *thread_wait(HANDLE thread)
368+{
369+ if (!thread)
370+ return (void*)-1;
371+ void *ret = 0;
372+ pthread_t *t = (pthread_t *)thread;
373+ if (!*t)
374+ return ret;
375+ int res = pthread_join(*t, &ret);
376+ if (res)
377+ return (void*)-1;
378+#if 0
379+ if (timeout == 0)
380+ {
381+ int res = pthread_tryjoin_np(*t, &ret);
382+ if (res)
383+ return false;
384+ } else
385+ if (timeout == INFINITE)
386+ {
387+ int res = pthread_join(*t, &ret);
388+ if (res)
389+ return false;
390+ } else
391+ {
392+ timespec ts;
393+ GetAbsTime(&ts, timeout);
394+ int res = pthread_timedjoin_np(*t, &ret, &ts);
395+ if (res)
396+ return false;
397+ }
398+#endif
399+ *t = 0; // thread joined - no need to detach
400+ return ret;
401+}
402+
403+#else //_WIN32
404+
405+bool event_destroy(HANDLE event)
406+{
407+ CloseHandle(event);
408+ return true;
409+}
410+
411+bool thread_close(HANDLE thread)
412+{
413+ CloseHandle(thread);
414+ return true;
415+}
416+
417+bool thread_wait(HANDLE thread)
418+{
419+ return WaitForSingleObject(thread, INFINITE) == WAIT_OBJECT_0;
420+}
421+
422+#endif //_WIN32
423+
424+bool thread_name(const char *name)
425+{
426+#ifdef _WIN32
427+ struct tagTHREADNAME_INFO
428+ {
429+ DWORD dwType;
430+ LPCSTR szName;
431+ DWORD dwThreadID;
432+ DWORD dwFlags;
433+ } info = { 0x1000, name, (DWORD)-1, 0 };
434+ __try
435+ {
436+ RaiseException(0x406D1388, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info);
437+ }
438+ __except(EXCEPTION_EXECUTE_HANDLER)
439+ {
440+ }
441+ return true;
442+#elif defined(__linux) || defined(__linux__)
443+ return (0 == prctl(PR_SET_NAME, name, 0, 0, 0));
444+ //return (0 == pthread_setname_np(pthread_self(), name));
445+#else // macos, ios
446+ return (0 == pthread_setname_np(name));
447+#endif
448+}
449+
450+bool thread_sleep(uint32_t milliseconds)
451+{
452+#ifdef _WIN32
453+ Sleep(milliseconds);
454+#else
455+ usleep((useconds_t)milliseconds*1000);
456+#endif
457+}
458+
459+uint64_t GetTime()
460+{
461+ uint64_t time;
462+#ifdef _WIN32
463+ QueryPerformanceCounter((LARGE_INTEGER*)&time);
464+#elif defined(__APPLE__)
465+ time = GetAbsTimeInNanoseconds() / 1000u;
466+#else
467+ timespec ts;
468+ // CLOCK_PROCESS_CPUTIME_ID CLOCK_THREAD_CPUTIME_ID
469+ clock_gettime(CLOCK_MONOTONIC, &ts);
470+ time = (uint64_t)ts.tv_sec * 1000000u + ts.tv_nsec / 1000u;
471+#endif
472+ return time;
473+}
player/system.h+107−0
@@ -0,0 +1,107 @@
1+#pragma once
2+#ifndef __LGE_SYSTEM_H__
3+#define __LGE_SYSTEM_H__
4+
5+#ifdef _WIN32
6+
7+#include <windows.h>
8+typedef DWORD THREAD_RET;
9+#define THRAPI __stdcall
10+#include <stdint.h>
11+
12+#else //_WIN32
13+
14+#include <stdint.h>
15+#include <pthread.h>
16+
17+typedef void * THREAD_RET;
18+typedef THREAD_RET (*PTHREAD_START_ROUTINE)(void *lpThreadParameter);
19+typedef PTHREAD_START_ROUTINE LPTHREAD_START_ROUTINE;
20+
21+typedef pthread_mutex_t CRITICAL_SECTION, *PCRITICAL_SECTION, *LPCRITICAL_SECTION;
22+
23+#define THRAPI
24+
25+#ifndef FALSE
26+#define FALSE 0
27+#endif
28+
29+#ifndef TRUE
30+#define TRUE 1
31+#endif
32+
33+typedef void * HANDLE;
34+#define MAXIMUM_WAIT_OBJECTS 64
35+#define INFINITE (-1)
36+#define WAIT_FAILED (-1)
37+#define WAIT_TIMEOUT 0x102
38+#define WAIT_OBJECT 0
39+#define WAIT_OBJECT_0 0
40+#define WAIT_ABANDONED 128
41+#define WAIT_ABANDONED_0 128
42+
43+#ifdef __cplusplus
44+extern "C" {
45+#else
46+#ifndef bool
47+#define bool int
48+#endif
49+#endif
50+
51+HANDLE event_create(bool manualReset
52+#ifdef __cplusplus
53+ = false
54+#endif
55+ , bool initialState
56+#ifdef __cplusplus
57+ = false
58+#endif
59+ );
60+bool event_set(HANDLE event);
61+bool event_reset(HANDLE event);
62+int event_wait(HANDLE event, uint32_t milliseconds
63+#ifdef __cplusplus
64+ = INFINITE
65+#endif
66+ );
67+int event_wait_multiple(uint32_t count, const HANDLE *events, bool waitAll
68+#ifdef __cplusplus
69+ = false
70+#endif
71+ , uint32_t milliseconds
72+#ifdef __cplusplus
73+ = INFINITE
74+#endif
75+ );
76+
77+bool InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
78+bool DeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
79+bool EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
80+bool LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
81+
82+HANDLE thread_create(LPTHREAD_START_ROUTINE lpStartAddress, void *lpParameter);
83+
84+#ifdef __cplusplus
85+}
86+#endif
87+
88+#endif //_WIN32
89+
90+#ifdef __cplusplus
91+extern "C" {
92+#endif
93+
94+bool event_destroy(HANDLE event);
95+
96+bool thread_close(HANDLE thread);
97+void *thread_wait(HANDLE thread);
98+bool thread_name(const char *name);
99+bool thread_sleep(uint32_t milliseconds);
100+
101+uint64_t GetTime();
102+
103+#ifdef __cplusplus
104+}
105+#endif
106+
107+#endif //__LGE_SYSTEM_H__

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t