Naming convention updates, separated out the work queue into its own file
This commit is contained in:
parent
78d44b9348
commit
0022efea8e
|
@ -292,7 +292,7 @@ InitStreamHeader (u8* Buffer, s32 BufferSize,
|
|||
//
|
||||
|
||||
internal streaming_acn
|
||||
InitializeSACN (context Context)
|
||||
SACN_Initialize (context Context)
|
||||
{
|
||||
streaming_acn SACN = {};
|
||||
|
||||
|
@ -304,13 +304,13 @@ InitializeSACN (context Context)
|
|||
}
|
||||
|
||||
internal void
|
||||
SACNCleanup(streaming_acn* SACN, context Context)
|
||||
SACN_Cleanup(streaming_acn* SACN, context Context)
|
||||
{
|
||||
Context.PlatformCloseSocket(SACN->SendSocket);
|
||||
}
|
||||
|
||||
internal void
|
||||
SACNUpdateSequence (streaming_acn* SACN)
|
||||
SACN_UpdateSequence (streaming_acn* SACN)
|
||||
{
|
||||
// Never use 0 after the first one
|
||||
if (++SACN->SequenceIterator == 0)
|
||||
|
@ -320,7 +320,7 @@ SACNUpdateSequence (streaming_acn* SACN)
|
|||
}
|
||||
|
||||
internal void
|
||||
SACNPrepareBufferHeader (s32 Universe, u8* Buffer, s32 BufferSize, s32 SizeReservedForHeader, streaming_acn SACN)
|
||||
SACN_PrepareBufferHeader (s32 Universe, u8* Buffer, s32 BufferSize, s32 SizeReservedForHeader, streaming_acn SACN)
|
||||
{
|
||||
Assert(SizeReservedForHeader == STREAM_HEADER_SIZE);
|
||||
Assert(Buffer && BufferSize > 0);
|
||||
|
@ -331,7 +331,7 @@ SACNPrepareBufferHeader (s32 Universe, u8* Buffer, s32 BufferSize, s32 SizeReser
|
|||
}
|
||||
|
||||
internal u32
|
||||
SACNGetUniverseSendAddress(s32 Universe)
|
||||
SACN_GetUniverseSendAddress(s32 Universe)
|
||||
{
|
||||
u8 MulticastAddressBuffer[4] = {};
|
||||
MulticastAddressBuffer[0] = 239;
|
||||
|
@ -362,7 +362,7 @@ SACN_FillBufferWithLeds(u8* BufferStart, u32 BufferSize, v2_strip Strip, led_buf
|
|||
internal void
|
||||
SACN_BuildOutputData(streaming_acn* SACN, addressed_data_buffer_list* Output, assembly_array Assemblies, led_system* LedSystem, gs_memory_arena* OutputStorage)
|
||||
{
|
||||
SACNUpdateSequence(SACN);
|
||||
SACN_UpdateSequence(SACN);
|
||||
|
||||
// TODO(pjs): 512 is a magic number - make it a constant?
|
||||
s32 BufferHeaderSize = STREAM_HEADER_SIZE;
|
||||
|
@ -378,13 +378,13 @@ SACN_BuildOutputData(streaming_acn* SACN, addressed_data_buffer_list* Output, as
|
|||
{
|
||||
v2_strip StripAt = Assembly.Strips[StripIdx];
|
||||
|
||||
u32 V4SendAddress = SACNGetUniverseSendAddress(StripAt.StartUniverse);
|
||||
u32 V4SendAddress = SACN_GetUniverseSendAddress(StripAt.StartUniverse);
|
||||
u32 SendPort = DEFAULT_STREAMING_ACN_PORT;
|
||||
|
||||
addressed_data_buffer* Data = AddressedDataBufferList_Push(Output, BufferSize, OutputStorage);
|
||||
AddressedDataBuffer_SetNetworkAddress(Data, V4SendAddress, SendPort);
|
||||
|
||||
SACNPrepareBufferHeader(StripAt.StartUniverse, Data->Memory, Data->MemorySize, BufferHeaderSize, *SACN);
|
||||
SACN_PrepareBufferHeader(StripAt.StartUniverse, Data->Memory, Data->MemorySize, BufferHeaderSize, *SACN);
|
||||
SACN_FillBufferWithLeds(Data->Memory + BufferHeaderSize, BufferBodySize, StripAt, *LedBuffer);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -111,7 +111,7 @@ INITIALIZE_APPLICATION(InitializeApplication)
|
|||
State->Interface.Style.Margin = v2{5, 5};
|
||||
State->Interface.Style.RowHeight = ui_GetTextLineHeight(State->Interface);
|
||||
|
||||
State->SACN = InitializeSACN(Context);
|
||||
State->SACN = SACN_Initialize(Context);
|
||||
|
||||
State->Camera.FieldOfView = 45.0f;
|
||||
State->Camera.AspectRatio = RectAspectRatio(State->WindowBounds);
|
||||
|
@ -341,37 +341,39 @@ UPDATE_AND_RENDER(UpdateAndRender)
|
|||
}
|
||||
}
|
||||
|
||||
addressed_data_buffer_list OutputData = {0};
|
||||
switch (State->NetworkProtocol)
|
||||
{
|
||||
case NetworkProtocol_SACN:
|
||||
// NOTE(pjs): Building data buffers to be sent out to the sculpture
|
||||
|
||||
addressed_data_buffer_list OutputData = {0};
|
||||
switch (State->NetworkProtocol)
|
||||
{
|
||||
SACN_BuildOutputData(&State->SACN, &OutputData, State->Assemblies, &State->LedSystem, State->Transient);
|
||||
}break;
|
||||
case NetworkProtocol_SACN:
|
||||
{
|
||||
SACN_BuildOutputData(&State->SACN, &OutputData, State->Assemblies, &State->LedSystem, State->Transient);
|
||||
}break;
|
||||
|
||||
case NetworkProtocol_UART:
|
||||
case NetworkProtocol_UART:
|
||||
{
|
||||
//UART_BuildOutputData(&OutputData, State, State->Transient);
|
||||
}break;
|
||||
|
||||
case NetworkProtocol_ArtNet:
|
||||
InvalidDefaultCase;
|
||||
}
|
||||
|
||||
// NOTE(pjs): Executing the job to actually send the data
|
||||
if (0)
|
||||
{
|
||||
//UART_BuildOutputData(&OutputData, State, State->Transient);
|
||||
}break;
|
||||
// TODO(pjs): This should happen on another thread
|
||||
AddressedDataBufferList_SendAll(OutputData, State->SACN.SendSocket, *Context);
|
||||
|
||||
case NetworkProtocol_ArtNet:
|
||||
InvalidDefaultCase;
|
||||
/*
|
||||
Saved this lien as an example of pushing onto a queue
|
||||
Context->GeneralWorkQueue->PushWorkOnQueue(Context->GeneralWorkQueue, SACNSendDMXBufferListJob, Job, "SACN Send Data Job");
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
if (0)
|
||||
{
|
||||
// TODO(pjs): This should happen on another thread
|
||||
AddressedDataBufferList_SendAll(OutputData, State->SACN.SendSocket, *Context);
|
||||
|
||||
/*
|
||||
Saved this lien as an example of pushing onto a queue
|
||||
Context->GeneralWorkQueue->PushWorkOnQueue(Context->GeneralWorkQueue, SACNSendDMXBufferListJob, Job, "SACN Send Data Job");
|
||||
*/
|
||||
}
|
||||
|
||||
// Skipped for performance at the moment
|
||||
|
||||
|
||||
|
||||
PushRenderOrthographic(RenderBuffer, State->WindowBounds);
|
||||
PushRenderClearScreen(RenderBuffer);
|
||||
|
@ -412,7 +414,7 @@ Saved this lien as an example of pushing onto a queue
|
|||
CLEANUP_APPLICATION(CleanupApplication)
|
||||
{
|
||||
app_state* State = (app_state*)Context.MemoryBase;
|
||||
SACNCleanup(&State->SACN, Context);
|
||||
SACN_Cleanup(&State->SACN, Context);
|
||||
}
|
||||
|
||||
#define FOLDHAUS_APP_CPP
|
||||
|
|
|
@ -15,10 +15,13 @@
|
|||
#include "../foldhaus_platform.h"
|
||||
|
||||
#include "../../gs_libs/gs_win32.cpp"
|
||||
#include "win32_foldhaus_utils.h"
|
||||
#include "win32_foldhaus_memory.h"
|
||||
#include "win32_foldhaus_fileio.h"
|
||||
#include "win32_foldhaus_dll.h"
|
||||
#include "win32_foldhaus_timing.h"
|
||||
#include "win32_serial.h"
|
||||
#include "win32_foldhaus_work_queue.h"
|
||||
#include "win32_foldhaus_serial.h"
|
||||
|
||||
#include "../foldhaus_renderer.cpp"
|
||||
|
||||
|
@ -31,469 +34,6 @@ char DLLLockFileName[] = "lock.tmp";
|
|||
|
||||
window MainWindow;
|
||||
|
||||
// Utils
|
||||
|
||||
internal gs_string
|
||||
Win32DumpErrorAndPrepareMessageBoxString(gs_memory_arena* Arena, char* Format, ...)
|
||||
{
|
||||
s32 Error = GetLastError();
|
||||
gs_string ErrorDump = PushString(Arena, 4096);
|
||||
PrintF(&ErrorDump,
|
||||
R"FOO(Win32 Error: %s\n
|
||||
Error Code: %d\n
|
||||
)FOO",
|
||||
__FUNCTION__,
|
||||
Error);
|
||||
|
||||
va_list Args;
|
||||
va_start(Args, Format);
|
||||
PrintFArgsList(&ErrorDump, Format, Args);
|
||||
va_end(Args);
|
||||
|
||||
gs_data ErrorDumpData = StringToData(ErrorDump);
|
||||
|
||||
HANDLE FileHandle = CreateFileA("./crashdump.txt", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (FileHandle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
DWORD BytesWritten = 0;
|
||||
if (WriteFile(FileHandle, ErrorDumpData.Memory, ErrorDumpData.Size, &BytesWritten, NULL))
|
||||
{
|
||||
|
||||
}
|
||||
CloseHandle(FileHandle);
|
||||
}
|
||||
|
||||
AppendPrintF(&ErrorDump, "Program will attempt to continue. See crashdump.txt for info");
|
||||
NullTerminate(&ErrorDump);
|
||||
|
||||
return ErrorDump;
|
||||
}
|
||||
|
||||
DEBUG_PRINT(Win32DebugPrint)
|
||||
{
|
||||
Assert(IsNullTerminated(Message));
|
||||
OutputDebugStringA(Message.Str);
|
||||
}
|
||||
|
||||
#define PrintLastError() PrintLastError_(__FILE__, __LINE__)
|
||||
internal void
|
||||
PrintLastError_(char* File, u32 Line)
|
||||
{
|
||||
char DebugStringData[256];
|
||||
gs_string DebugString = MakeString(DebugStringData, 0, 256);
|
||||
u32 Error = GetLastError();
|
||||
PrintF(&DebugString, "%s Line %d: Win32 Error %d\n\0", File, Line, Error);
|
||||
OutputDebugStringA(DebugString.Str);
|
||||
}
|
||||
|
||||
|
||||
internal HINSTANCE
|
||||
GetHInstance()
|
||||
{
|
||||
HINSTANCE Result = GetModuleHandle(NULL);
|
||||
if (Result == NULL)
|
||||
{
|
||||
PrintLastError();
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
///////////////////////
|
||||
//
|
||||
// Fie I/O
|
||||
|
||||
internal u64
|
||||
Win32HighLowToU64(u32 LowPart, u32 HighPart)
|
||||
{
|
||||
ULARGE_INTEGER Time = {};
|
||||
Time.LowPart = LowPart;
|
||||
Time.HighPart = HighPart;
|
||||
u64 Result = Time.QuadPart;
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal u64
|
||||
Win32FileTimeToU64(FILETIME FileTime)
|
||||
{
|
||||
u64 Result = Win32HighLowToU64(FileTime.dwLowDateTime, FileTime.dwHighDateTime);
|
||||
return Result;
|
||||
}
|
||||
|
||||
GET_FILE_INFO(Win32GetFileInfo)
|
||||
{
|
||||
Assert(IsNullTerminated(Path));
|
||||
gs_file_info Result = {};
|
||||
HANDLE FileHandle = CreateFileA(Path.Str, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (FileHandle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
Result.Path = Path;
|
||||
Result.FileSize = (u64)GetFileSize(FileHandle, NULL) + 1;
|
||||
FILETIME CreationTime, LastWriteTime;
|
||||
if (GetFileTime(FileHandle, &CreationTime, (LPFILETIME)0, &LastWriteTime))
|
||||
{
|
||||
Result.CreationTime = Win32FileTimeToU64(CreationTime);
|
||||
Result.LastWriteTime = Win32FileTimeToU64(LastWriteTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
PrintLastError();
|
||||
}
|
||||
CloseHandle(FileHandle);
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
READ_ENTIRE_FILE(Win32ReadEntireFile)
|
||||
{
|
||||
Assert(DataIsNonEmpty(Memory));
|
||||
Assert(IsNullTerminated(Path));
|
||||
|
||||
gs_file Result = {0};
|
||||
u32 Error = 0;
|
||||
Result.FileInfo.Path = Path;
|
||||
|
||||
HANDLE FileHandle = CreateFileA(Path.Str, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (FileHandle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
DWORD BytesRead = 0;
|
||||
if (ReadFile(FileHandle, (LPVOID)Memory.Memory, Memory.Size - 1, (LPDWORD)(&BytesRead), NULL))
|
||||
{
|
||||
Memory.Memory[Memory.Size - 1] = 0;
|
||||
Result.Data = Memory;
|
||||
|
||||
gs_string AbsolutePath = PushString(FileHandler.Transient, 512);
|
||||
AbsolutePath.Length = GetFullPathNameA(Path.Str, AbsolutePath.Size, AbsolutePath.Str, NULL);
|
||||
if (AbsolutePath.Length == 0)
|
||||
{
|
||||
Error = GetLastError();
|
||||
InvalidCodePath;
|
||||
}
|
||||
Result.FileInfo.AbsolutePath = AbsolutePath.ConstString;
|
||||
}
|
||||
else
|
||||
{
|
||||
// NOTE(Peter): If we get to this error case, it means that the file exists,
|
||||
// and was successfully opened, but we can't read from it. I'm choosing to
|
||||
// treat this as a legitimate error at this point.
|
||||
gs_string Message = Win32DumpErrorAndPrepareMessageBoxString(FileHandler.Transient, "Attempting to read file: %S", Path);
|
||||
if (MessageBox(NULL, Message.Str, "Error", MB_OK) == IDOK)
|
||||
{
|
||||
PostQuitMessage(-1);
|
||||
}
|
||||
}
|
||||
CloseHandle(FileHandle);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
WRITE_ENTIRE_FILE(Win32WriteEntireFile)
|
||||
{
|
||||
Assert(DataIsNonEmpty(Data));
|
||||
Assert(IsNullTerminated(Path));
|
||||
|
||||
bool Success = false;
|
||||
HANDLE FileHandle = CreateFileA(Path.Str, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (FileHandle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
DWORD BytesWritten = 0;
|
||||
if (WriteFile(FileHandle, Data.Memory, Data.Size, &BytesWritten, NULL))
|
||||
{
|
||||
Success = (BytesWritten == Data.Size);
|
||||
}
|
||||
else
|
||||
{
|
||||
gs_string Message = Win32DumpErrorAndPrepareMessageBoxString(FileHandler.Transient, "Attempting to write to file: %S", Path);
|
||||
MessageBox(NULL, Message.Str, "Error", MB_OK);
|
||||
}
|
||||
CloseHandle(FileHandle);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
return Success;
|
||||
}
|
||||
|
||||
internal FILETIME
|
||||
GetFileLastWriteTime(char* Path)
|
||||
{
|
||||
FILETIME Result = {};
|
||||
|
||||
WIN32_FIND_DATA FindData = {};
|
||||
HANDLE FileHandle = FindFirstFileA(Path, &FindData);
|
||||
|
||||
if (FileHandle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
Result = FindData.ftLastWriteTime;
|
||||
FindClose(FileHandle);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO(Peter): :ErrorLogging
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
struct temp_file_list_entry
|
||||
{
|
||||
gs_file_info Info;
|
||||
temp_file_list_entry* Next;
|
||||
};
|
||||
|
||||
struct temp_file_list
|
||||
{
|
||||
temp_file_list_entry* First;
|
||||
temp_file_list_entry* Last;
|
||||
};
|
||||
|
||||
internal void
|
||||
Win32SetFileInfoFromFindFileData(gs_file_info* Info, WIN32_FIND_DATA FindFileData, gs_const_string SearchPath, gs_memory_arena* Storage)
|
||||
{
|
||||
u32 FileNameLength = CharArrayLength(FindFileData.cFileName);
|
||||
|
||||
// NOTE(Peter): String Storage
|
||||
// Storing the string in the final storage means we don't have to copy the string later, and all
|
||||
// strings will be continguous in memory at the calling site, though they will be before the array
|
||||
gs_string FileName = PushString(Storage, SearchPath.Length + FileNameLength + 1);
|
||||
PrintF(&FileName, "%S%.*s", SearchPath, FileName.Size, FindFileData.cFileName);
|
||||
NullTerminate(&FileName);
|
||||
|
||||
Info->FileSize = Win32HighLowToU64(FindFileData.nFileSizeLow, FindFileData.nFileSizeHigh);
|
||||
Info->CreationTime = Win32FileTimeToU64(FindFileData.ftCreationTime);
|
||||
Info->LastWriteTime = Win32FileTimeToU64(FindFileData.ftLastWriteTime);
|
||||
Info->Path = FileName.ConstString;
|
||||
Info->IsDirectory = HasFlag(FindFileData.dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY);
|
||||
}
|
||||
|
||||
internal u32
|
||||
Win32EnumerateDirectoryIntoTempList(gs_file_handler FileHandler, temp_file_list* TempList, gs_const_string Path, gs_memory_arena* Storage, b32 Flags)
|
||||
{
|
||||
u32 FilesCount = 0;
|
||||
|
||||
u32 IndexOfLastSlash = FindLastFromSet(Path, "\\/");
|
||||
gs_const_string SearchPath = Substring(Path, 0, IndexOfLastSlash + 1);
|
||||
|
||||
WIN32_FIND_DATA FindFileData;
|
||||
HANDLE SearchHandle = FindFirstFile(Path.Str, &FindFileData);
|
||||
if (SearchHandle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
do
|
||||
{
|
||||
b32 AddFile = true;
|
||||
|
||||
if (HasFlag(FindFileData.dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY))
|
||||
{
|
||||
if (HasFlag(Flags, EnumerateDirectory_Recurse))
|
||||
{
|
||||
gs_const_string SubDirName = ConstString(FindFileData.cFileName);
|
||||
if (!StringsEqual(SubDirName, ConstString(".")) &&
|
||||
!StringsEqual(SubDirName, ConstString("..")))
|
||||
{
|
||||
gs_string SubDirectoryPath = PushString(FileHandler.Transient, SearchPath.Length + SubDirName.Length + 3);
|
||||
PrintF(&SubDirectoryPath, "%S%S/*\0", SearchPath, SubDirName);
|
||||
FilesCount += Win32EnumerateDirectoryIntoTempList(FileHandler, TempList, SubDirectoryPath.ConstString, Storage, Flags);
|
||||
}
|
||||
}
|
||||
|
||||
AddFile = HasFlag(Flags, EnumerateDirectory_IncludeDirectories);
|
||||
}
|
||||
|
||||
if (AddFile)
|
||||
{
|
||||
temp_file_list_entry* File = PushStruct(FileHandler.Transient, temp_file_list_entry);
|
||||
*File = {0};
|
||||
Win32SetFileInfoFromFindFileData(&File->Info, FindFileData, SearchPath, Storage);
|
||||
SLLPushOrInit(TempList->First, TempList->Last, File);
|
||||
FilesCount += 1;
|
||||
}
|
||||
}while(FindNextFile(SearchHandle, &FindFileData));
|
||||
}
|
||||
else
|
||||
{
|
||||
PrintLastError();
|
||||
}
|
||||
|
||||
return FilesCount;
|
||||
}
|
||||
|
||||
ENUMERATE_DIRECTORY(Win32EnumerateDirectory)
|
||||
{
|
||||
Assert(IsNullTerminated(Path));
|
||||
gs_file_info_array Result = {};
|
||||
|
||||
temp_file_list TempList = {};
|
||||
Result.MaxCount = Win32EnumerateDirectoryIntoTempList(FileHandler, &TempList, Path, Storage, Flags);
|
||||
|
||||
Result.Values = PushArray(Storage, gs_file_info, Result.MaxCount);
|
||||
for (temp_file_list_entry* FileAt = TempList.First;
|
||||
FileAt != 0;
|
||||
FileAt = FileAt->Next)
|
||||
{
|
||||
// NOTE(Peter): We don't copy the file name here because its already in Storage.
|
||||
// See String Storage note above ^^
|
||||
Result.Values[Result.Count++] = FileAt->Info;
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
///////////////////////
|
||||
//
|
||||
// Job System
|
||||
|
||||
struct worker_thread_entry
|
||||
{
|
||||
b32 IsValid;
|
||||
u32 Index;
|
||||
};
|
||||
|
||||
struct worker_thread_info
|
||||
{
|
||||
gs_thread_context ThreadContext;
|
||||
HANDLE Handle;
|
||||
gs_work_queue* Queue;
|
||||
};
|
||||
|
||||
internal s32
|
||||
Win32GetThreadId()
|
||||
{
|
||||
s32 Result = GetCurrentThreadId();
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal gs_thread_context
|
||||
Win32CreateThreadContext(gs_memory_arena* Transient = 0)
|
||||
{
|
||||
gs_thread_context Result = {0};
|
||||
Result.ThreadInfo.ThreadID = Win32GetThreadId();
|
||||
Result.Allocator = CreateAllocator(Win32Alloc, Win32Free);
|
||||
if (Transient != 0)
|
||||
{
|
||||
Result.Transient = Transient;
|
||||
}
|
||||
else
|
||||
{
|
||||
Result.Transient = (gs_memory_arena*)AllocatorAlloc(Result.Allocator, sizeof(gs_memory_arena)).Memory;
|
||||
*Result.Transient = CreateMemoryArena(Result.Allocator);
|
||||
}
|
||||
Result.FileHandler = CreateFileHandler(Win32GetFileInfo,
|
||||
Win32ReadEntireFile,
|
||||
Win32WriteEntireFile,
|
||||
Win32EnumerateDirectory,
|
||||
Result.Transient);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
PUSH_WORK_ON_QUEUE(Win32PushWorkOnQueue)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
// NOTE(Peter): Just prints out the names of all the pending jobs if we end up
|
||||
// overflowing the buffer
|
||||
if (Queue->JobsCount >= Queue->JobsMax)
|
||||
{
|
||||
gs_string DebugString = MakeString((char*)malloc(256), 256);
|
||||
for (u32 i = 0; i < Queue->JobsCount; i++)
|
||||
{
|
||||
PrintF(&DebugString, "%d %s\n", i, Queue->Jobs[i].JobName);
|
||||
NullTerminate(&DebugString);
|
||||
OutputDebugStringA(DebugString.Str);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
Assert(Queue->JobsCount < Queue->JobsMax);
|
||||
|
||||
gs_threaded_job* Job = Queue->Jobs + Queue->JobsCount;
|
||||
Job->WorkProc = WorkProc;
|
||||
Job->Data = Data;
|
||||
#ifdef DEBUG
|
||||
Job->JobName = JobName;
|
||||
#endif
|
||||
|
||||
// Complete Past Writes before Future Writes
|
||||
_WriteBarrier();
|
||||
_mm_sfence();
|
||||
|
||||
++Queue->JobsCount;
|
||||
ReleaseSemaphore(Queue->SemaphoreHandle, 1, 0);
|
||||
}
|
||||
|
||||
internal worker_thread_entry
|
||||
CompleteAndTakeNextJob(gs_work_queue* Queue, worker_thread_entry Completed, gs_thread_context Context)
|
||||
{
|
||||
if (Completed.IsValid)
|
||||
{
|
||||
InterlockedIncrement((LONG volatile*)&Queue->JobsCompleted);
|
||||
}
|
||||
|
||||
worker_thread_entry Result = {};
|
||||
Result.IsValid = false;
|
||||
|
||||
u32 OriginalNextJobIndex = Queue->NextJobIndex;
|
||||
while (OriginalNextJobIndex < Queue->JobsCount)
|
||||
{
|
||||
u32 Index = InterlockedCompareExchange((LONG volatile*)&Queue->NextJobIndex,
|
||||
OriginalNextJobIndex + 1,
|
||||
OriginalNextJobIndex);
|
||||
if (Index == OriginalNextJobIndex)
|
||||
{
|
||||
Result.Index = Index;
|
||||
Result.IsValid = true;
|
||||
break;
|
||||
}
|
||||
OriginalNextJobIndex = Queue->NextJobIndex;
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
COMPLETE_QUEUE_WORK(Win32DoQueueWorkUntilDone)
|
||||
{
|
||||
worker_thread_entry Entry = {};
|
||||
Entry.IsValid = false;
|
||||
while (Queue->JobsCompleted < Queue->JobsCount)
|
||||
{
|
||||
Entry = CompleteAndTakeNextJob(Queue, Entry, Context);
|
||||
if (Entry.IsValid)
|
||||
{
|
||||
Queue->Jobs[Entry.Index].WorkProc(Context, Queue->Jobs[Entry.Index].Data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DWORD WINAPI
|
||||
WorkerThreadProc (LPVOID InputThreadInfo)
|
||||
{
|
||||
worker_thread_info* ThreadInfo = (worker_thread_info*)InputThreadInfo;
|
||||
ThreadInfo->ThreadContext = Win32CreateThreadContext();
|
||||
|
||||
worker_thread_entry Entry = {};
|
||||
Entry.IsValid = false;
|
||||
while (true)
|
||||
{
|
||||
ClearArena(ThreadInfo->ThreadContext.Transient);
|
||||
Entry = CompleteAndTakeNextJob(ThreadInfo->Queue, Entry, ThreadInfo->ThreadContext);
|
||||
if (Entry.IsValid)
|
||||
{
|
||||
ThreadInfo->Queue->Jobs[Entry.Index].WorkProc(ThreadInfo->ThreadContext,
|
||||
ThreadInfo->Queue->Jobs[Entry.Index].Data);
|
||||
}
|
||||
else
|
||||
{
|
||||
WaitForSingleObjectEx(ThreadInfo->Queue->SemaphoreHandle, INFINITE, 0);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
PLATFORM_GET_GPU_TEXTURE_HANDLE(Win32GetGPUTextureHandle)
|
||||
{
|
||||
s32 Handle = SubmitTexture(Memory, Width, Height);
|
||||
|
|
|
@ -8,5 +8,247 @@
|
|||
//
|
||||
#ifndef WIN32_FOLDHAUS_FILEIO_H
|
||||
|
||||
internal u64
|
||||
Win32HighLowToU64(u32 LowPart, u32 HighPart)
|
||||
{
|
||||
ULARGE_INTEGER Time = {};
|
||||
Time.LowPart = LowPart;
|
||||
Time.HighPart = HighPart;
|
||||
u64 Result = Time.QuadPart;
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal u64
|
||||
Win32FileTimeToU64(FILETIME FileTime)
|
||||
{
|
||||
u64 Result = Win32HighLowToU64(FileTime.dwLowDateTime, FileTime.dwHighDateTime);
|
||||
return Result;
|
||||
}
|
||||
|
||||
GET_FILE_INFO(Win32GetFileInfo)
|
||||
{
|
||||
Assert(IsNullTerminated(Path));
|
||||
gs_file_info Result = {};
|
||||
HANDLE FileHandle = CreateFileA(Path.Str, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (FileHandle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
Result.Path = Path;
|
||||
Result.FileSize = (u64)GetFileSize(FileHandle, NULL) + 1;
|
||||
FILETIME CreationTime, LastWriteTime;
|
||||
if (GetFileTime(FileHandle, &CreationTime, (LPFILETIME)0, &LastWriteTime))
|
||||
{
|
||||
Result.CreationTime = Win32FileTimeToU64(CreationTime);
|
||||
Result.LastWriteTime = Win32FileTimeToU64(LastWriteTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
PrintLastError();
|
||||
}
|
||||
CloseHandle(FileHandle);
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
READ_ENTIRE_FILE(Win32ReadEntireFile)
|
||||
{
|
||||
Assert(DataIsNonEmpty(Memory));
|
||||
Assert(IsNullTerminated(Path));
|
||||
|
||||
gs_file Result = {0};
|
||||
u32 Error = 0;
|
||||
Result.FileInfo.Path = Path;
|
||||
|
||||
HANDLE FileHandle = CreateFileA(Path.Str, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (FileHandle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
DWORD BytesRead = 0;
|
||||
if (ReadFile(FileHandle, (LPVOID)Memory.Memory, Memory.Size - 1, (LPDWORD)(&BytesRead), NULL))
|
||||
{
|
||||
Memory.Memory[Memory.Size - 1] = 0;
|
||||
Result.Data = Memory;
|
||||
|
||||
gs_string AbsolutePath = PushString(FileHandler.Transient, 512);
|
||||
AbsolutePath.Length = GetFullPathNameA(Path.Str, AbsolutePath.Size, AbsolutePath.Str, NULL);
|
||||
if (AbsolutePath.Length == 0)
|
||||
{
|
||||
Error = GetLastError();
|
||||
InvalidCodePath;
|
||||
}
|
||||
Result.FileInfo.AbsolutePath = AbsolutePath.ConstString;
|
||||
}
|
||||
else
|
||||
{
|
||||
// NOTE(Peter): If we get to this error case, it means that the file exists,
|
||||
// and was successfully opened, but we can't read from it. I'm choosing to
|
||||
// treat this as a legitimate error at this point.
|
||||
gs_string Message = Win32DumpErrorAndPrepareMessageBoxString(FileHandler.Transient, "Attempting to read file: %S", Path);
|
||||
if (MessageBox(NULL, Message.Str, "Error", MB_OK) == IDOK)
|
||||
{
|
||||
PostQuitMessage(-1);
|
||||
}
|
||||
}
|
||||
CloseHandle(FileHandle);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
WRITE_ENTIRE_FILE(Win32WriteEntireFile)
|
||||
{
|
||||
Assert(DataIsNonEmpty(Data));
|
||||
Assert(IsNullTerminated(Path));
|
||||
|
||||
bool Success = false;
|
||||
HANDLE FileHandle = CreateFileA(Path.Str, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (FileHandle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
DWORD BytesWritten = 0;
|
||||
if (WriteFile(FileHandle, Data.Memory, Data.Size, &BytesWritten, NULL))
|
||||
{
|
||||
Success = (BytesWritten == Data.Size);
|
||||
}
|
||||
else
|
||||
{
|
||||
gs_string Message = Win32DumpErrorAndPrepareMessageBoxString(FileHandler.Transient, "Attempting to write to file: %S", Path);
|
||||
MessageBox(NULL, Message.Str, "Error", MB_OK);
|
||||
}
|
||||
CloseHandle(FileHandle);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
return Success;
|
||||
}
|
||||
|
||||
internal FILETIME
|
||||
GetFileLastWriteTime(char* Path)
|
||||
{
|
||||
FILETIME Result = {};
|
||||
|
||||
WIN32_FIND_DATA FindData = {};
|
||||
HANDLE FileHandle = FindFirstFileA(Path, &FindData);
|
||||
|
||||
if (FileHandle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
Result = FindData.ftLastWriteTime;
|
||||
FindClose(FileHandle);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO(Peter): :ErrorLogging
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
struct temp_file_list_entry
|
||||
{
|
||||
gs_file_info Info;
|
||||
temp_file_list_entry* Next;
|
||||
};
|
||||
|
||||
struct temp_file_list
|
||||
{
|
||||
temp_file_list_entry* First;
|
||||
temp_file_list_entry* Last;
|
||||
};
|
||||
|
||||
internal void
|
||||
Win32SetFileInfoFromFindFileData(gs_file_info* Info, WIN32_FIND_DATA FindFileData, gs_const_string SearchPath, gs_memory_arena* Storage)
|
||||
{
|
||||
u32 FileNameLength = CharArrayLength(FindFileData.cFileName);
|
||||
|
||||
// NOTE(Peter): String Storage
|
||||
// Storing the string in the final storage means we don't have to copy the string later, and all
|
||||
// strings will be continguous in memory at the calling site, though they will be before the array
|
||||
gs_string FileName = PushString(Storage, SearchPath.Length + FileNameLength + 1);
|
||||
PrintF(&FileName, "%S%.*s", SearchPath, FileName.Size, FindFileData.cFileName);
|
||||
NullTerminate(&FileName);
|
||||
|
||||
Info->FileSize = Win32HighLowToU64(FindFileData.nFileSizeLow, FindFileData.nFileSizeHigh);
|
||||
Info->CreationTime = Win32FileTimeToU64(FindFileData.ftCreationTime);
|
||||
Info->LastWriteTime = Win32FileTimeToU64(FindFileData.ftLastWriteTime);
|
||||
Info->Path = FileName.ConstString;
|
||||
Info->IsDirectory = HasFlag(FindFileData.dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY);
|
||||
}
|
||||
|
||||
internal u32
|
||||
Win32EnumerateDirectoryIntoTempList(gs_file_handler FileHandler, temp_file_list* TempList, gs_const_string Path, gs_memory_arena* Storage, b32 Flags)
|
||||
{
|
||||
u32 FilesCount = 0;
|
||||
|
||||
u32 IndexOfLastSlash = FindLastFromSet(Path, "\\/");
|
||||
gs_const_string SearchPath = Substring(Path, 0, IndexOfLastSlash + 1);
|
||||
|
||||
WIN32_FIND_DATA FindFileData;
|
||||
HANDLE SearchHandle = FindFirstFile(Path.Str, &FindFileData);
|
||||
if (SearchHandle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
do
|
||||
{
|
||||
b32 AddFile = true;
|
||||
|
||||
if (HasFlag(FindFileData.dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY))
|
||||
{
|
||||
if (HasFlag(Flags, EnumerateDirectory_Recurse))
|
||||
{
|
||||
gs_const_string SubDirName = ConstString(FindFileData.cFileName);
|
||||
if (!StringsEqual(SubDirName, ConstString(".")) &&
|
||||
!StringsEqual(SubDirName, ConstString("..")))
|
||||
{
|
||||
gs_string SubDirectoryPath = PushString(FileHandler.Transient, SearchPath.Length + SubDirName.Length + 3);
|
||||
PrintF(&SubDirectoryPath, "%S%S/*\0", SearchPath, SubDirName);
|
||||
FilesCount += Win32EnumerateDirectoryIntoTempList(FileHandler, TempList, SubDirectoryPath.ConstString, Storage, Flags);
|
||||
}
|
||||
}
|
||||
|
||||
AddFile = HasFlag(Flags, EnumerateDirectory_IncludeDirectories);
|
||||
}
|
||||
|
||||
if (AddFile)
|
||||
{
|
||||
temp_file_list_entry* File = PushStruct(FileHandler.Transient, temp_file_list_entry);
|
||||
*File = {0};
|
||||
Win32SetFileInfoFromFindFileData(&File->Info, FindFileData, SearchPath, Storage);
|
||||
SLLPushOrInit(TempList->First, TempList->Last, File);
|
||||
FilesCount += 1;
|
||||
}
|
||||
}while(FindNextFile(SearchHandle, &FindFileData));
|
||||
}
|
||||
else
|
||||
{
|
||||
PrintLastError();
|
||||
}
|
||||
|
||||
return FilesCount;
|
||||
}
|
||||
|
||||
ENUMERATE_DIRECTORY(Win32EnumerateDirectory)
|
||||
{
|
||||
Assert(IsNullTerminated(Path));
|
||||
gs_file_info_array Result = {};
|
||||
|
||||
temp_file_list TempList = {};
|
||||
Result.MaxCount = Win32EnumerateDirectoryIntoTempList(FileHandler, &TempList, Path, Storage, Flags);
|
||||
|
||||
Result.Values = PushArray(Storage, gs_file_info, Result.MaxCount);
|
||||
for (temp_file_list_entry* FileAt = TempList.First;
|
||||
FileAt != 0;
|
||||
FileAt = FileAt->Next)
|
||||
{
|
||||
// NOTE(Peter): We don't copy the file name here because its already in Storage.
|
||||
// See String Storage note above ^^
|
||||
Result.Values[Result.Count++] = FileAt->Info;
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
#define WIN32_FOLDHAUS_FILEIO_H
|
||||
#endif // WIN32_FOLDHAUS_FILEIO_H
|
|
@ -0,0 +1,75 @@
|
|||
//
|
||||
// File: win32_foldhaus_utils.h
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2020-10-01
|
||||
//
|
||||
#ifndef WIN32_FOLDHAUS_UTILS_H
|
||||
|
||||
internal gs_string
|
||||
Win32DumpErrorAndPrepareMessageBoxString(gs_memory_arena* Arena, char* Format, ...)
|
||||
{
|
||||
s32 Error = GetLastError();
|
||||
gs_string ErrorDump = PushString(Arena, 4096);
|
||||
PrintF(&ErrorDump,
|
||||
R"FOO(Win32 Error: %s\n
|
||||
Error Code: %d\n
|
||||
)FOO",
|
||||
__FUNCTION__,
|
||||
Error);
|
||||
|
||||
va_list Args;
|
||||
va_start(Args, Format);
|
||||
PrintFArgsList(&ErrorDump, Format, Args);
|
||||
va_end(Args);
|
||||
|
||||
gs_data ErrorDumpData = StringToData(ErrorDump);
|
||||
|
||||
HANDLE FileHandle = CreateFileA("./crashdump.txt", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (FileHandle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
DWORD BytesWritten = 0;
|
||||
if (WriteFile(FileHandle, ErrorDumpData.Memory, ErrorDumpData.Size, &BytesWritten, NULL))
|
||||
{
|
||||
|
||||
}
|
||||
CloseHandle(FileHandle);
|
||||
}
|
||||
|
||||
AppendPrintF(&ErrorDump, "Program will attempt to continue. See crashdump.txt for info");
|
||||
NullTerminate(&ErrorDump);
|
||||
|
||||
return ErrorDump;
|
||||
}
|
||||
|
||||
DEBUG_PRINT(Win32DebugPrint)
|
||||
{
|
||||
Assert(IsNullTerminated(Message));
|
||||
OutputDebugStringA(Message.Str);
|
||||
}
|
||||
|
||||
#define PrintLastError() PrintLastError_(__FILE__, __LINE__)
|
||||
internal void
|
||||
PrintLastError_(char* File, u32 Line)
|
||||
{
|
||||
char DebugStringData[256];
|
||||
gs_string DebugString = MakeString(DebugStringData, 0, 256);
|
||||
u32 Error = GetLastError();
|
||||
PrintF(&DebugString, "%s Line %d: Win32 Error %d\n\0", File, Line, Error);
|
||||
OutputDebugStringA(DebugString.Str);
|
||||
}
|
||||
|
||||
|
||||
internal HINSTANCE
|
||||
GetHInstance()
|
||||
{
|
||||
HINSTANCE Result = GetModuleHandle(NULL);
|
||||
if (Result == NULL)
|
||||
{
|
||||
PrintLastError();
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
|
||||
#define WIN32_FOLDHAUS_UTILS_H
|
||||
#endif // WIN32_FOLDHAUS_UTILS_H
|
|
@ -0,0 +1,155 @@
|
|||
//
|
||||
// File: win32_foldhaus_work_queue.h
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2020-10-01
|
||||
//
|
||||
#ifndef WIN32_FOLDHAUS_WORK_QUEUE_H
|
||||
|
||||
struct worker_thread_entry
|
||||
{
|
||||
b32 IsValid;
|
||||
u32 Index;
|
||||
};
|
||||
|
||||
struct worker_thread_info
|
||||
{
|
||||
gs_thread_context ThreadContext;
|
||||
HANDLE Handle;
|
||||
gs_work_queue* Queue;
|
||||
};
|
||||
|
||||
internal s32
|
||||
Win32GetThreadId()
|
||||
{
|
||||
s32 Result = GetCurrentThreadId();
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal gs_thread_context
|
||||
Win32CreateThreadContext(gs_memory_arena* Transient = 0)
|
||||
{
|
||||
gs_thread_context Result = {0};
|
||||
Result.ThreadInfo.ThreadID = Win32GetThreadId();
|
||||
Result.Allocator = CreateAllocator(Win32Alloc, Win32Free);
|
||||
if (Transient != 0)
|
||||
{
|
||||
Result.Transient = Transient;
|
||||
}
|
||||
else
|
||||
{
|
||||
Result.Transient = (gs_memory_arena*)AllocatorAlloc(Result.Allocator, sizeof(gs_memory_arena)).Memory;
|
||||
*Result.Transient = CreateMemoryArena(Result.Allocator);
|
||||
}
|
||||
Result.FileHandler = CreateFileHandler(Win32GetFileInfo,
|
||||
Win32ReadEntireFile,
|
||||
Win32WriteEntireFile,
|
||||
Win32EnumerateDirectory,
|
||||
Result.Transient);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
PUSH_WORK_ON_QUEUE(Win32PushWorkOnQueue)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
// NOTE(Peter): Just prints out the names of all the pending jobs if we end up
|
||||
// overflowing the buffer
|
||||
if (Queue->JobsCount >= Queue->JobsMax)
|
||||
{
|
||||
gs_string DebugString = MakeString((char*)malloc(256), 256);
|
||||
for (u32 i = 0; i < Queue->JobsCount; i++)
|
||||
{
|
||||
PrintF(&DebugString, "%d %s\n", i, Queue->Jobs[i].JobName);
|
||||
NullTerminate(&DebugString);
|
||||
OutputDebugStringA(DebugString.Str);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
Assert(Queue->JobsCount < Queue->JobsMax);
|
||||
|
||||
gs_threaded_job* Job = Queue->Jobs + Queue->JobsCount;
|
||||
Job->WorkProc = WorkProc;
|
||||
Job->Data = Data;
|
||||
#ifdef DEBUG
|
||||
Job->JobName = JobName;
|
||||
#endif
|
||||
|
||||
// Complete Past Writes before Future Writes
|
||||
_WriteBarrier();
|
||||
_mm_sfence();
|
||||
|
||||
++Queue->JobsCount;
|
||||
ReleaseSemaphore(Queue->SemaphoreHandle, 1, 0);
|
||||
}
|
||||
|
||||
internal worker_thread_entry
|
||||
CompleteAndTakeNextJob(gs_work_queue* Queue, worker_thread_entry Completed, gs_thread_context Context)
|
||||
{
|
||||
if (Completed.IsValid)
|
||||
{
|
||||
InterlockedIncrement((LONG volatile*)&Queue->JobsCompleted);
|
||||
}
|
||||
|
||||
worker_thread_entry Result = {};
|
||||
Result.IsValid = false;
|
||||
|
||||
u32 OriginalNextJobIndex = Queue->NextJobIndex;
|
||||
while (OriginalNextJobIndex < Queue->JobsCount)
|
||||
{
|
||||
u32 Index = InterlockedCompareExchange((LONG volatile*)&Queue->NextJobIndex,
|
||||
OriginalNextJobIndex + 1,
|
||||
OriginalNextJobIndex);
|
||||
if (Index == OriginalNextJobIndex)
|
||||
{
|
||||
Result.Index = Index;
|
||||
Result.IsValid = true;
|
||||
break;
|
||||
}
|
||||
OriginalNextJobIndex = Queue->NextJobIndex;
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
COMPLETE_QUEUE_WORK(Win32DoQueueWorkUntilDone)
|
||||
{
|
||||
worker_thread_entry Entry = {};
|
||||
Entry.IsValid = false;
|
||||
while (Queue->JobsCompleted < Queue->JobsCount)
|
||||
{
|
||||
Entry = CompleteAndTakeNextJob(Queue, Entry, Context);
|
||||
if (Entry.IsValid)
|
||||
{
|
||||
Queue->Jobs[Entry.Index].WorkProc(Context, Queue->Jobs[Entry.Index].Data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DWORD WINAPI
|
||||
WorkerThreadProc (LPVOID InputThreadInfo)
|
||||
{
|
||||
worker_thread_info* ThreadInfo = (worker_thread_info*)InputThreadInfo;
|
||||
ThreadInfo->ThreadContext = Win32CreateThreadContext();
|
||||
|
||||
worker_thread_entry Entry = {};
|
||||
Entry.IsValid = false;
|
||||
while (true)
|
||||
{
|
||||
ClearArena(ThreadInfo->ThreadContext.Transient);
|
||||
Entry = CompleteAndTakeNextJob(ThreadInfo->Queue, Entry, ThreadInfo->ThreadContext);
|
||||
if (Entry.IsValid)
|
||||
{
|
||||
ThreadInfo->Queue->Jobs[Entry.Index].WorkProc(ThreadInfo->ThreadContext,
|
||||
ThreadInfo->Queue->Jobs[Entry.Index].Data);
|
||||
}
|
||||
else
|
||||
{
|
||||
WaitForSingleObjectEx(ThreadInfo->Queue->SemaphoreHandle, INFINITE, 0);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define WIN32_FOLDHAUS_WORK_QUEUE_H
|
||||
#endif // WIN32_FOLDHAUS_WORK_QUEUE_H
|
Loading…
Reference in New Issue