feat: async io for windows
Submit async-io to the OS. Next step is replacing all FIO calls with async-io and removing the old code.
This commit is contained in:
		
							parent
							
								
									97adb0dffa
								
							
						
					
					
						commit
						1a4a2109ca
					
				
							
								
								
									
										29
									
								
								docs/NOTES_aio_assets.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								docs/NOTES_aio_assets.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,29 @@
 | 
				
			|||||||
 | 
					# AIO & Assets
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Async IO:
 | 
				
			||||||
 | 
					- Batches of loads (vy_load_batch)
 | 
				
			||||||
 | 
					- Each load: file-id, offset-in-file, num-bytes, destination buffer
 | 
				
			||||||
 | 
					- SubmitLoadBatch() -> Handle for whole batch or handles for each file op?
 | 
				
			||||||
 | 
					- Reason: Better saturation of disk interface
 | 
				
			||||||
 | 
					- Batch interacts nicely with asset system described below:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Assets
 | 
				
			||||||
 | 
					- Have a tool that detects asset dependencies (maybe the same tool that bakes assets into their runtime format)
 | 
				
			||||||
 | 
					- i.e. world-cell -> meshes -> textures
 | 
				
			||||||
 | 
					- Bake into a single binary (asset_meta.bin)
 | 
				
			||||||
 | 
					- Have that file loaded at runtime at all times
 | 
				
			||||||
 | 
					- DetermineAssetDependencies() -> CreateLoadBatch(asset-id) 
 | 
				
			||||||
 | 
					- CreateLoadBatch() could take a cache into account
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### File Storage in Memory 
 | 
				
			||||||
 | 
					- Linked lists of block-regions with fixed size blocks (bitmap allocator)
 | 
				
			||||||
 | 
					- E.g. 1, 2, 3, 5 mb (maybe down to kb) blocks
 | 
				
			||||||
 | 
					- Blocks have refcounts, explicitly increased/decreased
 | 
				
			||||||
 | 
					- Either lock the whole region or lock individual blocks
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Cache
 | 
				
			||||||
 | 
					- Hold one reference to storage
 | 
				
			||||||
 | 
					- Track how much space is taken for cache
 | 
				
			||||||
 | 
					- Evict LRU(?) once taken space exceeds threshold
 | 
				
			||||||
 | 
					- Copies could be managers handed to the cache
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -50,6 +50,8 @@ runtime_lib = library('vyrt',
 | 
				
			|||||||
  'src/runtime/app.h',
 | 
					  'src/runtime/app.h',
 | 
				
			||||||
  'src/runtime/dynamic_libs.h',
 | 
					  'src/runtime/dynamic_libs.h',
 | 
				
			||||||
  'src/runtime/jobs.h',
 | 
					  'src/runtime/jobs.h',
 | 
				
			||||||
 | 
					  'src/runtime/aio.h',
 | 
				
			||||||
 | 
					  'src/runtime/file_tab.h',
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  'src/runtime/error_report.c',
 | 
					  'src/runtime/error_report.c',
 | 
				
			||||||
  'src/runtime/gfx_main.c',
 | 
					  'src/runtime/gfx_main.c',
 | 
				
			||||||
@ -63,6 +65,8 @@ runtime_lib = library('vyrt',
 | 
				
			|||||||
  'src/runtime/app.c',
 | 
					  'src/runtime/app.c',
 | 
				
			||||||
  'src/runtime/dynamic_libs.c',
 | 
					  'src/runtime/dynamic_libs.c',
 | 
				
			||||||
  'src/runtime/jobs.c',
 | 
					  'src/runtime/jobs.c',
 | 
				
			||||||
 | 
					  'src/runtime/aio.c',
 | 
				
			||||||
 | 
					  'src/runtime/file_tab.c',
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  # Contrib Sources
 | 
					  # Contrib Sources
 | 
				
			||||||
  'contrib/xxhash/xxhash.c',
 | 
					  'contrib/xxhash/xxhash.c',
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										247
									
								
								src/runtime/aio.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										247
									
								
								src/runtime/aio.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,247 @@
 | 
				
			|||||||
 | 
					#include "aio.h"
 | 
				
			||||||
 | 
					#include "threading.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef _WIN32
 | 
				
			||||||
 | 
					#define WIN32_LEAN_AND_MEAN
 | 
				
			||||||
 | 
					#include <Windows.h>
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <assert.h>
 | 
				
			||||||
 | 
					#include <stdlib.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Maintain a ringbuffer of pending operations */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct {
 | 
				
			||||||
 | 
					#ifdef _WIN32
 | 
				
			||||||
 | 
					    HANDLE file_handle;
 | 
				
			||||||
 | 
					    OVERLAPPED overlapped;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					    volatile vy_aio_state state;
 | 
				
			||||||
 | 
					} vy_aio;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct {
 | 
				
			||||||
 | 
					    vy_mutex *guard;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    vy_aio *storage;
 | 
				
			||||||
 | 
					    uint32_t capacity;
 | 
				
			||||||
 | 
					    uint32_t head;
 | 
				
			||||||
 | 
					    uint32_t tail;
 | 
				
			||||||
 | 
					} vy_aio_ringbuffer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct {
 | 
				
			||||||
 | 
					    vy_aio *a;
 | 
				
			||||||
 | 
					    vy_aio *b;
 | 
				
			||||||
 | 
					    uint32_t a_count;
 | 
				
			||||||
 | 
					} vy_ringbuffer_space;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static vy_aio_ringbuffer _ringbuffer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static vy_ringbuffer_space ReserveRingbufferSpace(uint32_t count) {
 | 
				
			||||||
 | 
					    if (!vyLockMutex(_ringbuffer.guard)) {
 | 
				
			||||||
 | 
					        vy_ringbuffer_space failed = {NULL, NULL, 0};
 | 
				
			||||||
 | 
					        return failed;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    vy_ringbuffer_space result = {NULL, NULL, 0};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (_ringbuffer.head >= _ringbuffer.tail) {
 | 
				
			||||||
 | 
					        if (_ringbuffer.head + count <= _ringbuffer.capacity) {
 | 
				
			||||||
 | 
					            result.a_count   = count;
 | 
				
			||||||
 | 
					            result.a         = &_ringbuffer.storage[_ringbuffer.head];
 | 
				
			||||||
 | 
					            _ringbuffer.head =
 | 
				
			||||||
 | 
					                (_ringbuffer.head + count) % _ringbuffer.capacity;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            /* Check if enough space is free at the end */
 | 
				
			||||||
 | 
					            uint32_t a_count   = _ringbuffer.capacity - _ringbuffer.head;
 | 
				
			||||||
 | 
					            uint32_t b_count = count - a_count;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (b_count <= _ringbuffer.tail) {
 | 
				
			||||||
 | 
					                result.a_count   = a_count;
 | 
				
			||||||
 | 
					                result.a         = &_ringbuffer.storage[_ringbuffer.head];
 | 
				
			||||||
 | 
					                result.b         = &_ringbuffer.storage[0];
 | 
				
			||||||
 | 
					                _ringbuffer.head = b_count;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                /* Not enough space, we would overwrite the tail */
 | 
				
			||||||
 | 
					                vyLog("aio", "Ringbuffer is full.");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        /* Head is lower than tail */
 | 
				
			||||||
 | 
					        uint32_t num_free = _ringbuffer.tail - _ringbuffer.head;
 | 
				
			||||||
 | 
					        if (count < num_free) {
 | 
				
			||||||
 | 
					            result.a_count = count;
 | 
				
			||||||
 | 
					            result.a       = &_ringbuffer.storage[_ringbuffer.head];
 | 
				
			||||||
 | 
					            _ringbuffer.head =
 | 
				
			||||||
 | 
					                (_ringbuffer.head + count) % _ringbuffer.capacity;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            /* Not enough space, we would overwrite the tail */
 | 
				
			||||||
 | 
					            vyLog("aio", "Ringbuffer is full.");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    vyUnlockMutex(_ringbuffer.guard);
 | 
				
			||||||
 | 
					    return result;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef _WIN32
 | 
				
			||||||
 | 
					static void win32CompletionRoutine(DWORD error_code,
 | 
				
			||||||
 | 
					                                   DWORD num_bytes_transfered,
 | 
				
			||||||
 | 
					                                   LPOVERLAPPED overlapped) {
 | 
				
			||||||
 | 
					    vy_aio *op = (vy_aio*)overlapped->hEvent;
 | 
				
			||||||
 | 
					    assert(op->state == VY_AIO_STATE_PENDING);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (error_code != ERROR_SUCCESS) {
 | 
				
			||||||
 | 
					        op->state = VY_AIO_STATE_FAILED;
 | 
				
			||||||
 | 
					        vyLog("aio", "Async io failed: %u", error_code);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        op->state = VY_AIO_STATE_FINISHED;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    CloseHandle(op->file_handle);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					VY_DLLEXPORT vy_result vyInitAIO(unsigned int max_concurrent_operations) {
 | 
				
			||||||
 | 
					    _ringbuffer.guard = vyCreateMutex();
 | 
				
			||||||
 | 
					    if (!_ringbuffer.guard) {
 | 
				
			||||||
 | 
					        return VY_AIO_OUT_OF_MEMORY;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (max_concurrent_operations == 0)
 | 
				
			||||||
 | 
					        max_concurrent_operations = 1024;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    _ringbuffer.storage = calloc(max_concurrent_operations, sizeof(vy_aio));
 | 
				
			||||||
 | 
					    if (!_ringbuffer.storage)
 | 
				
			||||||
 | 
					        return VY_AIO_OUT_OF_MEMORY;
 | 
				
			||||||
 | 
					    _ringbuffer.head = 0;
 | 
				
			||||||
 | 
					    _ringbuffer.tail = 0;
 | 
				
			||||||
 | 
					    _ringbuffer.capacity = max_concurrent_operations;
 | 
				
			||||||
 | 
					    return VY_SUCCESS;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					VY_DLLEXPORT void vyShutdownAIO(void) {
 | 
				
			||||||
 | 
					    vyDestroyMutex(_ringbuffer.guard);
 | 
				
			||||||
 | 
					    free(_ringbuffer.storage);
 | 
				
			||||||
 | 
					    _ringbuffer.capacity = 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					VY_DLLEXPORT vy_result vySubmitLoadBatch(const vy_load_batch *batch,
 | 
				
			||||||
 | 
					                            vy_aio_handle *handles) {
 | 
				
			||||||
 | 
					    if (batch->num_loads > VY_LOAD_BATCH_MAX_SIZE) {
 | 
				
			||||||
 | 
					        return VY_AIO_LOAD_TOO_LARGE;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    vy_ringbuffer_space rbspace = ReserveRingbufferSpace(batch->num_loads);
 | 
				
			||||||
 | 
					    if (!rbspace.a) {
 | 
				
			||||||
 | 
					        vyReportError("aio", "Too many pending file operations");
 | 
				
			||||||
 | 
					        return VY_AIO_TOO_MANY_OPERATIONS;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (unsigned int i = 0; i < batch->num_loads; ++i) {
 | 
				
			||||||
 | 
					        vy_aio *op = (i < rbspace.a_count) ? &rbspace.a[i]
 | 
				
			||||||
 | 
					                                           : &rbspace.b[i - rbspace.a_count];
 | 
				
			||||||
 | 
					        op->state = VY_AIO_STATE_PENDING;
 | 
				
			||||||
 | 
					        const char *file_path = vyGetFilePath(batch->loads[i].file);
 | 
				
			||||||
 | 
					        if (!file_path) {
 | 
				
			||||||
 | 
					            vyReportError("aio",
 | 
				
			||||||
 | 
					                          "Failed to resolve file path for a batched load");
 | 
				
			||||||
 | 
					            op->state  = VY_AIO_STATE_INVALID;
 | 
				
			||||||
 | 
					            handles[i] = VY_AIO_INVALID_HANDLE;
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					#ifdef _WIN32
 | 
				
			||||||
 | 
					        op->overlapped = (OVERLAPPED){
 | 
				
			||||||
 | 
					            /* ReadFileEx does not use hEvent and we are free to use it for our own purposes. */
 | 
				
			||||||
 | 
					            .hEvent       = (HANDLE)(op),
 | 
				
			||||||
 | 
					            .Internal     = 0,
 | 
				
			||||||
 | 
					            .InternalHigh = 0,
 | 
				
			||||||
 | 
					            .Offset       = (DWORD)(batch->loads[i].offset & MAXDWORD),
 | 
				
			||||||
 | 
					            .OffsetHigh   = (DWORD)(batch->loads[i].offset >> 32),
 | 
				
			||||||
 | 
					            .Pointer      = NULL,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        WCHAR wpath[MAX_PATH];
 | 
				
			||||||
 | 
					        if (MultiByteToWideChar(CP_UTF8,
 | 
				
			||||||
 | 
					                                MB_PRECOMPOSED,
 | 
				
			||||||
 | 
					                                file_path,
 | 
				
			||||||
 | 
					                                -1,
 | 
				
			||||||
 | 
					                                wpath,
 | 
				
			||||||
 | 
					                                VY_ARRAY_COUNT(wpath)) == 0) {
 | 
				
			||||||
 | 
					            vyReportError("aio",
 | 
				
			||||||
 | 
					                          "MultiByteToWideChar failed with error code: %u",
 | 
				
			||||||
 | 
					                          GetLastError());
 | 
				
			||||||
 | 
					            op->state  = VY_AIO_STATE_FINISHED;
 | 
				
			||||||
 | 
					            handles[i] = VY_AIO_INVALID_HANDLE;
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        HANDLE file_handle = CreateFileW(wpath,
 | 
				
			||||||
 | 
					                                         GENERIC_READ,
 | 
				
			||||||
 | 
					                                         FILE_SHARE_READ,
 | 
				
			||||||
 | 
					                                         NULL,
 | 
				
			||||||
 | 
					                                         OPEN_EXISTING,
 | 
				
			||||||
 | 
					                                         FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
 | 
				
			||||||
 | 
					                                         NULL);
 | 
				
			||||||
 | 
					        if (file_handle == INVALID_HANDLE_VALUE) {
 | 
				
			||||||
 | 
					            vyReportError("aio",
 | 
				
			||||||
 | 
					                          "CreateFileW failed for file: %s with error code: %u",
 | 
				
			||||||
 | 
					                          file_path,
 | 
				
			||||||
 | 
					                          GetLastError());
 | 
				
			||||||
 | 
					            op->state  = VY_AIO_STATE_INVALID;
 | 
				
			||||||
 | 
					            handles[i] = VY_AIO_INVALID_HANDLE;
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        op->file_handle = file_handle;
 | 
				
			||||||
 | 
					        BOOL result = ReadFileEx(file_handle,
 | 
				
			||||||
 | 
					                                 batch->loads[i].dest,
 | 
				
			||||||
 | 
					                                 (DWORD)batch->loads[i].num_bytes,
 | 
				
			||||||
 | 
					                                 &op->overlapped,
 | 
				
			||||||
 | 
					                                 win32CompletionRoutine);
 | 
				
			||||||
 | 
					        DWORD err   = GetLastError();
 | 
				
			||||||
 | 
					        if (!result || err != ERROR_SUCCESS) {
 | 
				
			||||||
 | 
					            vyReportError("aio", "ReadFileEx failed with error code: %u", err);
 | 
				
			||||||
 | 
					            op->state = VY_AIO_STATE_FINISHED;
 | 
				
			||||||
 | 
					            handles[i] = VY_AIO_INVALID_HANDLE;
 | 
				
			||||||
 | 
					            CloseHandle(file_handle);
 | 
				
			||||||
 | 
					            op->file_handle = NULL;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /* Handle is the index into the ringbuffer + 1 */
 | 
				
			||||||
 | 
					        ptrdiff_t op_idx = op - _ringbuffer.storage;
 | 
				
			||||||
 | 
					        handles[i]       = (uint32_t)op_idx + 1;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return VY_SUCCESS;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					VY_DLLEXPORT volatile vy_aio_state vyGetAIOState(vy_aio_handle handle) {
 | 
				
			||||||
 | 
					    if (handle == VY_AIO_INVALID_HANDLE || handle > _ringbuffer.capacity)
 | 
				
			||||||
 | 
					        return VY_AIO_STATE_INVALID;
 | 
				
			||||||
 | 
					#ifdef _WIN32
 | 
				
			||||||
 | 
					    /* Give the compation function an opportunity to run */
 | 
				
			||||||
 | 
					    SleepEx(0, TRUE);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					    vyLockMutex(_ringbuffer.guard);
 | 
				
			||||||
 | 
					    vy_aio_state state = _ringbuffer.storage[handle - 1].state;
 | 
				
			||||||
 | 
					    vyUnlockMutex(_ringbuffer.guard);
 | 
				
			||||||
 | 
					    return state;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 VY_DLLEXPORT void vyReleaseAIO(vy_aio_handle handle) {
 | 
				
			||||||
 | 
					    if (handle == VY_AIO_INVALID_HANDLE || handle > _ringbuffer.capacity) {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    vyLockMutex(_ringbuffer.guard);
 | 
				
			||||||
 | 
					    _ringbuffer.storage[handle - 1].state = VY_AIO_STATE_INVALID;
 | 
				
			||||||
 | 
					    if (handle - 1 == _ringbuffer.tail) {
 | 
				
			||||||
 | 
					        /* Advance the tail such that it points to the last used slot. (Or to head, if the ringbuffer is now empty) */
 | 
				
			||||||
 | 
					        uint32_t i = _ringbuffer.tail;
 | 
				
			||||||
 | 
					        while ((_ringbuffer.storage[i].state == VY_AIO_STATE_INVALID) && i != _ringbuffer.head) {
 | 
				
			||||||
 | 
					            i = (i + 1) % _ringbuffer.capacity;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        _ringbuffer.tail = i;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    vyUnlockMutex(_ringbuffer.guard);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										66
									
								
								src/runtime/aio.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								src/runtime/aio.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,66 @@
 | 
				
			|||||||
 | 
					#ifndef VY_AIO_H
 | 
				
			||||||
 | 
					#define VY_AIO_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <stdint.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "runtime.h"
 | 
				
			||||||
 | 
					#include "file_tab.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct {
 | 
				
			||||||
 | 
					    size_t offset;    /**< Starting offset inside the file in bytes */
 | 
				
			||||||
 | 
					    size_t num_bytes; /**< Number of bytes to load */
 | 
				
			||||||
 | 
					    /** Destination buffer with at least @ num_bytes bytes.
 | 
				
			||||||
 | 
					     *  Must be valid until the load is finished.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    void *dest;
 | 
				
			||||||
 | 
					    vy_file_id file;  /**< Unique identifier for the file */
 | 
				
			||||||
 | 
					} vy_file_load;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define VY_LOAD_BATCH_MAX_SIZE 64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** A batch of loads that will be started together.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * The aio system will hand these to the OS.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					typedef struct {
 | 
				
			||||||
 | 
					    vy_file_load loads[VY_LOAD_BATCH_MAX_SIZE];
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /** Must be smaller or equal to @c VY_LOAD_BATCH_MAX_SIZE */
 | 
				
			||||||
 | 
					    unsigned int num_loads;
 | 
				
			||||||
 | 
					} vy_load_batch;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define VY_AIO_INVALID_HANDLE 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** Handle for an async io operation. Can be used to query the state and result. */
 | 
				
			||||||
 | 
					typedef uint32_t vy_aio_handle;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum {
 | 
				
			||||||
 | 
					    VY_AIO_LOAD_TOO_LARGE = (VY_SUCCESS + 1),
 | 
				
			||||||
 | 
					    VY_AIO_TOO_MANY_OPERATIONS,
 | 
				
			||||||
 | 
					    VY_AIO_OUT_OF_MEMORY,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef enum {
 | 
				
			||||||
 | 
					    VY_AIO_STATE_INVALID,
 | 
				
			||||||
 | 
					    VY_AIO_STATE_PENDING,
 | 
				
			||||||
 | 
					    VY_AIO_STATE_FINISHED,
 | 
				
			||||||
 | 
					    VY_AIO_STATE_FAILED,
 | 
				
			||||||
 | 
					} vy_aio_state;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					VY_DLLEXPORT vy_result vyInitAIO(unsigned int max_concurrent_operations);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					VY_DLLEXPORT void vyShutdownAIO(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					VY_DLLEXPORT vy_result vySubmitLoadBatch(const vy_load_batch *batch,
 | 
				
			||||||
 | 
					                                        vy_aio_handle *handles);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					VY_DLLEXPORT volatile vy_aio_state vyGetAIOState(vy_aio_handle handle);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Releases the internal storage for the operation.
 | 
				
			||||||
 | 
					 * The system is allowed to re-use the same handle value for new operations after this was called.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					VY_DLLEXPORT void vyReleaseAIO(vy_aio_handle handle);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
@ -2,6 +2,7 @@
 | 
				
			|||||||
#include "config.h"
 | 
					#include "config.h"
 | 
				
			||||||
#include "fio.h"
 | 
					#include "fio.h"
 | 
				
			||||||
#include "gfx.h"
 | 
					#include "gfx.h"
 | 
				
			||||||
 | 
					#include "aio.h"
 | 
				
			||||||
#include "renderer_api.h"
 | 
					#include "renderer_api.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
extern void __RegisterRuntimeCVars(void);
 | 
					extern void __RegisterRuntimeCVars(void);
 | 
				
			||||||
@ -43,6 +44,16 @@ VY_DLLEXPORT int vyWin32Entry(HINSTANCE hInstance,
 | 
				
			|||||||
        return 1;
 | 
					        return 1;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (vyInitFileTab(1024) != VY_SUCCESS) {
 | 
				
			||||||
 | 
					        vyReportError("FTAB", "Init failed.");
 | 
				
			||||||
 | 
					        return 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (vyInitAIO(0) != VY_SUCCESS) {
 | 
				
			||||||
 | 
					        vyReportError("AIO", "Init failed.");
 | 
				
			||||||
 | 
					        return 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    WNDCLASSEXW wndclass = {
 | 
					    WNDCLASSEXW wndclass = {
 | 
				
			||||||
        .cbSize        = sizeof(wndclass),
 | 
					        .cbSize        = sizeof(wndclass),
 | 
				
			||||||
        .hInstance     = hInstance,
 | 
					        .hInstance     = hInstance,
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										131
									
								
								src/runtime/file_tab.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								src/runtime/file_tab.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,131 @@
 | 
				
			|||||||
 | 
					#include "file_tab.h"
 | 
				
			||||||
 | 
					#include "threading.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <xxhash/xxhash.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define NAME_CAP(cap) ((cap)*128)
 | 
				
			||||||
 | 
					typedef struct {
 | 
				
			||||||
 | 
					    vy_file_id *ids;
 | 
				
			||||||
 | 
					    unsigned int *name_offsets;
 | 
				
			||||||
 | 
					    char *names;
 | 
				
			||||||
 | 
					    unsigned int capacity;
 | 
				
			||||||
 | 
					    unsigned int name_head;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    vy_mutex *mutex;
 | 
				
			||||||
 | 
					} vy_file_tab;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static vy_file_tab _file_tab;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					vy_result vyInitFileTab(unsigned int max_files) {
 | 
				
			||||||
 | 
					    _file_tab.ids = calloc(max_files, sizeof(vy_file_id));
 | 
				
			||||||
 | 
					    if (!_file_tab.ids)
 | 
				
			||||||
 | 
					        return 1;
 | 
				
			||||||
 | 
					    _file_tab.name_offsets = calloc(max_files, sizeof(unsigned int));
 | 
				
			||||||
 | 
					    if (!_file_tab.name_offsets)
 | 
				
			||||||
 | 
					        return 1;
 | 
				
			||||||
 | 
					    _file_tab.names = malloc(NAME_CAP(max_files));
 | 
				
			||||||
 | 
					    if (!_file_tab.names)
 | 
				
			||||||
 | 
					        return 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    _file_tab.capacity  = max_files;
 | 
				
			||||||
 | 
					    _file_tab.name_head = 0;
 | 
				
			||||||
 | 
					    _file_tab.mutex = vyCreateMutex();
 | 
				
			||||||
 | 
					    return VY_SUCCESS;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void vyShutdownFileTab(void) {
 | 
				
			||||||
 | 
					    free(_file_tab.ids);
 | 
				
			||||||
 | 
					    free(_file_tab.names);
 | 
				
			||||||
 | 
					    free(_file_tab.name_offsets);
 | 
				
			||||||
 | 
					    vyDestroyMutex(_file_tab.mutex);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					vy_file_id vyGetFileId(const char *path) {
 | 
				
			||||||
 | 
					    vy_text_span span;
 | 
				
			||||||
 | 
					    span.start  = path;
 | 
				
			||||||
 | 
					    span.length = (unsigned int)strlen(path);
 | 
				
			||||||
 | 
					    return vyGetFileIdFromSpan(span);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					vy_file_id vyGetFileIdFromSpan(vy_text_span path) {
 | 
				
			||||||
 | 
					    /* Randomly choosen, aka finger smash keyboard */
 | 
				
			||||||
 | 
					    XXH64_hash_t seed = 15340978;
 | 
				
			||||||
 | 
					    vy_file_id fid    = (vy_file_id)XXH64(path.start, path.length, seed);
 | 
				
			||||||
 | 
					    if (fid == 0)
 | 
				
			||||||
 | 
					        fid = ~fid;
 | 
				
			||||||
 | 
					    return fid;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					VY_DLLEXPORT vy_file_id vyAddFileFromSpan(vy_text_span path) {
 | 
				
			||||||
 | 
					    vy_file_id fid = vyGetFileIdFromSpan(path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!vyLockMutex(_file_tab.mutex)) {
 | 
				
			||||||
 | 
					        vyReportError("fio", "Failed to lock the guard mutex.");
 | 
				
			||||||
 | 
					        return 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Hash Insert */
 | 
				
			||||||
 | 
					    unsigned int i    = 0;
 | 
				
			||||||
 | 
					    unsigned int base = (unsigned int)(fid % _file_tab.capacity);
 | 
				
			||||||
 | 
					    while (i < _file_tab.capacity) {
 | 
				
			||||||
 | 
					        unsigned int at = (base + i) % _file_tab.capacity;
 | 
				
			||||||
 | 
					        if (_file_tab.ids[at] == 0) {
 | 
				
			||||||
 | 
					            /* Insert */
 | 
				
			||||||
 | 
					            unsigned int slen = (unsigned int)path.length + 1;
 | 
				
			||||||
 | 
					            if ((_file_tab.name_head + slen) >= NAME_CAP(_file_tab.capacity)) {
 | 
				
			||||||
 | 
					                /* Out of name storage */
 | 
				
			||||||
 | 
					                fid = 0;
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            memcpy(_file_tab.names + _file_tab.name_head, path.start, slen);
 | 
				
			||||||
 | 
					            _file_tab.name_offsets[at] = _file_tab.name_head;
 | 
				
			||||||
 | 
					            _file_tab.ids[at]          = fid;
 | 
				
			||||||
 | 
					            _file_tab.name_head += slen;
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        } else if (_file_tab.ids[at] == fid) {
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        ++i;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Out of space */
 | 
				
			||||||
 | 
					    if (i == _file_tab.capacity)
 | 
				
			||||||
 | 
					        fid = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    vyUnlockMutex(_file_tab.mutex);
 | 
				
			||||||
 | 
					    return fid;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					VY_DLLEXPORT vy_file_id vyAddFile(const char *path) {
 | 
				
			||||||
 | 
					    vy_text_span span;
 | 
				
			||||||
 | 
					    span.start  = path;
 | 
				
			||||||
 | 
					    span.length = (unsigned int)strlen(path);
 | 
				
			||||||
 | 
					    return vyAddFileFromSpan(span);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					VY_DLLEXPORT const char *vyGetFilePath(vy_file_id fid) {
 | 
				
			||||||
 | 
					    /* Hash Lookup */
 | 
				
			||||||
 | 
					    if (fid == 0)
 | 
				
			||||||
 | 
					        return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!vyLockMutex(_file_tab.mutex)) {
 | 
				
			||||||
 | 
					        vyReportError("fio", "Failed to lock the guard mutex.");
 | 
				
			||||||
 | 
					        return 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    const char *result = NULL;
 | 
				
			||||||
 | 
					    unsigned int i     = 0;
 | 
				
			||||||
 | 
					    unsigned int base  = (unsigned int)(fid % _file_tab.capacity);
 | 
				
			||||||
 | 
					    while (i < _file_tab.capacity) {
 | 
				
			||||||
 | 
					        unsigned int at = (base + i) % _file_tab.capacity;
 | 
				
			||||||
 | 
					        if (_file_tab.ids[at] == fid) {
 | 
				
			||||||
 | 
					            result = _file_tab.names + _file_tab.name_offsets[at];
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        } else if (_file_tab.ids[at] == 0) {
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        ++i;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    vyUnlockMutex(_file_tab.mutex);
 | 
				
			||||||
 | 
					    return result;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										26
									
								
								src/runtime/file_tab.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								src/runtime/file_tab.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,26 @@
 | 
				
			|||||||
 | 
					#ifndef VY_FILE_TAB_H
 | 
				
			||||||
 | 
					#define VY_FILE_TAB_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "runtime.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <stdint.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* used to identify a file (XXH3 hash of the path) */
 | 
				
			||||||
 | 
					typedef uint64_t vy_file_id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					VY_DLLEXPORT vy_result vyInitFileTab(unsigned int max_files);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					VY_DLLEXPORT void vyShutdownFileTab(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					VY_DLLEXPORT vy_file_id vyGetFileId(const char *path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					VY_DLLEXPORT vy_file_id vyGetFileIdFromSpan(vy_text_span path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					VY_DLLEXPORT vy_file_id vyAddFile(const char *path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					VY_DLLEXPORT vy_file_id vyAddFileFromSpan(vy_text_span path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					VY_DLLEXPORT const char *vyGetFilePath(vy_file_id fid);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
@ -4,8 +4,6 @@
 | 
				
			|||||||
#include <stdlib.h>
 | 
					#include <stdlib.h>
 | 
				
			||||||
#include <string.h>
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <xxhash/xxhash.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifdef __linux__
 | 
					#ifdef __linux__
 | 
				
			||||||
    #include <pthread.h>
 | 
					    #include <pthread.h>
 | 
				
			||||||
#elif defined(_WIN32)
 | 
					#elif defined(_WIN32)
 | 
				
			||||||
@ -38,23 +36,7 @@ typedef struct {
 | 
				
			|||||||
#endif
 | 
					#endif
 | 
				
			||||||
} vy_fio_queue;
 | 
					} vy_fio_queue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define NAME_CAP(cap) ((cap)*128)
 | 
					 | 
				
			||||||
typedef struct {
 | 
					 | 
				
			||||||
    vy_file_id *ids;
 | 
					 | 
				
			||||||
    unsigned int *name_offsets;
 | 
					 | 
				
			||||||
    char *names;
 | 
					 | 
				
			||||||
    unsigned int capacity;
 | 
					 | 
				
			||||||
    unsigned int name_head;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifdef __linux__
 | 
					 | 
				
			||||||
    pthread_mutex_t mutex;
 | 
					 | 
				
			||||||
#elif defined(_WIN32)
 | 
					 | 
				
			||||||
    HANDLE mutex;
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
} vy_file_tab;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static vy_fio_queue _queue;
 | 
					static vy_fio_queue _queue;
 | 
				
			||||||
static vy_file_tab _file_tab;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef __linux__
 | 
					#ifdef __linux__
 | 
				
			||||||
static pthread_t _thread;
 | 
					static pthread_t _thread;
 | 
				
			||||||
@ -96,43 +78,6 @@ static void ShutdownFIOQueue(void) {
 | 
				
			|||||||
#endif
 | 
					#endif
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static bool InitFileTab(unsigned int max_files) {
 | 
					 | 
				
			||||||
    _file_tab.ids = calloc(max_files, sizeof(vy_file_id));
 | 
					 | 
				
			||||||
    if (!_file_tab.ids)
 | 
					 | 
				
			||||||
        return false;
 | 
					 | 
				
			||||||
    _file_tab.name_offsets = calloc(max_files, sizeof(unsigned int));
 | 
					 | 
				
			||||||
    if (!_file_tab.name_offsets)
 | 
					 | 
				
			||||||
        return false;
 | 
					 | 
				
			||||||
    _file_tab.names = malloc(NAME_CAP(max_files));
 | 
					 | 
				
			||||||
    if (!_file_tab.names)
 | 
					 | 
				
			||||||
        return false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    _file_tab.capacity  = max_files;
 | 
					 | 
				
			||||||
    _file_tab.name_head = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifdef __linux__
 | 
					 | 
				
			||||||
    if (pthread_mutex_init(&_file_tab.mutex, NULL) != 0)
 | 
					 | 
				
			||||||
        return false;
 | 
					 | 
				
			||||||
#elif defined(_WIN32)
 | 
					 | 
				
			||||||
    _file_tab.mutex = CreateMutex(NULL, FALSE, NULL);
 | 
					 | 
				
			||||||
    if (!_file_tab.mutex)
 | 
					 | 
				
			||||||
        return false;
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
    return true;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void ShutdownFileTab(void) {
 | 
					 | 
				
			||||||
    free(_file_tab.ids);
 | 
					 | 
				
			||||||
    free(_file_tab.names);
 | 
					 | 
				
			||||||
    free(_file_tab.name_offsets);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifdef __linux__
 | 
					 | 
				
			||||||
    pthread_mutex_destroy(&_file_tab.mutex);
 | 
					 | 
				
			||||||
#elif defined(_WIN32)
 | 
					 | 
				
			||||||
    CloseHandle(_file_tab.mutex);
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifdef __linux__
 | 
					#ifdef __linux__
 | 
				
			||||||
static void *linuxFIOThreadProc(void *);
 | 
					static void *linuxFIOThreadProc(void *);
 | 
				
			||||||
#elif defined(_WIN32)
 | 
					#elif defined(_WIN32)
 | 
				
			||||||
@ -141,13 +86,8 @@ static DWORD WINAPI win32FIOThreadProc(_In_ LPVOID);
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
VY_DLLEXPORT bool vyInitFIO(const vy_fio_config *config) {
 | 
					VY_DLLEXPORT bool vyInitFIO(const vy_fio_config *config) {
 | 
				
			||||||
    unsigned int queue_size = (config->queue_size) ? config->queue_size : 512;
 | 
					    unsigned int queue_size = (config->queue_size) ? config->queue_size : 512;
 | 
				
			||||||
    unsigned int max_file_count =
 | 
					 | 
				
			||||||
        (config->max_file_count) ? config->max_file_count : 512;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (!InitFIOQueue(queue_size))
 | 
					    if (!InitFIOQueue(queue_size))
 | 
				
			||||||
        return false;
 | 
					        return false;
 | 
				
			||||||
    if (!InitFileTab(max_file_count))
 | 
					 | 
				
			||||||
        return false;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef __linux__
 | 
					#ifdef __linux__
 | 
				
			||||||
    if (pthread_create(&_thread, NULL, linuxFIOThreadProc, NULL) != 0)
 | 
					    if (pthread_create(&_thread, NULL, linuxFIOThreadProc, NULL) != 0)
 | 
				
			||||||
@ -180,110 +120,6 @@ VY_DLLEXPORT void vyShutdownFIO(void) {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
    ShutdownFIOQueue();
 | 
					    ShutdownFIOQueue();
 | 
				
			||||||
    ShutdownFileTab();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
vy_file_id vyGetFileId(const char *path) {
 | 
					 | 
				
			||||||
    vy_text_span span;
 | 
					 | 
				
			||||||
    span.start  = path;
 | 
					 | 
				
			||||||
    span.length = (unsigned int)strlen(path);
 | 
					 | 
				
			||||||
    return vyGetFileIdFromSpan(span);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
vy_file_id vyGetFileIdFromSpan(vy_text_span path) {
 | 
					 | 
				
			||||||
    /* Randomly choosen, aka finger smash keyboard */
 | 
					 | 
				
			||||||
    XXH64_hash_t seed = 15340978;
 | 
					 | 
				
			||||||
    vy_file_id fid    = (vy_file_id)XXH64(path.start, path.length, seed);
 | 
					 | 
				
			||||||
    if (fid == 0)
 | 
					 | 
				
			||||||
        fid = ~fid;
 | 
					 | 
				
			||||||
    return fid;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
VY_DLLEXPORT vy_file_id vyAddFileFromSpan(vy_text_span path) {
 | 
					 | 
				
			||||||
    vy_file_id fid = vyGetFileIdFromSpan(path);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifdef __linux__
 | 
					 | 
				
			||||||
    pthread_mutex_lock(&_file_tab.mutex);
 | 
					 | 
				
			||||||
#elif defined(_WIN32)
 | 
					 | 
				
			||||||
    if (WaitForSingleObject(_file_tab.mutex, INFINITE) == WAIT_FAILED) {
 | 
					 | 
				
			||||||
        vyReportError("fio", "WaitForSingleObject failed with WAIT_FAILED!");
 | 
					 | 
				
			||||||
        return 0;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
    /* Hash Insert */
 | 
					 | 
				
			||||||
    unsigned int i    = 0;
 | 
					 | 
				
			||||||
    unsigned int base = (unsigned int)(fid % _file_tab.capacity);
 | 
					 | 
				
			||||||
    while (i < _file_tab.capacity) {
 | 
					 | 
				
			||||||
        unsigned int at = (base + i) % _file_tab.capacity;
 | 
					 | 
				
			||||||
        if (_file_tab.ids[at] == 0) {
 | 
					 | 
				
			||||||
            /* Insert */
 | 
					 | 
				
			||||||
            unsigned int slen = (unsigned int)path.length + 1;
 | 
					 | 
				
			||||||
            if ((_file_tab.name_head + slen) >= NAME_CAP(_file_tab.capacity)) {
 | 
					 | 
				
			||||||
                /* Out of name storage */
 | 
					 | 
				
			||||||
                fid = 0;
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            memcpy(_file_tab.names + _file_tab.name_head, path.start, slen);
 | 
					 | 
				
			||||||
            _file_tab.name_offsets[at] = _file_tab.name_head;
 | 
					 | 
				
			||||||
            _file_tab.ids[at]          = fid;
 | 
					 | 
				
			||||||
            _file_tab.name_head += slen;
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        } else if (_file_tab.ids[at] == fid) {
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        ++i;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /* Out of space */
 | 
					 | 
				
			||||||
    if (i == _file_tab.capacity)
 | 
					 | 
				
			||||||
        fid = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifdef __linux__
 | 
					 | 
				
			||||||
    pthread_mutex_unlock(&_file_tab.mutex);
 | 
					 | 
				
			||||||
#elif defined(_WIN32)
 | 
					 | 
				
			||||||
    ReleaseMutex(_file_tab.mutex);
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
    return fid;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
VY_DLLEXPORT vy_file_id vyAddFile(const char *path) {
 | 
					 | 
				
			||||||
    vy_text_span span;
 | 
					 | 
				
			||||||
    span.start  = path;
 | 
					 | 
				
			||||||
    span.length = (unsigned int)strlen(path);
 | 
					 | 
				
			||||||
    return vyAddFileFromSpan(span);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
VY_DLLEXPORT const char *vyGetFilePath(vy_file_id fid) {
 | 
					 | 
				
			||||||
    /* Hash Lookup */
 | 
					 | 
				
			||||||
    if (fid == 0)
 | 
					 | 
				
			||||||
        return NULL;
 | 
					 | 
				
			||||||
#ifdef __linux__
 | 
					 | 
				
			||||||
    pthread_mutex_lock(&_file_tab.mutex);
 | 
					 | 
				
			||||||
#elif defined(_WIN32)
 | 
					 | 
				
			||||||
    if (WaitForSingleObject(_file_tab.mutex, INFINITE) == WAIT_FAILED) {
 | 
					 | 
				
			||||||
        vyReportError("fio", "WaitForSingleObject failed with WAIT_FAILED!");
 | 
					 | 
				
			||||||
        return 0;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
    const char *result = NULL;
 | 
					 | 
				
			||||||
    unsigned int i     = 0;
 | 
					 | 
				
			||||||
    unsigned int base  = (unsigned int)(fid % _file_tab.capacity);
 | 
					 | 
				
			||||||
    while (i < _file_tab.capacity) {
 | 
					 | 
				
			||||||
        unsigned int at = (base + i) % _file_tab.capacity;
 | 
					 | 
				
			||||||
        if (_file_tab.ids[at] == fid) {
 | 
					 | 
				
			||||||
            result = _file_tab.names + _file_tab.name_offsets[at];
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        } else if (_file_tab.ids[at] == 0) {
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        ++i;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
#ifdef __linux__
 | 
					 | 
				
			||||||
    pthread_mutex_unlock(&_file_tab.mutex);
 | 
					 | 
				
			||||||
#elif defined(_WIN32)
 | 
					 | 
				
			||||||
    ReleaseMutex(_file_tab.mutex);
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
    return result;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
VY_DLLEXPORT vy_fio_handle vyEnqueueRead(vy_file_id fid) {
 | 
					VY_DLLEXPORT vy_fio_handle vyEnqueueRead(vy_file_id fid) {
 | 
				
			||||||
 | 
				
			|||||||
@ -6,6 +6,7 @@
 | 
				
			|||||||
#include <stdint.h>
 | 
					#include <stdint.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "runtime.h"
 | 
					#include "runtime.h"
 | 
				
			||||||
 | 
					#include "file_tab.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum {
 | 
					enum {
 | 
				
			||||||
    VY_FILE_BUFFER_FLAG_FILE_NOT_FOUND = 0x1,
 | 
					    VY_FILE_BUFFER_FLAG_FILE_NOT_FOUND = 0x1,
 | 
				
			||||||
@ -22,8 +23,6 @@ static inline bool vyWasFileBufferSuccessful(const vy_file_buffer *fb) {
 | 
				
			|||||||
    return fb->flags == 0;
 | 
					    return fb->flags == 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* used to identify a file (XXH3 hash of the path) */
 | 
					 | 
				
			||||||
typedef uint64_t vy_file_id;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef unsigned int vy_fio_handle;
 | 
					typedef unsigned int vy_fio_handle;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -36,16 +35,6 @@ VY_DLLEXPORT bool vyInitFIO(const vy_fio_config *config);
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
VY_DLLEXPORT void vyShutdownFIO(void);
 | 
					VY_DLLEXPORT void vyShutdownFIO(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
VY_DLLEXPORT vy_file_id vyGetFileId(const char *path);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
VY_DLLEXPORT vy_file_id vyGetFileIdFromSpan(vy_text_span path);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
VY_DLLEXPORT vy_file_id vyAddFile(const char *path);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
VY_DLLEXPORT vy_file_id vyAddFileFromSpan(vy_text_span path);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
VY_DLLEXPORT const char *vyGetFilePath(vy_file_id fid);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
VY_DLLEXPORT vy_fio_handle vyEnqueueRead(vy_file_id fid);
 | 
					VY_DLLEXPORT vy_fio_handle vyEnqueueRead(vy_file_id fid);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
VY_DLLEXPORT void vyAbortFIO(vy_fio_handle fio);
 | 
					VY_DLLEXPORT void vyAbortFIO(vy_fio_handle fio);
 | 
				
			||||||
 | 
				
			|||||||
@ -65,4 +65,90 @@ VY_DLLEXPORT void vyWaitOnConditionVar(vy_condition_var *var) {
 | 
				
			|||||||
    pthread_cond_wait(&var->cond, &var->mutex);
 | 
					    pthread_cond_wait(&var->cond, &var->mutex);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#elif defined(_WIN32)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define WIN32_LEAN_AND_MEAN
 | 
				
			||||||
 | 
					    #include <Windows.h>
 | 
				
			||||||
 | 
					struct vy_condition_var_s {
 | 
				
			||||||
 | 
					    CRITICAL_SECTION critical_section;
 | 
				
			||||||
 | 
					    CONDITION_VARIABLE cond;
 | 
				
			||||||
 | 
					    ptrdiff_t next_reusable;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #define MAX_CONDS 1024
 | 
				
			||||||
 | 
					vy_condition_var _conds[MAX_CONDS];
 | 
				
			||||||
 | 
					static ptrdiff_t _first_reusable = MAX_CONDS;
 | 
				
			||||||
 | 
					static ptrdiff_t _next           = 0;
 | 
				
			||||||
 | 
					static HANDLE _guard;
 | 
				
			||||||
 | 
					static INIT_ONCE _guard_init = INIT_ONCE_STATIC_INIT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static BOOL CALLBACK InitGuardFn(PINIT_ONCE initOnce,
 | 
				
			||||||
 | 
					                                 PVOID parameter,
 | 
				
			||||||
 | 
					                                 PVOID *context) {
 | 
				
			||||||
 | 
					    VY_UNUSED(initOnce);
 | 
				
			||||||
 | 
					    VY_UNUSED(parameter);
 | 
				
			||||||
 | 
					    VY_UNUSED(context);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    _guard = CreateMutexW(NULL, FALSE, NULL);
 | 
				
			||||||
 | 
					    return _guard != NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					VY_DLLEXPORT vy_condition_var *vyCreateConditionVar(void) {
 | 
				
			||||||
 | 
					    if (!InitOnceExecuteOnce(&_guard_init, InitGuardFn, NULL, NULL)) {
 | 
				
			||||||
 | 
					        vyReportError("core", "Failed to initialize the guard mutex.");
 | 
				
			||||||
 | 
					        return NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (WaitForSingleObjectEx(_guard, INFINITE, TRUE) != WAIT_OBJECT_0) {
 | 
				
			||||||
 | 
					        vyLog("core", "Failed to lock the guard variable: %u", GetLastError());
 | 
				
			||||||
 | 
					        return NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (_first_reusable < MAX_CONDS) {
 | 
				
			||||||
 | 
					        vy_condition_var *cond = &_conds[_first_reusable];
 | 
				
			||||||
 | 
					        _first_reusable        = cond->next_reusable;
 | 
				
			||||||
 | 
					        ReleaseMutex(_guard);
 | 
				
			||||||
 | 
					        return cond;
 | 
				
			||||||
 | 
					    } else if (_next < MAX_CONDS) {
 | 
				
			||||||
 | 
					        vy_condition_var *cond = &_conds[_next];
 | 
				
			||||||
 | 
					        if (!InitializeCriticalSectionAndSpinCount(&cond->critical_section, 4000)) {
 | 
				
			||||||
 | 
					            vyLog("core", "Condition variable creation failed");
 | 
				
			||||||
 | 
					            ReleaseMutex(_guard);
 | 
				
			||||||
 | 
					            return NULL;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        InitializeConditionVariable(&cond->cond);
 | 
				
			||||||
 | 
					        cond->next_reusable = MAX_CONDS;
 | 
				
			||||||
 | 
					        ++_next;
 | 
				
			||||||
 | 
					        ReleaseMutex(_guard);
 | 
				
			||||||
 | 
					        return cond;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    vyReportError("core", "Ran out of condition variable objects");
 | 
				
			||||||
 | 
					    ReleaseMutex(_guard);
 | 
				
			||||||
 | 
					    return NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					VY_DLLEXPORT void vyDestroyConditionVar(vy_condition_var *var) {
 | 
				
			||||||
 | 
					    ptrdiff_t index = var - &_conds[0];
 | 
				
			||||||
 | 
					    if (WaitForSingleObjectEx(_guard, INFINITE, TRUE) != WAIT_OBJECT_0) {
 | 
				
			||||||
 | 
					        vyLog("core", "Failed to lock the guard variable: %u", GetLastError());
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    var->next_reusable = _first_reusable;
 | 
				
			||||||
 | 
					    _first_reusable    = index;
 | 
				
			||||||
 | 
					    ReleaseMutex(_guard);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					VY_DLLEXPORT void vyLockConditionVar(vy_condition_var *var) {
 | 
				
			||||||
 | 
					    EnterCriticalSection(&var->critical_section);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					VY_DLLEXPORT void vyUnlockConditionVar(vy_condition_var *var, bool signal) {
 | 
				
			||||||
 | 
					    LeaveCriticalSection(&var->critical_section);
 | 
				
			||||||
 | 
					    if (signal)
 | 
				
			||||||
 | 
					        WakeAllConditionVariable(&var->cond);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					VY_DLLEXPORT void vyWaitOnConditionVar(vy_condition_var *var) {
 | 
				
			||||||
 | 
					    SleepConditionVariableCS(&var->cond, &var->critical_section, INFINITE);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
				
			|||||||
@ -14,8 +14,31 @@ struct vy_mutex_s {
 | 
				
			|||||||
static vy_mutex _mutex[MAX_MUTEX];
 | 
					static vy_mutex _mutex[MAX_MUTEX];
 | 
				
			||||||
static ptrdiff_t _first_reusable = MAX_MUTEX;
 | 
					static ptrdiff_t _first_reusable = MAX_MUTEX;
 | 
				
			||||||
static ptrdiff_t _next           = 0;
 | 
					static ptrdiff_t _next           = 0;
 | 
				
			||||||
 | 
					static HANDLE _guard;
 | 
				
			||||||
 | 
					static INIT_ONCE _guard_init = INIT_ONCE_STATIC_INIT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static BOOL CALLBACK InitGuardFn(PINIT_ONCE initOnce,
 | 
				
			||||||
 | 
					                                 PVOID parameter,
 | 
				
			||||||
 | 
					                                 PVOID *context) {
 | 
				
			||||||
 | 
					    VY_UNUSED(initOnce);
 | 
				
			||||||
 | 
					    VY_UNUSED(parameter);
 | 
				
			||||||
 | 
					    VY_UNUSED(context);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    _guard = CreateMutexW(NULL, FALSE, NULL);
 | 
				
			||||||
 | 
					    return _guard != NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
VY_DLLEXPORT vy_mutex *vyCreateMutex(void) {
 | 
					VY_DLLEXPORT vy_mutex *vyCreateMutex(void) {
 | 
				
			||||||
 | 
					    if (!InitOnceExecuteOnce(&_guard_init, InitGuardFn, NULL, NULL)) {
 | 
				
			||||||
 | 
					        vyReportError("core", "Failed to initialize the guard mutex.");
 | 
				
			||||||
 | 
					        return NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (WaitForSingleObjectEx(_guard, INFINITE, TRUE) != WAIT_OBJECT_0) {
 | 
				
			||||||
 | 
					        vyLog("core", "Failed to lock the guard variable: %u", GetLastError());
 | 
				
			||||||
 | 
					        return NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    if (_first_reusable < MAX_MUTEX) {
 | 
					    if (_first_reusable < MAX_MUTEX) {
 | 
				
			||||||
        vy_mutex *mtx   = &_mutex[_first_reusable];
 | 
					        vy_mutex *mtx   = &_mutex[_first_reusable];
 | 
				
			||||||
        _first_reusable = mtx->next_reusable;
 | 
					        _first_reusable = mtx->next_reusable;
 | 
				
			||||||
@ -42,9 +65,10 @@ VY_DLLEXPORT void vyDestroyMutex(vy_mutex *mutex) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
VY_DLLEXPORT bool vyLockMutex(vy_mutex *mutex) {
 | 
					VY_DLLEXPORT bool vyLockMutex(vy_mutex *mutex) {
 | 
				
			||||||
    return WaitForSingleObject(mutex->handle, INFINITE) == WAIT_OBJECT_0;
 | 
					    return WaitForSingleObjectEx(mutex->handle, INFINITE, TRUE) == WAIT_OBJECT_0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
v VY_DLLEXPORT bool vyUnlockMutex(vy_mutex *mutex) {
 | 
					
 | 
				
			||||||
 | 
					VY_DLLEXPORT bool vyUnlockMutex(vy_mutex *mutex) {
 | 
				
			||||||
    return ReleaseMutex(mutex->handle) != 0;
 | 
					    return ReleaseMutex(mutex->handle) != 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -2,6 +2,104 @@
 | 
				
			|||||||
#include "threading.h"
 | 
					#include "threading.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if defined(_WIN32)
 | 
					#if defined(_WIN32)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define WIN32_LEAN_AND_MEAN
 | 
				
			||||||
 | 
					#include <Windows.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct vy_thread_s {
 | 
				
			||||||
 | 
					    HANDLE handle;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ptrdiff_t next_reusable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    vy_thread_entry_fn *entry;
 | 
				
			||||||
 | 
					    void *param;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool needs_join;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #define MAX_THREADS 256
 | 
				
			||||||
 | 
					static vy_thread _threads[MAX_THREADS];
 | 
				
			||||||
 | 
					static ptrdiff_t _first_reusable = MAX_THREADS;
 | 
				
			||||||
 | 
					static ptrdiff_t _next           = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static HANDLE _guard;
 | 
				
			||||||
 | 
					static INIT_ONCE _guard_init = INIT_ONCE_STATIC_INIT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static BOOL CALLBACK InitGuardFn(PINIT_ONCE initOnce,
 | 
				
			||||||
 | 
					                                 PVOID parameter,
 | 
				
			||||||
 | 
					                                 PVOID *context) {
 | 
				
			||||||
 | 
					    VY_UNUSED(initOnce);
 | 
				
			||||||
 | 
					    VY_UNUSED(parameter);
 | 
				
			||||||
 | 
					    VY_UNUSED(context);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    _guard = CreateMutexW(NULL, FALSE, NULL);
 | 
				
			||||||
 | 
					    return _guard != NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static DWORD WINAPI win32ThreadWrapper(LPVOID arg) {
 | 
				
			||||||
 | 
					    vy_thread *user_thread  = arg;
 | 
				
			||||||
 | 
					    user_thread->needs_join = false;
 | 
				
			||||||
 | 
					    user_thread->entry(user_thread->param);
 | 
				
			||||||
 | 
					    user_thread->needs_join = true;
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					VY_DLLEXPORT vy_thread *vySpawnThread(vy_thread_entry_fn *entry, void *param) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!InitOnceExecuteOnce(&_guard_init, InitGuardFn, NULL, NULL)) {
 | 
				
			||||||
 | 
					        vyReportError("core", "Failed to initialize the guard mutex.");
 | 
				
			||||||
 | 
					        return NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    vy_thread *thrd = NULL;
 | 
				
			||||||
 | 
					    if (WaitForSingleObject(_guard, INFINITE) != WAIT_OBJECT_0) {
 | 
				
			||||||
 | 
					        vyLog("core", "Failed to lock the guard variable: %u", GetLastError());
 | 
				
			||||||
 | 
					        return NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (_first_reusable < MAX_THREADS) {
 | 
				
			||||||
 | 
					        thrd            = &_threads[_first_reusable];
 | 
				
			||||||
 | 
					        _first_reusable = thrd->next_reusable;
 | 
				
			||||||
 | 
					        if (thrd->needs_join) {
 | 
				
			||||||
 | 
					            WaitForSingleObject(thrd->handle, INFINITE);
 | 
				
			||||||
 | 
					            CloseHandle(thrd->handle);
 | 
				
			||||||
 | 
					            thrd->needs_join = false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    } else if (_next < MAX_THREADS) {
 | 
				
			||||||
 | 
					        thrd                = &_threads[_next];
 | 
				
			||||||
 | 
					        thrd->next_reusable = MAX_THREADS;
 | 
				
			||||||
 | 
					        ++_next;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (thrd) {
 | 
				
			||||||
 | 
					        thrd->entry = entry;
 | 
				
			||||||
 | 
					        thrd->param = param;
 | 
				
			||||||
 | 
					        thrd->handle = CreateThread(NULL, 0, win32ThreadWrapper, (LPVOID)thrd, 0, NULL);
 | 
				
			||||||
 | 
					        if (thrd->handle == NULL) {
 | 
				
			||||||
 | 
					            vyLog("core", "Thread creation failed");
 | 
				
			||||||
 | 
					            thrd = NULL;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        vyReportError("core", "Ran out of thread objects");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    ReleaseMutex(_guard);
 | 
				
			||||||
 | 
					    return thrd;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					VY_DLLEXPORT void vyJoinThread(vy_thread *thread) {
 | 
				
			||||||
 | 
					    WaitForSingleObject(thread->handle, INFINITE);
 | 
				
			||||||
 | 
					    CloseHandle(thread->handle);
 | 
				
			||||||
 | 
					    thread->needs_join = false;
 | 
				
			||||||
 | 
					    ptrdiff_t index    = thread - &_threads[0];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (WaitForSingleObject(_guard, INFINITE) != WAIT_OBJECT_0) {
 | 
				
			||||||
 | 
					        vyLog("core", "Failed to lock the guard variable: %u", GetLastError());
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    thread->next_reusable = _first_reusable;
 | 
				
			||||||
 | 
					    _first_reusable       = index;
 | 
				
			||||||
 | 
					    ReleaseMutex(_guard);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#elif defined(__linux__)
 | 
					#elif defined(__linux__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #include <pthread.h>
 | 
					    #include <pthread.h>
 | 
				
			||||||
@ -51,7 +149,7 @@ VY_DLLEXPORT vy_thread *vySpawnThread(vy_thread_entry_fn *entry, void *param) {
 | 
				
			|||||||
        thrd->param = param;
 | 
					        thrd->param = param;
 | 
				
			||||||
        if (pthread_create(&thrd->handle, NULL, linuxThreadWrapper, thrd) !=
 | 
					        if (pthread_create(&thrd->handle, NULL, linuxThreadWrapper, thrd) !=
 | 
				
			||||||
            0) {
 | 
					            0) {
 | 
				
			||||||
            vyLog("core", "Mutex creation failed");
 | 
					            vyLog("core", "Thread creation failed");
 | 
				
			||||||
            thrd = NULL;
 | 
					            thrd = NULL;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user