threads, jobs and random numbers

This commit is contained in:
Abdulmujeeb Raji 2025-01-26 16:19:25 +00:00
parent 4b0703d3a6
commit 515c1200a7
Signed by: midnadimple
GPG key ID: EB02C582F8C3962B
8 changed files with 352 additions and 3 deletions

View file

@ -32,9 +32,9 @@ brings the power of C++'s `std::vector` to C.
input/output. It's built on a basic buffer type that you can flush to output input/output. It's built on a basic buffer type that you can flush to output
data. This buffer type also acts as a **string builder**, by letting you append data. This buffer type also acts as a **string builder**, by letting you append
data to the buffer, then compile it to a string type. data to the buffer, then compile it to a string type.
- [ ] A multi-threaded job system. You can also just use the cross-platform - [x] A multi-threaded job system. You can also just use the operating system's
primitives. primitives.
- [ ] A Randomness API. Not really sure what else there is to say, really. - [x] A Randomness API. Not really sure what else there is to say, really.
- [ ] A 3D Math API with supports for Vectors and Matricies using SIMD. - [ ] A 3D Math API with supports for Vectors and Matricies using SIMD.
- [ ] A simple GUI initialization system that gets you a window with immediate-mode - [ ] A simple GUI initialization system that gets you a window with immediate-mode
input and a Direct3D11 (Win32) / OpenGL (Linux) context to build a renderer with. input and a Direct3D11 (Win32) / OpenGL (Linux) context to build a renderer with.

View file

@ -211,5 +211,7 @@ typedef U8 B8;
#include "bastd/string.c" #include "bastd/string.c"
#include "bastd/buffer.c" #include "bastd/buffer.c"
#include "bastd/args.c" #include "bastd/args.c"
#include "bastd/job.c"
#include "bastd/rand.c"
#endif //BASTD_C #endif //BASTD_C

View file

@ -0,0 +1,42 @@
#define BASTD_CLI
#include "../../bastd.c"
typedef struct DataToPass DataToPass;
struct DataToPass {
I64 job_num;
I64 random_num;
};
CALLBACK_EXPORT void
printJob(void *args)
{
DataToPass *dtp = (DataToPass *)args;
U8 raw[256];
Buffer buf = BUFFER(raw, 256);
Buffer_appendS8(&buf, S8("Thread #"));
Buffer_appendI64(&buf, dtp->job_num);
Buffer_appendS8(&buf, S8(" | Random number is "));
Buffer_appendI64(&buf, dtp->random_num);
Buffer_appendU8(&buf, '\n');
Buffer_standardOutput(&buf);
}
CALLBACK_EXPORT os_ErrorCode
os_entry(void)
{
m_Buddy buddy = m_Buddy_create(os_alloc(MEGA(2)), MEGA(2));
m_Allocator perm = m_BUDDY_ALLOCATOR(buddy);
JobQueue *queue = JobQueue_start(8, &perm);
for (I64 i = 0; i < 255; i++) {
DataToPass *dtp = m_MAKE(DataToPass, 1, &perm);
dtp->job_num = i;
dtp->random_num = rand_next() % 100;
JobQueue_dispatch(queue, JOB(printJob, dtp));
}
JobQueue_completeAll(queue);
return os_ErrorCode_success;
}

100
bastd/job.c Normal file
View file

@ -0,0 +1,100 @@
#ifndef BASTD_JOB_C
#define BASTD_JOB_C
typedef void (*JobProc)(void *args);
typedef struct Job Job;
struct Job {
JobProc proc;
void *args;
};
#define JOB(p, a) (Job){.proc = p, .args = (void *)a}
#define MAX_NUM_JOBS 256
typedef struct JobQueue JobQueue;
struct JobQueue {
U64 next_read;
U64 next_write;
U64 complete_len;
U64 complete_goal;
Job jobs[MAX_NUM_JOBS];
os_Semaphore semaphore;
};
FUNCTION B8
JobQueue__nextJob(JobQueue *queue)
{
B8 done = FALSE;
U64 next_read = queue->next_read;
U64 new_next_read = (next_read + 1) % MAX_NUM_JOBS;
if (next_read != queue->next_write) {
U64 index = os_atomic_compareExchange64((I64 volatile *)&queue->next_read, new_next_read, next_read);
if (index == next_read) {
done = TRUE;
Job *job = queue->jobs + index;
job->proc(job->args);
os_atomic_increment64((I64 volatile *)&queue->complete_len);
}
}
return done;
}
CALLBACK_EXPORT U32
JobQueue__threadProc(void *args)
{
JobQueue *queue = (JobQueue *)args;
for (;;) {
if (!JobQueue__nextJob(queue)) {
os_Semaphore_wait(queue->semaphore);
}
}
}
FUNCTION JobQueue *
JobQueue_start(int num_threads, m_Allocator *perm)
{
JobQueue *res = m_MAKE(JobQueue, 1, perm);
res->semaphore = os_Semaphore_create(0, num_threads);
for (int i = 0; i < num_threads; i++) {
os_Thread t = os_Thread_start(JobQueue__threadProc, res);
os_Thread_detach(&t);
}
return res;
}
// Should be called on main thread for now. Maybe need to sync up later
FUNCTION void
JobQueue_dispatch(JobQueue *queue, Job job)
{
U64 next_write = queue->next_write;
U64 new_next_write = (next_write + 1) % MAX_NUM_JOBS;
//ASSERT(new_next_write != queue->next_read, "This will override the current entry");
Job *job_to_write = queue->jobs + next_write;
*job_to_write = job;
queue->complete_goal++;
os_WRITE_BARRIER;
queue->next_write = new_next_write;
os_Semaphore_increment(queue->semaphore, 1);
}
FUNCTION void
JobQueue_completeAll(JobQueue *queue)
{
while (queue->complete_len != queue->complete_goal) {
JobQueue__nextJob(queue);
}
queue->complete_len = 0;
queue->complete_goal = 0;
}
#endif//BASTD_JOB_C

View file

@ -9,6 +9,26 @@ FUNCTION U64 os_getFileSize(int fd);
FUNCTION int os_openFile(U8 *filename, B8 always_create); FUNCTION int os_openFile(U8 *filename, B8 always_create);
FUNCTION B8 os_closeFile(int fd); FUNCTION B8 os_closeFile(int fd);
typedef struct os_Thread os_Thread;
struct os_Thread; // Depends on the OS
typedef U32 (*os_ThreadProc)(void *);
FUNCTION os_Thread os_Thread_start(os_ThreadProc proc, void *ctx);
FUNCTION void os_Thread_detach(os_Thread *t);
FUNCTION void os_Thread_join(os_Thread *t);
FUNCTION I64 os_atomic_compareExchange64(I64 volatile *dst, I64 exchange, I64 compare);
FUNCTION I64 os_atomic_increment64(I64 volatile *dst);
FUNCTION I64 os_atomic_decrement64(I64 volatile *dst);
#define os_WRITE_BARRIER // Depends on the OS
#define os_READ_BARRIER // Depends on the OS
typedef struct os_Semaphore os_Semaphore;
struct os_Semaphore; // Depends on the OS
FUNCTION os_Semaphore os_Semaphore_create(I32 initial_count, I32 max_count);
FUNCTION B8 os_Semaphore_increment(os_Semaphore semaphore);
FUNCTION void os_Semaphore_wait(os_Semaphore semaphore);
typedef int os_ErrorCode; typedef int os_ErrorCode;
enum { enum {
os_ErrorCode_success = 0, os_ErrorCode_success = 0,
@ -16,6 +36,8 @@ enum {
}; };
CALLBACK_EXPORT os_ErrorCode os_entry(void); CALLBACK_EXPORT os_ErrorCode os_entry(void);
FUNCTION U64 os_rdtsc(void);
#if defined(OS_WINDOWS) #if defined(OS_WINDOWS)
#include "os_windows.c" #include "os_windows.c"

View file

@ -2,10 +2,13 @@
#define BASTD_OS_WINDOWS_C #define BASTD_OS_WINDOWS_C
#include <windows.h> #include <windows.h>
#include <intrin.h>
#include <shellapi.h> #include <shellapi.h>
#include <io.h> #include <io.h>
#define os_DEBUGBREAK() __debugbreak(); #define os_DEBUGBREAK() __debugbreak();
#define os_WRITE_BARRIER _WriteBarrier()
#define os_READ_BARRIER _ReadBarrier()
FUNCTION void FUNCTION void
os_abort(char *msg) os_abort(char *msg)
@ -22,7 +25,6 @@ os_alloc(U64 cap)
return VirtualAlloc(NIL, cap, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); return VirtualAlloc(NIL, cap, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
} }
FUNCTION B8 FUNCTION B8
os_write(int fd, U8 *buf, int len) os_write(int fd, U8 *buf, int len)
{ {
@ -106,6 +108,84 @@ os_getArgcAndArgv(int *argc)
return argv; return argv;
} }
struct os_Thread {
HANDLE raw;
};
FUNCTION os_Thread
os_Thread_start(os_ThreadProc proc, void *ctx)
{
os_Thread res = {0};
res.raw = CreateThread(NIL, 0, proc, ctx, 0, NIL);
return res;
}
FUNCTION void
os_Thread_detach(os_Thread *t)
{
if (t->raw == NIL) return;
CloseHandle(t->raw);
t->raw = NIL;
}
FUNCTION void
os_Thread_join(os_Thread *t)
{
if (t->raw == NIL) return;
WaitForSingleObject(t->raw, INFINITE);
os_Thread_detach(t);
}
FUNCTION I64
os_atomic_compareExchange64(I64 volatile *dst, I64 exchange, I64 compare)
{
return InterlockedCompareExchange64(dst, exchange, compare);
}
FUNCTION I64
os_atomic_increment64(I64 volatile *dst)
{
return InterlockedIncrement64(dst);
}
FUNCTION I64
os_atomic_decrement64(I64 volatile *dst)
{
return InterlockedDecrement64(dst);
}
struct os_Semaphore {
HANDLE raw;
};
FUNCTION os_Semaphore
os_Semaphore_create(I32 initial_count, I32 max_count)
{
os_Semaphore res = {0};
res.raw = CreateSemaphoreExA(NIL, initial_count, max_count, NIL, 0, SEMAPHORE_ALL_ACCESS);
return res;
}
FUNCTION B8
os_Semaphore_increment(os_Semaphore semaphore, I32 count)
{
return ReleaseSemaphore(semaphore.raw, count, NIL);
}
FUNCTION void
os_Semaphore_wait(os_Semaphore semaphore)
{
WaitForSingleObjectEx(semaphore.raw, INFINITE, FALSE);
}
FUNCTION U64
os_rdtsc(void)
{
return __rdtsc();
}
#if defined(BASTD_CLI) #if defined(BASTD_CLI)
CALLBACK_EXPORT int CALLBACK_EXPORT int

102
bastd/rand.c Normal file
View file

@ -0,0 +1,102 @@
#ifndef BASTD_RAND_C
#define BASTD_RAND_C
// standard xoshiro256**, idk what any of ts means but it works :P
FUNCTION inline U64
rand_rotl(U64 x, int k)
{
return (x << k) | (x >> (64 - k));
}
GLOBAL_VAR U64 rand_seed[4];
FUNCTION U64
rand_generateSeed(U64 x)
{
// SplitMix64
U64 z = (x += 0x9e3779b97f4a7c15);
z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9;
z = (z ^ (z >> 27)) * 0x94d049bb133111eb;
return z ^ (z >> 31);
}
FUNCTION U64
rand_next(void)
{
U64 rdtscp_val = os_rdtsc();
for (int i = 0; i < COUNT_OF(rand_seed); i++) {
if (rand_seed[i] == 0) {
rand_seed[i] = rand_generateSeed(rdtscp_val);
}
}
U64 res = rand_rotl(rand_seed[1] * 5, 7) * 9;
U64 t = rand_seed[1] << 17;
rand_seed[2] ^= rand_seed[0];
rand_seed[3] ^= rand_seed[1];
rand_seed[1] ^= rand_seed[2];
rand_seed[0] ^= rand_seed[3];
rand_seed[2] ^= t;
rand_seed[3] = rand_rotl(rand_seed[3], 45);
return res;
}
FUNCTION void
rand_jump(void) {
LOCAL_PERSIST const U64 JUMP[] = { 0x180ec6d33cfd0aba, 0xd5a61266f0c9392c, 0xa9582618e03fc9aa, 0x39abdc4529b1661c };
U64 s0 = 0;
U64 s1 = 0;
U64 s2 = 0;
U64 s3 = 0;
for(int i = 0; i < COUNT_OF(JUMP); i++) {
for(int b = 0; b < 64; b++) {
if (JUMP[i] & UINT64_C(1) << b) {
s0 ^= rand_seed[0];
s1 ^= rand_seed[1];
s2 ^= rand_seed[2];
s3 ^= rand_seed[3];
}
rand_next();
}
}
rand_seed[0] = s0;
rand_seed[1] = s1;
rand_seed[2] = s2;
rand_seed[3] = s3;
}
FUNCTION void
rand_longJump(void) {
LOCAL_PERSIST const U64 LONG_JUMP[] = { 0x76e15d3efefdcbbf, 0xc5004e441c522fb3, 0x77710069854ee241, 0x39109bb02acbe635 };
U64 s0 = 0;
U64 s1 = 0;
U64 s2 = 0;
U64 s3 = 0;
for(int i = 0; i < COUNT_OF(LONG_JUMP); i++) {
for(int b = 0; b < 64; b++) {
if (LONG_JUMP[i] & UINT64_C(1) << b) {
s0 ^= rand_seed[0];
s1 ^= rand_seed[1];
s2 ^= rand_seed[2];
s3 ^= rand_seed[3];
}
rand_next();
}
}
rand_seed[0] = s0;
rand_seed[1] = s1;
rand_seed[2] = s2;
rand_seed[3] = s3;
}
#endif

View file

@ -10,6 +10,7 @@ REM cl ..\bastd\examples\memory.c %debugflags%
REM cl ..\bastd\examples\fib_slice.c %debugflags% REM cl ..\bastd\examples\fib_slice.c %debugflags%
REM cl ..\bastd\examples\string.c %debugflags% REM cl ..\bastd\examples\string.c %debugflags%
REM cl ..\bastd\examples\buffer.c %debugflags% REM cl ..\bastd\examples\buffer.c %debugflags%
REM cl ..\bastd\examples\job_random.c %debugflags%
REM This builds your application with debug symbols. REM This builds your application with debug symbols.
cl ..\main.c %debugflags% cl ..\main.c %debugflags%