And split all the thread stuff into their own file. Almost done with all this test library cleanup!

This commit is contained in:
Pietro Gagliardi 2019-05-04 16:47:56 -04:00
parent 89e882c4a8
commit 57abc83fe3
10 changed files with 125 additions and 106 deletions

View File

@ -2,6 +2,7 @@
#include <inttypes.h> #include <inttypes.h>
#include <string.h> #include <string.h>
#include "test.h" #include "test.h"
#include "thread.h"
// TODO fix up the formatting of testing.c so we can use newlines on the got/want stuff // 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) testingTest(QueueMain_DifferentThread)
{ {
testingThread *thread; threadThread *thread;
int flag = 0; int flag = 0;
thread = testingNewThread(queueThread, &flag); // TODO check errors
threadNewThread(queueThread, &flag, &thread);
timeout_uiMain(t, 5 * timerSecond); timeout_uiMain(t, 5 * timerSecond);
testingThreadWaitAndFree(thread); threadThreadWaitAndFree(thread);
if (flag != 1) if (flag != 1)
testingTErrorf(t, "uiQueueMain() didn't set flag properly: got %d, want 1", flag); 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) testingTest(QueueMain_DifferentThreadSequence)
{ {
testingThread *thread; threadThread *thread;
uint32_t flag = 1; // make sure it's initialized just in case 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); timeout_uiMain(t, 5 * timerSecond);
testingThreadWaitAndFree(thread); threadThreadWaitAndFree(thread);
checkOrder(t, flag); checkOrder(t, flag);
} }

View File

@ -9,12 +9,12 @@ libui_test_sources = [
if libui_OS == 'windows' if libui_OS == 'windows'
libui_test_sources += [ libui_test_sources += [
'testing_windows.c', 'thread_windows.c',
'timer_windows.c', 'timer_windows.c',
] ]
else else
libui_test_sources += [ libui_test_sources += [
'testing_darwinunix.c', 'thread_darwinunix.c',
'timer_darwinunix.c' 'timer_darwinunix.c'
] ]
endif endif

View File

@ -5,7 +5,6 @@
#include <string.h> #include <string.h>
#include "timer.h" #include "timer.h"
#include "testing.h" #include "testing.h"
#include "testingpriv.h"
void testingprivInternalError(const char *fmt, ...) void testingprivInternalError(const char *fmt, ...)
{ {
@ -30,6 +29,9 @@ void *testingprivMalloc(size_t n, const char *what)
return x; 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 *testingprivRealloc(void *x, size_t n, const char *what)
{ {
void *y; void *y;
@ -40,6 +42,8 @@ void *testingprivRealloc(void *x, size_t n, const char *what)
return y; return y;
} }
#define testingprivResizeArray(x, T, n) ((T *) testingprivRealloc(x, n * sizeof (T), #T "[" #n "]"))
void testingprivFree(void *x) void testingprivFree(void *x)
{ {
free(x); free(x);

View File

@ -64,11 +64,6 @@ extern void testingTFailNow(testingT *t);
extern void testingTSkipNow(testingT *t); extern void testingTSkipNow(testingT *t);
extern void testingTDefer(testingT *t, void (*f)(testingT *t, void *data), void *data); 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 testingprivRegisterTest(const char *, void (*)(testingT *), const char *, long);
extern void testingprivRegisterTestBefore(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); extern void testingprivRegisterTestAfter(const char *, void (*)(testingT *), const char *, long);

View File

@ -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 <errno.h>
#include <inttypes.h>
#include <setjmp.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include <pthread.h>
#include <unistd.h>
#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);
}

View File

@ -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);

20
test/thread.h Normal file
View File

@ -0,0 +1,20 @@
// 4 may 2019
#include <stdint.h>
// 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 <string.h>
#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);

61
test/thread_darwinunix.c Normal file
View File

@ -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 <errno.h>
#include <stdlib.h>
#include <pthread.h>
#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;
}

View File

@ -10,13 +10,8 @@
#define NTDDI_VERSION 0x06000000 #define NTDDI_VERSION 0x06000000
#include <windows.h> #include <windows.h>
#include <process.h> #include <process.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include "thread.h"
#include "timer.h"
#include "testing.h"
#include "testingpriv.h"
static HRESULT lastErrorCodeToHRESULT(DWORD lastError) static HRESULT lastErrorCodeToHRESULT(DWORD lastError)
{ {
@ -55,7 +50,7 @@ static HRESULT WINAPI hrWaitForSingleObject(HANDLE handle, DWORD timeout)
return S_OK; return S_OK;
} }
struct testingThread { struct threadThread {
uintptr_t handle; uintptr_t handle;
void (*f)(void *data); void (*f)(void *data);
void *data; void *data;
@ -63,34 +58,43 @@ struct testingThread {
static unsigned __stdcall threadThreadProc(void *data) static unsigned __stdcall threadThreadProc(void *data)
{ {
testingThread *t = (testingThread *) data; threadThread *t = (threadThread *) data;
(*(t->f))(t->data); (*(t->f))(t->data);
return 0; 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; HRESULT hr;
t = testingprivNew(testingThread); *t = NULL;
t->f = f;
t->data = data;
hr = hr_beginthreadex(NULL, 0, threadThreadProc, t, 0, NULL, &(t->handle)); tout = (threadThread *) malloc(sizeof (threadThread));
if (hr != S_OK) if (tout == NULL)
testingprivInternalError("error creating thread: 0x%08I32X", hr); return (threadSysError) E_OUTOFMEMORY;
return t; 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; HRESULT hr;
hr = hrWaitForSingleObject((HANDLE) (t->handle), INFINITE); hr = hrWaitForSingleObject((HANDLE) (t->handle), INFINITE);
if (hr != S_OK) if (hr != S_OK)
testingprivInternalError("error waiting for thread to finish: 0x%08I32X", hr); return (threadSysError) hr;
CloseHandle((HANDLE) (t->handle)); CloseHandle((HANDLE) (t->handle));
testingprivFree(t); free(t);
return 0;
} }

View File

@ -206,6 +206,10 @@ struct timeoutParams {
HRESULT hr; HRESULT hr;
}; };
static DWORD timeoutParamsSlot;
static HRESULT timeoutParamsHRESULT = S_OK;
static INIT_ONCE timeoutParamsOnce = INIT_ONCE_STATIC_INIT;
static void onTimeout(void) static void onTimeout(void)
{ {
struct timeoutParams *p; struct timeoutParams *p;
@ -214,10 +218,6 @@ static void onTimeout(void)
longjmp(p->retpos, 1); 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) static BOOL CALLBACK timeoutParamsSlotInit(PINIT_ONCE once, PVOID param, PVOID *ctx)
{ {
SetLastError(0); SetLastError(0);