From 206eb88036f8cc6344680485b0635d0846762e7d Mon Sep 17 00:00:00 2001 From: UnknownShadow200 Date: Mon, 30 Apr 2018 14:55:42 +1000 Subject: [PATCH] Start porting AsyncDownloader to C --- .../Network/Utils/AsyncDownloader.cs | 6 +- src/Client/AsyncDownloader.c | 353 ++++++++++++++++++ src/Client/AsyncDownloader.h | 28 +- src/Client/Client.vcxproj | 1 + src/Client/Client.vcxproj.filters | 3 + src/Client/Entity.c | 5 +- src/Client/Game.c | 11 +- src/Client/PacketHandlers.c | 6 +- src/Client/Platform.h | 11 + src/Client/Screens.c | 2 +- src/Client/ServerConnection.c | 4 +- src/Client/String.c | 10 +- src/Client/String.h | 1 - src/Client/WinPlatform.c | 52 +++ 14 files changed, 450 insertions(+), 43 deletions(-) create mode 100644 src/Client/AsyncDownloader.c diff --git a/ClassicalSharp/Network/Utils/AsyncDownloader.cs b/ClassicalSharp/Network/Utils/AsyncDownloader.cs index 558586f09..2f0f6a7dd 100644 --- a/ClassicalSharp/Network/Utils/AsyncDownloader.cs +++ b/ClassicalSharp/Network/Utils/AsyncDownloader.cs @@ -39,11 +39,7 @@ namespace ClassicalSharp.Network { #if !LAUNCHER void IGameComponent.Init(Game game) { Init(game.skinServer); } void IGameComponent.Ready(Game game) { } - void IGameComponent.Reset(Game game) { - lock (pendingLocker) - pending.Clear(); - handle.Set(); - } + void IGameComponent.Reset(Game game) { Clear(); } void IGameComponent.OnNewMap(Game game) { } void IGameComponent.OnNewMapLoaded(Game game) { } diff --git a/src/Client/AsyncDownloader.c b/src/Client/AsyncDownloader.c new file mode 100644 index 000000000..1e0540755 --- /dev/null +++ b/src/Client/AsyncDownloader.c @@ -0,0 +1,353 @@ +#include "AsyncDownloader.h" +#include "Platform.h" + +void ASyncRequest_Free(AsyncRequest* request) { + switch (request->RequestType) { + case REQUEST_TYPE_IMAGE: + Platform_MemFree(&request->ResultBitmap.Scan0); + break; + case REQUEST_TYPE_DATA: + Platform_MemFree(&request->ResultData.Ptr); + break; + case REQUEST_TYPE_STRING: + Platform_MemFree(&request->ResultString.buffer); + break; + } +} + +typedef struct AsyncRequestList_ { + UInt32 Count; + AsyncRequest* Requests; + AsyncRequest DefaultRequests[10]; +} AsyncRequestList; + +void AsyncRequestList_Append(AsyncRequestList* list, AsyncRequest* item); +void AsyncRequestList_Prepend(AsyncRequestList* list, AsyncRequest* item); +void AsyncRequestList_RemoveAt(AsyncRequestList* list, UInt32 i); + +void AsyncRequestList_Init(AsyncRequestList* list) { + list->Count = 0; + list->Requests = list->DefaultRequests; +} + +void AsyncRequestList_Free(AsyncRequestList* list) { + if (list->Requests != list->DefaultRequests) { + Platform_MemFree(&list->Requests); + } + AsyncDownloader_Init(list); +} + +void* async_eventHandle; +void* async_workerThread; +void* async_pendingMutex; +void* async_processedMutex; +void* async_curRequestMutex; +volatile bool async_terminate; + +AsyncRequestList async_pending; +AsyncRequestList async_processed; +String async_skinServer = String_FromConst("http://static.classicube.net/skins/"); +AsyncRequest async_curRequest; +volatile Int32 async_curRequestProgress = -3; +bool ManageCookies; +bool KeepAlive; + +void AsyncDownloader_Init(void) { + AsyncRequestList_Init(&async_pending); + AsyncRequestList_Init(&async_processed); + async_eventHandle = Platform_EventCreate(); + async_pendingMutex = Platform_MutexCreate(); + async_processedMutex = Platform_MutexCreate(); + async_curRequestMutex = Platform_MutexCreate(); + async_workerThread = Platform_ThreadStart(DownloadThreadWorker); +} + + void AsyncDownloader_Reset(void) { + Platform_MutexLock(async_pendingMutex); + { + AsyncRequestList_Free(&async_pending); + } + Platform_MutexUnlock(async_pendingMutex); + Platform_EventSet(async_eventHandle); + } + + void AsyncDownloader_Free(void) { + async_terminate = true; + AsyncDownloader_Reset(); + Platform_ThreadJoin(async_workerThread); + Platform_ThreadFreeHandle(async_workerThread); + + Platform_EventFree(async_eventHandle); + Platform_MutexFree(async_pendingMutex); + Platform_MutexFree(async_processedMutex); + Platform_MutexFree(async_curRequestMutex); + } + + void AsyncDownloader_GetSkin(STRING_PURE String* id, STRING_PURE String* skinName) { + UInt8 urlBuffer[String_BufferSize(STRING_SIZE)]; + String url = String_InitAndClearArray(urlBuffer); + + if (Utils_IsUrlPrefix(skinName, 0)) { + String_Set(&url, &skinName); + } else { + String_AppendString(&url, &async_skinServer); + String_AppendColorless(&url, skinName); + String_AppendConst(&url, ".png"); + } + + AddRequest(&url, false, id, REQUEST_TYPE_IMAGE, NULL, NULL, NULL); + } + + void AsyncDownloader_GetData(STRING_PURE String* url, bool priority, STRING_PURE String* id) { + AddRequest(url, priority, id, REQUEST_TYPE_DATA, NULL, NULL, NULL); + } + + void AsyncDownloader_GetImage(STRING_PURE String* url, bool priority, STRING_PURE String* id) { + AddRequest(url, priority, id, REQUEST_TYPE_IMAGE, NULL, NULL, NULL); + } + + void AsyncDownloader_GetString(STRING_PURE String* url, bool priority, STRING_PURE String* id) { + AddRequest(url, priority, id, REQUEST_TYPE_STRING, NULL, NULL, NULL); + } + + void AsyncDownloader_GetContentLength(STRING_PURE String* url, bool priority, STRING_PURE String* id) { + AddRequest(url, priority, id, REQUEST_TYPE_CONTENT_LENGTH, NULL, NULL, NULL); + } + + void AsyncDownloader_PostString(STRING_PURE String* url, bool priority, STRING_PURE String* id, STRING_PURE String* contents) { + AddRequest(url, priority, id, REQUEST_TYPE_STRING, NULL, NULL, contents); + } + + void AsyncDownloader_GetDataEx(STRING_PURE String* url, bool priority, STRING_PURE String* id, DateTime* lastModified, STRING_PURE String* etag) { + AddRequest(url, priority, id, REQUEST_TYPE_DATA, lastModified, etag, NULL); + } + + void AsyncDownloader_GetImageEx(STRING_PURE String* url, bool priority, STRING_PURE String* id, DateTime* lastModified, STRING_PURE String* etag) { + AddRequest(url, priority, id, REQUEST_TYPE_IMAGE, lastModified, etag, NULL); + } + + void AddRequest(String* url, bool priority, String* id, UInt8 type, DateTime* lastModified, String* etag, object data) { + Platform_MutexLock(async_pendingMutex); + { + AsyncRequest request = { 0 }; + String reqUrl = String_FromEmptyArray(request.URL); String_Set(&reqUrl, url); + String reqID = String_FromEmptyArray(request.ID); String_Set(&reqID, id); + request.RequestType = type; + request.LastModified = lastModified; // can be null + request.ETag = etag; // can be null + request.Data = data; + + Platform_CurrentUTCTime(&request.TimeAdded); + if (priority) { + AsyncRequestList_Prepend(&async_pending, &request); + } else { + AsyncRequestList_Append(&async_pending, &request); + } + } + Platform_MutexUnlock(async_pendingMutex); + Platform_EventSet(async_eventHandle); + } + + void PurgeOldEntriesTask(ScheduledTask* task) { + Platform_MutexLock(async_processedMutex); + { + DateTime now; Platform_CurrentUTCTime(&now); + Int32 i; + for (i = async_processed.Count - 1; i >= 0; i--) { + AsyncRequest* item = &async_processed.Requests[i]; + if ((now - item.TimeDownloaded).TotalSeconds < 10) continue; + + ASyncRequest_Free(item); + AsyncRequestList_RemoveAt(&async_processed, i); + } + } + Platform_MutexUnlock(async_processedMutex); + } + + bool AsyncDownloader_Get(STRING_PURE String* id, AsyncRequest* item) { + bool success = false; + + Platform_MutexLock(async_processedMutex); + { + Int32 i = FindRequest(id, item); + success = i >= 0; + if (success) AsyncRequestList_RemoveAt(&async_processed, i); + } + Platform_MutexUnlock(async_processedMutex); + return success; + } + + void DownloadThreadWorker() { + while (true) { + AsyncRequest request; + bool hasRequest = false; + + Platform_MutexLock(async_pendingMutex); + { + if (async_terminate) return; + if (async_pending.Count > 0) { + request = async_pending.Requests[0]; + hasRequest = true; + AsyncRequestList_RemoveAt(&async_pending, 0); + } + } + Platform_MutexUnlock(async_pendingMutex); + + if (hasRequest) { + Platform_MutexLock(async_curRequestMutex); + { + async_curRequest = request; + async_curRequestProgress = -2; + } + Platform_MutexUnlock(async_curRequestMutex); + + ProcessRequest(request); + + Platform_MutexLock(async_curRequestMutex); + { + async_curRequest.ID[0] = NULL; + async_curRequestProgress = -3; + } + Platform_MutexUnlock(async_curRequestMutex); + } else { + Platform_EventWait(async_eventHandle); + } + } + } + + Int32 FindRequest(STRING_PURE String* id, AsyncRequest* item) { + Int32 i; + for (i = 0; i < async_processed.Count; i++) { + String requestID = String_FromRawArray(async_processed.Requests[i].ID); + if (!String_Equals(&requestID, id)) continue; + + *item = async_processed.Requests[i]; + return i; + } + return -1; + } + + void ProcessRequest(AsyncRequest* request) { + String url = String_FromRawArray(request->URL); + Platform_Log2("Downloading from %s (type %b)", &url, &request->RequestType); + HttpStatusCode status = HttpStatusCode.OK; + + try { + HttpWebRequest req = MakeRequest(request); + using (HttpWebResponse response = (HttpWebResponse)req.GetResponse()) { + request.ETag = response.Headers.Get("ETag"); + if (response.Headers.Get("Last-Modified") != null) { + request.LastModified = response.LastModified; + } + request.Data = DownloadContent(request, response); + } + } catch (Exception ex) { + if (!(ex is WebException || ex is ArgumentException || ex is UriFormatException || ex is IOException)) throw; + + if (ex is WebException) { + WebException webEx = (WebException)ex; + if (webEx.Response != null) { + status = ((HttpWebResponse)webEx.Response).StatusCode; + webEx.Response.Close(); + } + request.WebEx = webEx; + } + + if (status != HttpStatusCode.OK) { + Utils.LogDebug("Failed to download (" + (int)status + ") from: " + url); + } else { + Utils.LogDebug("Failed to download from: " + url); + } + } + request.TimeDownloaded = DateTime.UtcNow; + + Platform_MutexLock(async_processedMutex); + { + AsyncRequest older; + String id = String_FromRawArray(request->ID); + Int32 index = FindRequest(&id, &older); + + if (index >= 0) { + /* very rare case - priority item was inserted, then inserted again (so put before first item), */ + /* and both items got downloaded before an external function removed them from the queue */ + if (older.TimeAdded > request.TimeAdded) { + AsyncRequest tmp = older; older = *request; *request = tmp; + } + + ASyncRequest_Free(&older); + async_processed.Requests[index] = *request; + } else { + AsyncRequestList_Append(&async_processed, request); + } + } + Platform_MutexUnlock(async_processedMutex); + } + /* + object DownloadContent(Request request, HttpWebResponse response) { + if (request.Type == RequestType.Bitmap) { + MemoryStream data = DownloadBytes(response); + Bitmap bmp = Platform.ReadBmp32Bpp(drawer, data); + + if (bmp == null) { + Utils.LogDebug("Failed to download from: " + request.Url); + } + return bmp; + } else if (request.Type == RequestType.String) { + MemoryStream data = DownloadBytes(response); + byte[] rawBuffer = data.GetBuffer(); + return Encoding.UTF8.GetString(rawBuffer, 0, (int)data.Length); + } else if (request.Type == RequestType.ByteArray) { + MemoryStream data = DownloadBytes(response); + return data.ToArray(); + } else if (request.Type == RequestType.ContentLength) { + return response.ContentLength; + } + return null; + } + + HttpWebRequest MakeRequest(Request request) { + HttpWebRequest req = (HttpWebRequest)WebRequest.Create(request.Url); + req.AutomaticDecompression = DecompressionMethods.GZip; + req.ReadWriteTimeout = 90 * 1000; + req.Timeout = 90 * 1000; + req.Proxy = null; + req.UserAgent = Program.AppName; + req.CookieContainer = Cookies; + req.KeepAlive = KeepAlive; + + if (request.LastModified != DateTime.MinValue) { + req.IfModifiedSince = request.LastModified; + } + if (request.ETag != null) { + req.Headers["If-None-Match"] = request.ETag; + } + + if (request.Data != null) { + req.Method = "POST"; + req.ContentType = "application/x-www-form-urlencoded; charset=UTF-8;"; + byte[] encodedData = Encoding.UTF8.GetBytes((string)request.Data); + req.ContentLength = encodedData.Length; + using (Stream stream = req.GetRequestStream()) { + stream.Write(encodedData, 0, encodedData.Length); + } + } + return req; + } + + static byte[] buffer = new byte[4096 * 8]; + MemoryStream DownloadBytes(HttpWebResponse response) { + int length = (int)response.ContentLength; + MemoryStream dst = length > 0 ? new MemoryStream(length) : new MemoryStream(); + CurrentItemProgress = length > 0 ? 0 : -1; + + using (Stream src = response.GetResponseStream()) { + int read = 0; + while ((read = src.Read(buffer, 0, buffer.Length)) > 0) { + dst.Write(buffer, 0, read); + if (length <= 0) continue; + CurrentItemProgress = (int)(100 * (float)dst.Length / length); + } + } + return dst; + } +}*/ \ No newline at end of file diff --git a/src/Client/AsyncDownloader.h b/src/Client/AsyncDownloader.h index a7738f514..80fa7bf6e 100644 --- a/src/Client/AsyncDownloader.h +++ b/src/Client/AsyncDownloader.h @@ -8,11 +8,10 @@ Copyright 2014-2017 ClassicalSharp | Licensed under BSD-3 */ -#define REQUEST_TYPE_BITMAP 0 -#define REQUEST_TYPE_STRING 1 -#define REQUEST_TYPE_DATA 2 -#define REQUEST_TYPE_CONTENT_LENGTH 3 - +enum REQUEST_TYPE { + REQUEST_TYPE_DATA, REQUEST_TYPE_IMAGE, + REQUEST_TYPE_STRING, REQUEST_TYPE_CONTENT_LENGTH, +}; typedef struct AsyncRequest_ { UInt8 URL[String_BufferSize(STRING_SIZE)]; UInt8 ID[String_BufferSize(STRING_SIZE)]; @@ -35,12 +34,19 @@ typedef struct AsyncRequest_ { void ASyncRequest_Free(AsyncRequest* request); IGameComponent AsyncDownloader_MakeComponent(void); -void AsyncDownloader_Init(STRING_PURE String* skinServer); -void AsyncDownloader_DownloadSkin(STRING_PURE String* identifier, STRING_PURE String* skinName); -void AsyncDownloader_Download(STRING_PURE String* url, bool priority, UInt8 type, STRING_PURE String* identifier); -void AsyncDownloader_Download2(STRING_PURE String* url, bool priority, UInt8 type, STRING_PURE String* identifier, DateTime* lastModified, STRING_PURE String* etag); +void AsyncDownloader_Init(void); void AsyncDownloader_Free(void); -bool AsyncDownloader_Get(STRING_PURE String* identifier, AsyncRequest* item); -bool AsyncDownloader_GetInProgress(AsyncRequest* request, Int32* progress); + +void AsyncDownloader_GetSkin(STRING_PURE String* id, STRING_PURE String* skinName); +void AsyncDownloader_GetData(STRING_PURE String* url, bool priority, STRING_PURE String* id); +void AsyncDownloader_GetImage(STRING_PURE String* url, bool priority, STRING_PURE String* id); +void AsyncDownloader_GetString(STRING_PURE String* url, bool priority, STRING_PURE String* id); +void AsyncDownloader_GetContentLength(STRING_PURE String* url, bool priority, STRING_PURE String* id); +void AsyncDownloader_PostString(STRING_PURE String* url, bool priority, STRING_PURE String* id, STRING_PURE String* contents); +void AsyncDownloader_GetDataEx(STRING_PURE String* url, bool priority, STRING_PURE String* id, DateTime* lastModified, STRING_PURE String* etag); +void AsyncDownloader_GetImageEx(STRING_PURE String* url, bool priority, STRING_PURE String* id, DateTime* lastModified, STRING_PURE String* etag); + +bool AsyncDownloader_Get(STRING_PURE String* id, AsyncRequest* item); +bool AsyncDownloader_GetCurrent(AsyncRequest* request, Int32* progress); void AsyncDownloader_PurgeOldEntriesTask(ScheduledTask* task); #endif \ No newline at end of file diff --git a/src/Client/Client.vcxproj b/src/Client/Client.vcxproj index 813a0886c..af4c9527e 100644 --- a/src/Client/Client.vcxproj +++ b/src/Client/Client.vcxproj @@ -255,6 +255,7 @@ + diff --git a/src/Client/Client.vcxproj.filters b/src/Client/Client.vcxproj.filters index c741df0ac..6ddecefa3 100644 --- a/src/Client/Client.vcxproj.filters +++ b/src/Client/Client.vcxproj.filters @@ -572,5 +572,8 @@ Source Files\Network + + Source Files\Network + \ No newline at end of file diff --git a/src/Client/Entity.c b/src/Client/Entity.c index a96b1f00f..0220f5b46 100644 --- a/src/Client/Entity.c +++ b/src/Client/Entity.c @@ -454,8 +454,7 @@ void TabList_Reset(void) { Platform_MemSet(TabList_ListNames, 0, sizeof(TabList_ListNames)); Platform_MemSet(TabList_GroupNames, 0, sizeof(TabList_GroupNames)); Platform_MemSet(TabList_GroupRanks, 0, sizeof(TabList_GroupRanks)); - /* TODO: Should we be trying to free the buffer here? */ - StringsBuffer_UNSAFE_Reset(&TabList_Buffer); + StringsBuffer_Free(&TabList_Buffer); } IGameComponent TabList_MakeComponent(void) { @@ -692,7 +691,7 @@ void Player_CheckSkin(Player* player) { if (!player->FetchedSkin && entity->Model->UsesSkin) { Player* first = Player_FirstOtherWithSameSkinAndFetchedSkin(player); if (first == NULL) { - AsyncDownloader_DownloadSkin(&skin, &skin); + AsyncDownloader_GetSkin(&skin, &skin); } else { Player_ApplySkin(player, first); } diff --git a/src/Client/Game.c b/src/Client/Game.c index 17f852baf..ede7a2315 100644 --- a/src/Client/Game.c +++ b/src/Client/Game.c @@ -767,16 +767,7 @@ void Audio_SetSounds(Int32 volume) { } void Audio_PlayDigSound(UInt8 type) { } void Audio_PlayStepSound(UInt8 type) { } -void ASyncRequest_Free(AsyncRequest* request) { } -IGameComponent AsyncDownloader_MakeComponent(void) { return IGameComponent_MakeEmpty(); } -void AsyncDownloader_Init(STRING_PURE String* skinServer) { } -void AsyncDownloader_DownloadSkin(STRING_PURE String* identifier, STRING_PURE String* skinName) { } -void AsyncDownloader_Download(STRING_PURE String* url, bool priority, UInt8 type, STRING_PURE String* identifier) { } -void AsyncDownloader_Download2(STRING_PURE String* url, bool priority, UInt8 type, STRING_PURE String* identifier, DateTime* lastModified, STRING_PURE String* etag) { } -void AsyncDownloader_Free(void) { } -bool AsyncDownloader_Get(STRING_PURE String* identifier, AsyncRequest* item) { return false; } -bool AsyncDownloader_GetInProgress(AsyncRequest* request, Int32* progress) { return false; } -void AsyncDownloader_PurgeOldEntriesTask(ScheduledTask* task) { } +/* TODO: needed for async downloading */ DateTime DateTime_FromTotalMs(Int64 ms) { DateTime time; return time; } void Bitmap_EncodePng(Bitmap* bmp, Stream* stream) { } diff --git a/src/Client/PacketHandlers.c b/src/Client/PacketHandlers.c index 29841510c..70851d146 100644 --- a/src/Client/PacketHandlers.c +++ b/src/Client/PacketHandlers.c @@ -214,14 +214,14 @@ void WoM_CheckMotd(void) { applied in the new world if the async 'get env request' didn't complete before the old world was unloaded */ wom_counter++; WoM_UpdateIdentifier(); - AsyncDownloader_Download(&url, true, REQUEST_TYPE_STRING, &wom_identifier); + AsyncDownloader_GetString(&url, true, &wom_identifier); wom_sendId = true; } void WoM_CheckSendWomID(void) { if (wom_sendId && !wom_sentId) { - String msg = String_FromConst("/womid WoMClient-2.0.7") - ServerConnection_SendChat(&msg); + String msg = String_FromConst("/womid WoMClient-2.0.7"); + ServerConnection_SendChat(&msg); wom_sentId = true; } } diff --git a/src/Client/Platform.h b/src/Client/Platform.h index 45a0db317..c7c20406f 100644 --- a/src/Client/Platform.h +++ b/src/Client/Platform.h @@ -52,9 +52,20 @@ UInt32 Platform_FileLength(void* file); void Platform_ThreadSleep(UInt32 milliseconds); typedef void Platform_ThreadFunc(void); void* Platform_ThreadStart(Platform_ThreadFunc* func); +void Platform_ThreadJoin(void* handle); /* Frees handle to thread - NOT THE THREAD ITSELF */ void Platform_ThreadFreeHandle(void* handle); +void* Platform_MutexCreate(void); +void Platform_MutexFree(void* handle); +void Platform_MutexLock(void* handle); +void Platform_MutexUnlock(void* handle); + +void* Platform_EventCreate(void); +void Platform_EventFree(void* handle); +void Platform_EventSet(void* handle); +void Platform_EventWait(void* handle); + typedef Int64 Stopwatch; void Stopwatch_Start(Stopwatch* timer); Int32 Stopwatch_ElapsedMicroseconds(Stopwatch* timer); diff --git a/src/Client/Screens.c b/src/Client/Screens.c index 77b70b483..1a502f00d 100644 --- a/src/Client/Screens.c +++ b/src/Client/Screens.c @@ -767,7 +767,7 @@ void ChatScreen_SetInitialMessages(ChatScreen* screen) { void ChatScreen_CheckOtherStatuses(ChatScreen* screen) { AsyncRequest request; Int32 progress; - bool hasRequest = AsyncDownloader_GetInProgress(&request, &progress); + bool hasRequest = AsyncDownloader_GetCurrent(&request, &progress); String id = String_FromRawArray(request.ID); String terrain = String_FromConst("terrain"); diff --git a/src/Client/ServerConnection.c b/src/Client/ServerConnection.c index 030d40356..e38f09adc 100644 --- a/src/Client/ServerConnection.c +++ b/src/Client/ServerConnection.c @@ -63,10 +63,10 @@ void ServerConnection_DownloadTexturePack(STRING_PURE String* url) { String zip = String_FromConst(".zip"); if (String_ContainsString(url, &zip)) { String texPack = String_FromConst("texturePack"); - AsyncDownloader_Download2(url, true, REQUEST_TYPE_DATA, &texPack, &lastModified, &etag); + AsyncDownloader_GetDataEx(url, true, &texPack, &lastModified, &etag); } else { String terrain = String_FromConst("terrain"); - AsyncDownloader_Download2(url, true, REQUEST_TYPE_BITMAP, &terrain, &lastModified, &etag); + AsyncDownloader_GetImageEx(url, true, &terrain, &lastModified, &etag); } } diff --git a/src/Client/String.c b/src/Client/String.c index 4e3a60b9a..663dc7a87 100644 --- a/src/Client/String.c +++ b/src/Client/String.c @@ -641,7 +641,8 @@ bool Convert_TryParseBool(STRING_PURE String* str, bool* value) { #define STRINGSBUFFER_LEN_SHIFT 9 #define STRINGSBUFFER_LEN_MASK 0x1FFUL void StringsBuffer_Init(StringsBuffer* buffer) { - StringsBuffer_UNSAFE_Reset(buffer); + buffer->Count = 0; + buffer->UsedElems = 0; buffer->TextBuffer = buffer->DefaultBuffer; buffer->FlagsBuffer = buffer->DefaultFlags; buffer->TextBufferElems = STRINGSBUFFER_BUFFER_DEF_SIZE; @@ -655,12 +656,7 @@ void StringsBuffer_Free(StringsBuffer* buffer) { if (buffer->FlagsBuffer != buffer->DefaultFlags) { Platform_MemFree(&buffer->FlagsBuffer); } - StringsBuffer_UNSAFE_Reset(buffer); -} - -void StringsBuffer_UNSAFE_Reset(StringsBuffer* buffer) { - buffer->Count = 0; - buffer->UsedElems = 0; + StringsBuffer_Init(buffer); } void StringsBuffer_Get(StringsBuffer* buffer, UInt32 index, STRING_TRANSIENT String* text) { diff --git a/src/Client/String.h b/src/Client/String.h index 2f963724c..ff08b3f5d 100644 --- a/src/Client/String.h +++ b/src/Client/String.h @@ -115,7 +115,6 @@ typedef struct StringsBuffer_ { void StringsBuffer_Init(StringsBuffer* buffer); void StringsBuffer_Free(StringsBuffer* buffer); -void StringsBuffer_UNSAFE_Reset(StringsBuffer* buffer); void StringsBuffer_Get(StringsBuffer* buffer, UInt32 index, STRING_TRANSIENT String* text); STRING_REF String StringsBuffer_UNSAFE_Get(StringsBuffer* buffer, UInt32 index); void StringsBuffer_Resize(void** buffer, UInt32* elems, UInt32 elemSize, UInt32 defElems, UInt32 expandElems); diff --git a/src/Client/WinPlatform.c b/src/Client/WinPlatform.c index 830578f4c..8c44fbd72 100644 --- a/src/Client/WinPlatform.c +++ b/src/Client/WinPlatform.c @@ -4,6 +4,7 @@ #include "ExtMath.h" #include "ErrorHandler.h" #include "Drawer2D.h" +#include "Funcs.h" #define WIN32_LEAN_AND_MEAN #define NOSERVICE #define NOMCX @@ -297,12 +298,63 @@ void* Platform_ThreadStart(Platform_ThreadFunc* func) { return handle; } +void Platform_ThreadJoin(void* handle) { + WaitForSingleObject((HANDLE)handle, INFINITE); +} + void Platform_ThreadFreeHandle(void* handle) { if (!CloseHandle((HANDLE)handle)) { ErrorHandler_FailWithCode(GetLastError(), "Freeing thread handle"); } } +CRITICAL_SECTION mutexList[3]; +Int32 mutexIndex; +void* Platform_MutexCreate(void) { + if (mutexIndex == Array_Elems(mutexList)) { + ErrorHandler_Fail("Cannot allocate another mutex"); + return NULL; + } else { + CRITICAL_SECTION* ptr = &mutexList[mutexIndex]; + InitializeCriticalSection(ptr); mutexIndex++; + return ptr; + } +} + +void Platform_MutexFree(void* handle) { + DeleteCriticalSection((CRITICAL_SECTION*)handle); +} + +void Platform_MutexLock(void* handle) { + EnterCriticalSection((CRITICAL_SECTION*)handle); +} + +void Platform_MutexUnlock(void* handle) { + LeaveCriticalSection((CRITICAL_SECTION*)handle); +} + +void* Platform_EventCreate(void) { + void* handle = CreateEventA(NULL, false, false, NULL); + if (handle == NULL) { + ErrorHandler_FailWithCode(GetLastError(), "Creating event"); + } + return handle; +} + +void Platform_EventFree(void* handle) { + if (!CloseHandle((HANDLE)handle)) { + ErrorHandler_FailWithCode(GetLastError(), "Freeing event"); + } +} + +void Platform_EventSet(void* handle) { + SetEvent((HANDLE)handle); +} + +void Platform_EventWait(void* handle) { + WaitForSingleObject((HANDLE)handle, INFINITE); +} + void Stopwatch_Start(Stopwatch* timer) { if (stopwatch_highResolution) { QueryPerformanceCounter(timer);