diff --git a/test/initmain.c b/test/initmain.c index 1c4630e4..a9a568b4 100644 --- a/test/initmain.c +++ b/test/initmain.c @@ -2,6 +2,7 @@ #include #include #include "test.h" +#include "thread.h" // TODO fix up the formatting of testing.c so we can use newlines on the got/want stuff @@ -137,12 +138,13 @@ static void queueThread(void *data) testingTest(QueueMain_DifferentThread) { - testingThread *thread; + threadThread *thread; int flag = 0; - thread = testingNewThread(queueThread, &flag); + // TODO check errors + threadNewThread(queueThread, &flag, &thread); timeout_uiMain(t, 5 * timerSecond); - testingThreadWaitAndFree(thread); + threadThreadWaitAndFree(thread); if (flag != 1) testingTErrorf(t, "uiQueueMain() didn't set flag properly: got %d, want 1", flag); } @@ -158,12 +160,13 @@ static void queueOrderThread(void *data) testingTest(QueueMain_DifferentThreadSequence) { - testingThread *thread; + threadThread *thread; uint32_t flag = 1; // make sure it's initialized just in case - thread = testingNewThread(queueOrderThread, &flag); + // TODO check errors + threadNewThread(queueOrderThread, &flag, &thread); timeout_uiMain(t, 5 * timerSecond); - testingThreadWaitAndFree(thread); + threadThreadWaitAndFree(thread); checkOrder(t, flag); } diff --git a/test/meson.build b/test/meson.build index ab2c73f6..f20a1b19 100644 --- a/test/meson.build +++ b/test/meson.build @@ -9,12 +9,12 @@ libui_test_sources = [ if libui_OS == 'windows' libui_test_sources += [ - 'testing_windows.c', + 'thread_windows.c', 'timer_windows.c', ] else libui_test_sources += [ - 'testing_darwinunix.c', + 'thread_darwinunix.c', 'timer_darwinunix.c' ] endif diff --git a/test/testing.c b/test/testing.c index 978a796b..539f8fa1 100644 --- a/test/testing.c +++ b/test/testing.c @@ -5,7 +5,6 @@ #include #include "timer.h" #include "testing.h" -#include "testingpriv.h" void testingprivInternalError(const char *fmt, ...) { @@ -30,6 +29,9 @@ void *testingprivMalloc(size_t n, const char *what) return x; } +#define testingprivNew(T) ((T *) testingprivMalloc(sizeof (T), #T)) +#define testingprivNewArray(T, n) ((T *) testingprivMalloc(n * sizeof (T), #T "[" #n "]")) + void *testingprivRealloc(void *x, size_t n, const char *what) { void *y; @@ -40,6 +42,8 @@ void *testingprivRealloc(void *x, size_t n, const char *what) return y; } +#define testingprivResizeArray(x, T, n) ((T *) testingprivRealloc(x, n * sizeof (T), #T "[" #n "]")) + void testingprivFree(void *x) { free(x); diff --git a/test/testing.h b/test/testing.h index 83d58d6e..cd835974 100644 --- a/test/testing.h +++ b/test/testing.h @@ -64,11 +64,6 @@ extern void testingTFailNow(testingT *t); extern void testingTSkipNow(testingT *t); extern void testingTDefer(testingT *t, void (*f)(testingT *t, void *data), void *data); -// TODO I don't like this threading model, but let's use it for now so I can continue working -typedef struct testingThread testingThread; -extern testingThread *testingNewThread(void (*f)(void *data), void *data); -extern void testingThreadWaitAndFree(testingThread *t); - extern void testingprivRegisterTest(const char *, void (*)(testingT *), const char *, long); extern void testingprivRegisterTestBefore(const char *, void (*)(testingT *), const char *, long); extern void testingprivRegisterTestAfter(const char *, void (*)(testingT *), const char *, long); diff --git a/test/testing_darwinunix.c b/test/testing_darwinunix.c deleted file mode 100644 index c0e8d2a3..00000000 --- a/test/testing_darwinunix.c +++ /dev/null @@ -1,59 +0,0 @@ -// 28 april 2019 -// TODO pin down minimum POSIX versions (depends on what macOS 10.8 conforms to and what GLib/GTK+ require) -// TODO feature test macros for things like monotonic clocks? -// TODO is this needed in this file specifically, or just in testing_unix.c? -#define _POSIX_C_SOURCE 200112L -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "testing.h" -#include "testingpriv.h" - -// TODO don't start the timer on any platform until after we call setjmp(); also decide whether to start the timer before or after resuming the thread on Windows - -struct testingThread { - pthread_t thread; - void (*f)(void *data); - void *data; -}; - -static void *threadThreadProc(void *data) -{ - testingThread *t = (testingThread *) data; - - (*(t->f))(t->data); - return NULL; -} - -testingThread *testingNewThread(void (*f)(void *data), void *data) -{ - testingThread *t; - int err; - - t = testingprivNew(testingThread); - t->f = f; - t->data = data; - - err = pthread_create(&(t->thread), NULL, threadThreadProc, t); - if (err != 0) - testingprivInternalError("error creating thread: %s (%d)", strerror(err), err); - return t; -} - -void testingThreadWaitAndFree(testingThread *t) -{ - int err; - - err = pthread_join(t->thread, NULL); - if (err != 0) - testingprivInternalError("error waiting for thread to finish: %s (%d)", strerror(err), err); - // TODO do we need to free t->thread somehow? - testingprivFree(t); -} diff --git a/test/testingpriv.h b/test/testingpriv.h deleted file mode 100644 index e27bd3f6..00000000 --- a/test/testingpriv.h +++ /dev/null @@ -1,9 +0,0 @@ -// 29 april 2019 - -extern void testingprivInternalError(const char *fmt, ...); -extern void *testingprivMalloc(size_t n, const char *what); -#define testingprivNew(T) ((T *) testingprivMalloc(sizeof (T), #T)) -#define testingprivNewArray(T, n) ((T *) testingprivMalloc(n * sizeof (T), #T "[" #n "]")) -extern void *testingprivRealloc(void *x, size_t n, const char *what); -#define testingprivResizeArray(x, T, n) ((T *) testingprivRealloc(x, n * sizeof (T), #T "[" #n "]")) -extern void testingprivFree(void *x); diff --git a/test/thread.h b/test/thread.h new file mode 100644 index 00000000..6fa4b8b3 --- /dev/null +++ b/test/thread.h @@ -0,0 +1,20 @@ +// 4 may 2019 + +#include + +// I don't like this threading model, but let's use it for now so I can continue working + +typedef struct threadThread threadThread; + +typedef uint64_t threadSysError; +#ifdef _WIN32 +#define threadSysErrorFmt "0x%08I32X" +#define threadSysErrorFmtArg(x) ((uint32_t) x) +#else +#include +#define threadSysErrorFmt "%s (%d)" +#define threadSysErrorFmtArg(x) strerror((int) x), ((int) x) +#endif + +extern threadSysError threadNewThread(void (*f)(void *data), void *data, threadThread **t); +extern threadSysError threadThreadWaitAndFree(threadThread *t); diff --git a/test/thread_darwinunix.c b/test/thread_darwinunix.c new file mode 100644 index 00000000..24ec21e4 --- /dev/null +++ b/test/thread_darwinunix.c @@ -0,0 +1,61 @@ +// 28 april 2019 +// TODO pin down minimum POSIX versions (depends on what macOS 10.8 conforms to and what GLib/GTK+ require) +#define _POSIX_C_SOURCE 200112L +#include +#include +#include +#include "thread.h" + +struct threadThread { + pthread_t thread; + void (*f)(void *data); + void *data; +}; + +static void *threadThreadProc(void *data) +{ + threadThread *t = (threadThread *) data; + + (*(t->f))(t->data); + return NULL; +} + +threadSysError threadNewThread(void (*f)(void *data), void *data, threadThread **t) +{ + threadThread *tout; + int err; + + *t = NULL; + + errno = 0; + tout = (threadThread *) malloc(sizeof (threadThread)); + if (tout == NULL) { + err = errno; + if (err == 0) + err = ENOMEM; + return (threadSysError) err; + } + tout->f = f; + tout->data = data; + + err = pthread_create(&(tout->thread), NULL, threadThreadProc, tout); + if (err != 0) { + free(tout); + return (threadSysError) err; + } + + *t = tout; + return 0; +} + +threadSysError threadThreadWaitAndFree(threadThread *t) +{ + int err; + + err = pthread_join(t->thread, NULL); + if (err != 0) + return (threadSysError) err; + // TODO do we have to release t->thread somehow? + free(t); + return 0; +} diff --git a/test/testing_windows.c b/test/thread_windows.c similarity index 70% rename from test/testing_windows.c rename to test/thread_windows.c index 209430e3..eb48a0c5 100644 --- a/test/testing_windows.c +++ b/test/thread_windows.c @@ -10,13 +10,8 @@ #define NTDDI_VERSION 0x06000000 #include #include -#include -#include #include -#include -#include "timer.h" -#include "testing.h" -#include "testingpriv.h" +#include "thread.h" static HRESULT lastErrorCodeToHRESULT(DWORD lastError) { @@ -55,7 +50,7 @@ static HRESULT WINAPI hrWaitForSingleObject(HANDLE handle, DWORD timeout) return S_OK; } -struct testingThread { +struct threadThread { uintptr_t handle; void (*f)(void *data); void *data; @@ -63,34 +58,43 @@ struct testingThread { static unsigned __stdcall threadThreadProc(void *data) { - testingThread *t = (testingThread *) data; + threadThread *t = (threadThread *) data; (*(t->f))(t->data); return 0; } -testingThread *testingNewThread(void (*f)(void *data), void *data) +threadSysError threadNewThread(void (*f)(void *data), void *data, threadThread **t) { - testingThread *t; + threadThread *tout; HRESULT hr; - t = testingprivNew(testingThread); - t->f = f; - t->data = data; + *t = NULL; - hr = hr_beginthreadex(NULL, 0, threadThreadProc, t, 0, NULL, &(t->handle)); - if (hr != S_OK) - testingprivInternalError("error creating thread: 0x%08I32X", hr); - return t; + tout = (threadThread *) malloc(sizeof (threadThread)); + if (tout == NULL) + return (threadSysError) E_OUTOFMEMORY; + tout->f = f; + tout->data = data; + + hr = hr_beginthreadex(NULL, 0, threadThreadProc, tout, 0, NULL, &(tout->handle)); + if (hr != S_OK) { + free(tout); + return (threadSysError) hr; + } + + *t = tout; + return 0; } -void testingThreadWaitAndFree(testingThread *t) +threadSysError threadThreadWaitAndFree(threadThread *t) { HRESULT hr; hr = hrWaitForSingleObject((HANDLE) (t->handle), INFINITE); if (hr != S_OK) - testingprivInternalError("error waiting for thread to finish: 0x%08I32X", hr); + return (threadSysError) hr; CloseHandle((HANDLE) (t->handle)); - testingprivFree(t); + free(t); + return 0; } diff --git a/test/timer_windows.c b/test/timer_windows.c index 582a63bb..fb4e4bfe 100644 --- a/test/timer_windows.c +++ b/test/timer_windows.c @@ -206,6 +206,10 @@ struct timeoutParams { HRESULT hr; }; +static DWORD timeoutParamsSlot; +static HRESULT timeoutParamsHRESULT = S_OK; +static INIT_ONCE timeoutParamsOnce = INIT_ONCE_STATIC_INIT; + static void onTimeout(void) { struct timeoutParams *p; @@ -214,10 +218,6 @@ static void onTimeout(void) longjmp(p->retpos, 1); } -static DWORD timeoutParamsSlot; -static HRESULT timeoutParamsHRESULT = S_OK; -static INIT_ONCE timeoutParamsOnce = INIT_ONCE_STATIC_INIT; - static BOOL CALLBACK timeoutParamsSlotInit(PINIT_ONCE once, PVOID param, PVOID *ctx) { SetLastError(0);