Naming convention updates, separated out the work queue into its own file

This commit is contained in:
PS 2020-10-01 15:41:32 -07:00
parent 78d44b9348
commit 0022efea8e
7 changed files with 513 additions and 499 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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