diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | src/OSSupport/Promise.cpp | 54 | ||||
-rw-r--r-- | src/OSSupport/Promise.h | 38 | ||||
-rw-r--r-- | src/OSSupport/Queue.h | 109 | ||||
-rw-r--r-- | src/WorldStorage/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/WorldStorage/WorldStorage.cpp | 136 | ||||
-rw-r--r-- | src/WorldStorage/WorldStorage.h | 28 |
7 files changed, 253 insertions, 115 deletions
diff --git a/.gitignore b/.gitignore index 977122047..b15b6d502 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,7 @@ cloc.xsl ## emacs *.*~ *~ +*.orig # world inside source ChunkWorx.ini diff --git a/src/OSSupport/Promise.cpp b/src/OSSupport/Promise.cpp new file mode 100644 index 000000000..b31869334 --- /dev/null +++ b/src/OSSupport/Promise.cpp @@ -0,0 +1,54 @@ + +#include "Globals.h" + +#include "Promise.h" + +cPromise * cPromise::WaitFor(cPromise * a_Promise) +{ + return new cCombinedPromise(this, a_Promise); +} + +cPromise * cPromise::CancelOn(volatile bool& cancelation) +{ + return new cCancelablePromise(this, cancelation); +} + +void cPromise::Wait() +{ + while(!IsCompleted()){}; //busywait best we can do until waitany +} + + +cCombinedPromise::cCombinedPromise(cPromise* a_left, cPromise* a_right) : + cPromise(), + m_left(a_left), + m_right(a_right) +{ +} + +cCombinedPromise::~cCombinedPromise() +{ +} + +bool cCombinedPromise::IsCompleted() +{ + return m_left->IsCompleted() || m_right->IsCompleted(); +} + +cCancelablePromise::cCancelablePromise(cPromise* a_wrapped, volatile bool& a_cancel) : + cPromise(), + m_cancel(a_cancel), + m_wrapped(a_wrapped) +{ +} + +cCancelablePromise::~cCancelablePromise () +{ +} + +bool cCancelablePromise::IsCompleted() +{ + return m_cancel || m_wrapped->IsCompleted(); +} + + diff --git a/src/OSSupport/Promise.h b/src/OSSupport/Promise.h new file mode 100644 index 000000000..83d04860b --- /dev/null +++ b/src/OSSupport/Promise.h @@ -0,0 +1,38 @@ +#pragma once + +class cCombinedPromise; + + +class cPromise { + public: + cPromise() {} + virtual ~cPromise () {} + cPromise * WaitFor(cPromise * a_Promise); + cPromise * CancelOn(volatile bool& cancelationtoken); + void Wait(); + virtual bool IsCompleted() = 0; + //TODO:Expose Events for waiting on +}; + +class cCombinedPromise : public cPromise { +public: + cCombinedPromise(cPromise*, cPromise*); + ~cCombinedPromise(); + virtual bool IsCompleted(); +private: + cPromise* m_left; + cPromise* m_right; +}; + +class cCancelablePromise : public cPromise { +public: + cCancelablePromise(cPromise*, volatile bool&); + ~cCancelablePromise(); + virtual bool IsCompleted(); +private: + volatile bool& m_cancel; + cPromise* m_wrapped; +}; + + + diff --git a/src/OSSupport/Queue.h b/src/OSSupport/Queue.h index 4571272b3..eb323b067 100644 --- a/src/OSSupport/Queue.h +++ b/src/OSSupport/Queue.h @@ -1,31 +1,104 @@ + #pragma once +#include <list> + +#include "../OSSupport/Promise.h" + +//this empty struct allows function inlining template<class T> -class cDeleter +struct cQueueFuncs { public: static void Delete(T) {}; + static void Combine(T&, const T) {}; }; -template<class T, class D = cDeleter<T>> +template<class ItemType, class Funcs = cQueueFuncs<ItemType> > class cQueue { + +typedef typename std::list<ItemType> ListType; +//magic typedef to persuade clang that the iterator is a type +typedef typename ListType::iterator iterator; public: - cQueue(int warnsize); - cQueue(cQueue<T>& queue); - ~cQueue(); - - void EnqueueItem(T item); - bool TryDequeueItem(T& item); - T DequeueItem(); - void BlockTillEmpty(cEvent CancelationEvent); - void Clear(); - int Size(); - + cQueue() {} + ~cQueue() {} + + void EnqueueItem(ItemType a_item) + { + cCSLock Lock(m_CS); + m_contents.push_back(a_item); + m_evtAdded.Set(); + } + void EnqueueItemIfNotPresent(ItemType a_item) + { + cCSLock Lock(m_CS); + + for (iterator itr = m_contents.begin(); itr != m_contents.end(); ++itr) + { + if((*itr) == a_item) { + Funcs funcTable; + funcTable.Combine(*itr,a_item); + return; + } + } + m_contents.push_back(a_item); + m_evtAdded.Set(); + } + bool TryDequeueItem(ItemType& item) + { + cCSLock Lock(m_CS); + if (m_contents.size() == 0) return false; + item = m_contents.front(); + m_contents.pop_front(); + return true; + } + ItemType DequeueItem() + { + cCSLock Lock(m_CS); + while (m_contents.size() == 0) + { + cCSUnlock Unlock(m_CS); + m_evtAdded.Wait(); + } + return m_contents.pop_front(); + } + cPromise* BlockTillEmpty() { + return new cEmptyQueuePromise(this); + } + //can all be inlined when delete is a noop + void Clear() + { + cCSLock Lock(m_CS); + Funcs funcTable; + while (!m_contents.empty()) + { + funcTable.Delete(m_contents.front()); + m_contents.pop_front(); + } + } + size_t Size() + { + cCSLock Lock(m_CS); + return m_contents.size(); + } + bool Remove(ItemType item) + { + cCSLock Lock(m_CS); + m_contents.remove(item); + } + private: - int warnsize; - std::list<T> contents; -}; + ListType m_contents; + cCriticalSection m_CS; + cEvent m_evtAdded; -//template classes must be implemented in the header -#include "Queue.inc" + class cEmptyQueuePromise : public cPromise { + public: + cEmptyQueuePromise(cQueue* a_Queue) : cPromise(), m_Queue(a_Queue) {} + virtual bool IsCompleted() {return m_Queue->Size() != 0;} + private: + cQueue* m_Queue; + }; +}; diff --git a/src/WorldStorage/CMakeLists.txt b/src/WorldStorage/CMakeLists.txt index d431bdf6a..2c83c4662 100644 --- a/src/WorldStorage/CMakeLists.txt +++ b/src/WorldStorage/CMakeLists.txt @@ -9,3 +9,5 @@ file(GLOB SOURCE ) add_library(WorldStorage ${SOURCE}) + +target_link_libraries(WorldStorage OSSupport) diff --git a/src/WorldStorage/WorldStorage.cpp b/src/WorldStorage/WorldStorage.cpp index f290ec128..c3bfbd4f6 100644 --- a/src/WorldStorage/WorldStorage.cpp +++ b/src/WorldStorage/WorldStorage.cpp @@ -13,7 +13,7 @@ #include "../Generating/ChunkGenerator.h" #include "../Entities/Entity.h" #include "../BlockEntities/BlockEntity.h" - +#include "../OSSupport/Promise.h" @@ -63,8 +63,6 @@ cWorldStorage::~cWorldStorage() { delete *itr; } // for itr - m_Schemas[] - m_LoadQueue.clear(); - m_SaveQueue.clear(); } @@ -98,9 +96,7 @@ void cWorldStorage::WaitForFinish(void) LOG("Waiting for the world storage to finish saving"); { - // Cancel all loading requests: - cCSLock Lock(m_CSQueues); - m_LoadQueue.clear(); + m_LoadQueue.Clear(); } // Wait for the saving to finish: @@ -120,32 +116,36 @@ void cWorldStorage::WaitForFinish(void) void cWorldStorage::WaitForQueuesEmpty(void) { - cCSLock Lock(m_CSQueues); - while (!m_ShouldTerminate && (!m_LoadQueue.empty() || !m_SaveQueue.empty())) - { - cCSUnlock Unlock(Lock); - m_evtRemoved.Wait(); - } + + cPromise * LoadPromise = m_LoadQueue.BlockTillEmpty(); + cPromise * SavePromise = m_SaveQueue.BlockTillEmpty(); + cPromise * QueuePromise = LoadPromise->WaitFor(SavePromise); + cPromise * CancelPromise = QueuePromise->CancelOn(m_ShouldTerminate); + CancelPromise->Wait(); + delete CancelPromise; + delete QueuePromise; + delete SavePromise; + delete LoadPromise; } -int cWorldStorage::GetLoadQueueLength(void) +size_t cWorldStorage::GetLoadQueueLength(void) { cCSLock Lock(m_CSQueues); - return (int)m_LoadQueue.size(); + return m_LoadQueue.Size(); } -int cWorldStorage::GetSaveQueueLength(void) +size_t cWorldStorage::GetSaveQueueLength(void) { cCSLock Lock(m_CSQueues); - return (int)m_SaveQueue.size(); + return m_SaveQueue.Size(); } @@ -154,22 +154,7 @@ int cWorldStorage::GetSaveQueueLength(void) void cWorldStorage::QueueLoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, bool a_Generate) { - // Queues the chunk for loading; if not loaded, the chunk will be generated - { - cCSLock Lock(m_CSQueues); - - // Check if already in the queue: - for (sChunkLoadQueue::iterator itr = m_LoadQueue.begin(); itr != m_LoadQueue.end(); ++itr) - { - if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkY == a_ChunkY) && (itr->m_ChunkZ == a_ChunkZ) && (itr->m_Generate == a_Generate)) - { - return; - } - } - m_LoadQueue.push_back(sChunkLoad(a_ChunkX, a_ChunkY, a_ChunkZ, a_Generate)); - } - - m_Event.Set(); + m_LoadQueue.EnqueueItemIfNotPresent(sChunkLoad(a_ChunkX, a_ChunkY, a_ChunkZ, a_Generate)); } @@ -178,12 +163,7 @@ void cWorldStorage::QueueLoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, boo void cWorldStorage::QueueSaveChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ) { - { - cCSLock Lock(m_CSQueues); - m_SaveQueue.remove (cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ)); // Don't add twice - m_SaveQueue.push_back(cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ)); - } - m_Event.Set(); + m_SaveQueue.EnqueueItemIfNotPresent(cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ)); } @@ -192,12 +172,8 @@ void cWorldStorage::QueueSaveChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ) void cWorldStorage::QueueSavedMessage(void) { - // Pushes a special coord pair into the queue, signalizing a message instead: - { - cCSLock Lock(m_CSQueues); - m_SaveQueue.push_back(cChunkCoords(0, CHUNK_Y_MESSAGE, 0)); - } - m_Event.Set(); + // Pushes a special coord pair into the queue, signalizing a message instead + m_SaveQueue.EnqueueItem(cChunkCoords(0, CHUNK_Y_MESSAGE, 0)); } @@ -206,7 +182,7 @@ void cWorldStorage::QueueSavedMessage(void) void cWorldStorage::UnqueueLoad(int a_ChunkX, int a_ChunkY, int a_ChunkZ) { - cCSLock Lock(m_CSQueues); + /*cCSLock Lock(m_CSQueues); for (sChunkLoadQueue::iterator itr = m_LoadQueue.begin(); itr != m_LoadQueue.end(); ++itr) { if ((itr->m_ChunkX != a_ChunkX) || (itr->m_ChunkY != a_ChunkY) || (itr->m_ChunkZ != a_ChunkZ)) @@ -217,7 +193,8 @@ void cWorldStorage::UnqueueLoad(int a_ChunkX, int a_ChunkY, int a_ChunkZ) Lock.Unlock(); m_evtRemoved.Set(); return; - } // for itr - m_LoadQueue[] + } // for itr - m_LoadQueue[]*/ + m_LoadQueue.Remove(sChunkLoad(a_ChunkX, a_ChunkY, a_ChunkZ,true)); } @@ -226,11 +203,7 @@ void cWorldStorage::UnqueueLoad(int a_ChunkX, int a_ChunkY, int a_ChunkZ) void cWorldStorage::UnqueueSave(const cChunkCoords & a_Chunk) { - { - cCSLock Lock(m_CSQueues); - m_SaveQueue.remove(a_Chunk); - } - m_evtRemoved.Set(); + m_SaveQueue.Remove(a_Chunk); } @@ -281,19 +254,19 @@ void cWorldStorage::Execute(void) m_Event.Wait(); // Process both queues until they are empty again: - bool HasMore; + bool Success; do { - HasMore = false; + Success = false; if (m_ShouldTerminate) { return; } - HasMore = LoadOneChunk(); - HasMore = HasMore | SaveOneChunk(); + Success = LoadOneChunk(); + Success |= SaveOneChunk(); m_evtRemoved.Set(); - } while (HasMore); + } while (Success); } } @@ -304,19 +277,7 @@ void cWorldStorage::Execute(void) bool cWorldStorage::LoadOneChunk(void) { sChunkLoad ToLoad(0, 0, 0, false); - bool HasMore; - bool ShouldLoad = false; - { - cCSLock Lock(m_CSQueues); - if (!m_LoadQueue.empty()) - { - ToLoad = m_LoadQueue.front(); - m_LoadQueue.pop_front(); - ShouldLoad = true; - } - HasMore = !m_LoadQueue.empty(); - } - + bool ShouldLoad = m_LoadQueue.TryDequeueItem(ToLoad); if (ShouldLoad && !LoadChunk(ToLoad.m_ChunkX, ToLoad.m_ChunkY, ToLoad.m_ChunkZ)) { if (ToLoad.m_Generate) @@ -330,7 +291,7 @@ bool cWorldStorage::LoadOneChunk(void) // m_World->ChunkLoadFailed(ToLoad.m_ChunkX, ToLoad.m_ChunkY, ToLoad.m_ChunkZ); } } - return HasMore; + return ShouldLoad; } @@ -339,33 +300,24 @@ bool cWorldStorage::LoadOneChunk(void) bool cWorldStorage::SaveOneChunk(void) { - cChunkCoords Save(0, 0, 0); - bool HasMore; - bool ShouldSave = false; - { - cCSLock Lock(m_CSQueues); - if (!m_SaveQueue.empty()) + cChunkCoords ToSave(0, 0, 0); + bool ShouldSave = m_SaveQueue.TryDequeueItem(ToSave); + if(ShouldSave) { + if (ToSave.m_ChunkY == CHUNK_Y_MESSAGE) { - Save = m_SaveQueue.front(); - m_SaveQueue.pop_front(); - ShouldSave = true; + LOGINFO("Saved all chunks in world %s", m_World->GetName().c_str()); + return ShouldSave; } - HasMore = !m_SaveQueue.empty(); - } - if (Save.m_ChunkY == CHUNK_Y_MESSAGE) - { - LOGINFO("Saved all chunks in world %s", m_World->GetName().c_str()); - return HasMore; - } - if (ShouldSave && m_World->IsChunkValid(Save.m_ChunkX, Save.m_ChunkZ)) - { - m_World->MarkChunkSaving(Save.m_ChunkX, Save.m_ChunkZ); - if (m_SaveSchema->SaveChunk(Save)) + if (ShouldSave && m_World->IsChunkValid(ToSave.m_ChunkX, ToSave.m_ChunkZ)) { - m_World->MarkChunkSaved(Save.m_ChunkX, Save.m_ChunkZ); + m_World->MarkChunkSaving(ToSave.m_ChunkX, ToSave.m_ChunkZ); + if (m_SaveSchema->SaveChunk(ToSave)) + { + m_World->MarkChunkSaved(ToSave.m_ChunkX, ToSave.m_ChunkZ); + } } } - return HasMore; + return ShouldSave; } diff --git a/src/WorldStorage/WorldStorage.h b/src/WorldStorage/WorldStorage.h index 007d37571..c3eb96ce8 100644 --- a/src/WorldStorage/WorldStorage.h +++ b/src/WorldStorage/WorldStorage.h @@ -16,6 +16,7 @@ #include "../ChunkDef.h" #include "../OSSupport/IsThread.h" +#include "../OSSupport/Queue.h" @@ -24,6 +25,8 @@ // fwd: class cWorld; +typedef cQueue<cChunkCoords> cChunkCoordsQueue; + @@ -78,8 +81,8 @@ public: void WaitForFinish(void); void WaitForQueuesEmpty(void); - int GetLoadQueueLength(void); - int GetSaveQueueLength(void); + size_t GetLoadQueueLength(void); + size_t GetSaveQueueLength(void); protected: @@ -91,9 +94,24 @@ protected: bool m_Generate; // If true, the chunk will be generated if it cannot be loaded sChunkLoad(int a_ChunkX, int a_ChunkY, int a_ChunkZ, bool a_Generate) : m_ChunkX(a_ChunkX), m_ChunkY(a_ChunkY), m_ChunkZ(a_ChunkZ), m_Generate(a_Generate) {} + + bool operator==(const sChunkLoad other) const + { + return this->m_ChunkX == other.m_ChunkX && + this->m_ChunkY == other.m_ChunkY && + this->m_ChunkZ == other.m_ChunkZ; + } } ; - - typedef std::list<sChunkLoad> sChunkLoadQueue; + + struct FuncTable { + static void Delete(sChunkLoad) {}; + static void Combine(sChunkLoad& a_orig, const sChunkLoad a_new) + { + a_orig.m_Generate |= a_new.m_Generate; + }; + }; + + typedef cQueue<sChunkLoad,FuncTable> sChunkLoadQueue; cWorld * m_World; AString m_StorageSchemaName; @@ -101,7 +119,7 @@ protected: // Both queues are locked by the same CS cCriticalSection m_CSQueues; sChunkLoadQueue m_LoadQueue; - cChunkCoordsList m_SaveQueue; + cChunkCoordsQueue m_SaveQueue; cEvent m_Event; // Set when there's any addition to the queues cEvent m_evtRemoved; // Set when an item has been removed from the queue, either by the worker thread or the Unqueue methods |