Trying to improve threading, thread queue
This commit is contained in:
30
src/basic/thread_queue.h
Normal file
30
src/basic/thread_queue.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#define WORK_FUNCTION(name) void name(void *data)
|
||||
typedef WORK_FUNCTION(WorkQueueCallback);
|
||||
|
||||
struct WorkQueueEntry {
|
||||
WorkQueueCallback *callback;
|
||||
void *data;
|
||||
};
|
||||
|
||||
struct ThreadCtx {
|
||||
int thread_index;
|
||||
};
|
||||
|
||||
struct WorkQueue {
|
||||
WorkQueueEntry entries[256];
|
||||
int64_t volatile index_to_write;
|
||||
int64_t volatile index_to_read;
|
||||
int64_t volatile completion_index;
|
||||
int64_t volatile completion_goal;
|
||||
void *semaphore;
|
||||
};
|
||||
|
||||
struct ThreadStartupInfo {
|
||||
uint32_t thread_id;
|
||||
int32_t thread_index;
|
||||
WorkQueue *queue;
|
||||
};
|
||||
|
||||
void PushWork(WorkQueue *wq, void *data, WorkQueueCallback *callback);
|
||||
void InitWorkQueue(WorkQueue *queue, uint32_t thread_count, ThreadStartupInfo *info);
|
||||
void WaitUntilCompletion(WorkQueue *wq);
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "filesystem.h"
|
||||
#include "thread_queue.h"
|
||||
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
@@ -8,6 +9,9 @@
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include <intrin.h>
|
||||
|
||||
#include "win32_thread.cpp"
|
||||
|
||||
// Basic begin
|
||||
void *VReserve(size_t size) {
|
||||
|
||||
83
src/basic/win32_thread.cpp
Normal file
83
src/basic/win32_thread.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
int64_t AtomicIncrement(volatile int64_t *i) {
|
||||
return InterlockedIncrement64(i);
|
||||
}
|
||||
|
||||
int64_t AtomicCompareAndSwap(volatile int64_t *dst, int64_t exchange, int64_t comperand) {
|
||||
return InterlockedCompareExchange64(dst, exchange, comperand);
|
||||
}
|
||||
|
||||
void PushWork(WorkQueue *wq, void *data, WorkQueueCallback *callback) {
|
||||
uint32_t new_index = (wq->index_to_write + 1) % Lengthof(wq->entries);
|
||||
assert(new_index != wq->index_to_read);
|
||||
|
||||
WorkQueueEntry *entry = wq->entries + wq->index_to_write;
|
||||
entry->data = data;
|
||||
entry->callback = callback;
|
||||
|
||||
wq->completion_goal += 1;
|
||||
_WriteBarrier();
|
||||
wq->index_to_write = new_index;
|
||||
ReleaseSemaphore(wq->semaphore, 1, 0);
|
||||
}
|
||||
|
||||
bool TryDoingWork(WorkQueue *wq) {
|
||||
bool should_sleep = false;
|
||||
int64_t original_index_to_read = wq->index_to_read;
|
||||
int64_t new_index_to_read = (original_index_to_read + 1) % Lengthof(wq->entries);
|
||||
if (original_index_to_read != wq->index_to_write) {
|
||||
int64_t index = AtomicCompareAndSwap(&wq->index_to_read, new_index_to_read, original_index_to_read);
|
||||
if (index == original_index_to_read) {
|
||||
WorkQueueEntry *entry = wq->entries + index;
|
||||
entry->callback(entry->data);
|
||||
AtomicIncrement(&wq->completion_index);
|
||||
}
|
||||
} else {
|
||||
should_sleep = true;
|
||||
}
|
||||
return should_sleep;
|
||||
}
|
||||
|
||||
DWORD WINAPI WorkQueueThreadEntry(LPVOID param) {
|
||||
auto ti = (ThreadStartupInfo *)param;
|
||||
|
||||
ThreadCtx ctx = {};
|
||||
ctx.thread_index = ti->thread_index;
|
||||
InitScratch();
|
||||
for (;;) {
|
||||
if (TryDoingWork(ti->queue)) {
|
||||
WaitForSingleObject(ti->queue->semaphore, INFINITE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InitWorkQueue(WorkQueue *queue, uint32_t thread_count, ThreadStartupInfo *info) {
|
||||
queue->index_to_read = 0;
|
||||
queue->index_to_write = 0;
|
||||
queue->completion_index = 0;
|
||||
queue->completion_goal = 0;
|
||||
queue->semaphore = CreateSemaphoreExA(0, 0, thread_count, 0, 0, SEMAPHORE_ALL_ACCESS);
|
||||
Assert(queue->semaphore != INVALID_HANDLE_VALUE);
|
||||
|
||||
for (uint32_t i = 0; i < thread_count; i++) {
|
||||
ThreadStartupInfo *ti = info + i;
|
||||
ti->thread_index = i;
|
||||
ti->queue = queue;
|
||||
|
||||
DWORD thread_id = 0;
|
||||
HANDLE thread_handle = CreateThread(0, 0, WorkQueueThreadEntry, ti, 0, &thread_id);
|
||||
Assert(thread_handle != INVALID_HANDLE_VALUE);
|
||||
ti->thread_id = thread_id;
|
||||
CloseHandle(thread_handle);
|
||||
}
|
||||
}
|
||||
|
||||
void WaitUntilCompletion(WorkQueue *wq) {
|
||||
while (wq->completion_goal != wq->completion_index) {
|
||||
TryDoingWork(wq);
|
||||
}
|
||||
}
|
||||
|
||||
bool IsWorkCompleted(WorkQueue *wq) {
|
||||
bool result = wq->completion_goal == wq->completion_index;
|
||||
return result;
|
||||
}
|
||||
Reference in New Issue
Block a user