1
0

Merge pull request #2522 from cuberite/FixLoaderGenRaceCondition

Fixed a race condition between chunk loader and generator.
This commit is contained in:
worktycho 2015-10-04 14:55:42 +01:00
commit 0786fda18b
10 changed files with 45 additions and 65 deletions

View File

@ -348,11 +348,11 @@ static int tolua_cWorld_PrepareChunk(lua_State * tolua_S)
} }
// cChunkCoordCallback override: // cChunkCoordCallback override:
virtual void Call(int a_CBChunkX, int a_CBChunkZ) override virtual void Call(int a_CBChunkX, int a_CBChunkZ, bool a_IsSuccess) override
{ {
if (m_Callback.IsValid()) if (m_Callback.IsValid())
{ {
m_LuaState.Call(m_Callback, a_CBChunkX, a_CBChunkZ); m_LuaState.Call(m_Callback, a_CBChunkX, a_CBChunkZ, a_IsSuccess);
} }
// This is the last reference of this object, we must delete it so that it doesn't leak: // This is the last reference of this object, we must delete it so that it doesn't leak:

View File

@ -464,7 +464,8 @@ public:
virtual ~cChunkCoordCallback() {} virtual ~cChunkCoordCallback() {}
virtual void Call(int a_ChunkX, int a_ChunkZ) = 0; /** Called with the chunk's coords, and an optional operation status flag for operations that support it. */
virtual void Call(int a_ChunkX, int a_ChunkZ, bool a_IsSuccess) = 0;
} ; } ;

View File

@ -2396,10 +2396,10 @@ void cChunkMap::PrepareChunk(int a_ChunkX, int a_ChunkZ, std::unique_ptr<cChunkC
return; return;
} }
// The chunk is present and lit, just call the callback: // The chunk is present and lit, just call the callback, report as success:
if (a_Callback != nullptr) if (a_Callback != nullptr)
{ {
a_Callback->Call(a_ChunkX, a_ChunkZ); a_Callback->Call(a_ChunkX, a_ChunkZ, true);
} }
} }
@ -2432,9 +2432,19 @@ bool cChunkMap::GenerateChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback *
} }
// cChunkCoordCallback override: // cChunkCoordCallback override:
virtual void Call(int a_CBChunkX, int a_CBChunkZ) override virtual void Call(int a_CBChunkX, int a_CBChunkZ, bool a_CBIsSuccess) override
{ {
// The chunk has been loaded or an error occurred, check if it's valid now: // If success is reported, the chunk is already valid, no need to do anything else:
if (a_CBIsSuccess)
{
if (m_Callback != nullptr)
{
m_Callback->Call(a_CBChunkX, a_CBChunkZ, true);
}
return;
}
// The chunk failed to load, generate it:
cCSLock Lock(m_ChunkMap.m_CSLayers); cCSLock Lock(m_ChunkMap.m_CSLayers);
cChunkPtr CBChunk = m_ChunkMap.GetChunkNoLoad(a_CBChunkX, a_CBChunkZ); cChunkPtr CBChunk = m_ChunkMap.GetChunkNoLoad(a_CBChunkX, a_CBChunkZ);
@ -2443,23 +2453,13 @@ bool cChunkMap::GenerateChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback *
// An error occurred, but we promised to call the callback, so call it even when there's no real chunk data: // An error occurred, but we promised to call the callback, so call it even when there's no real chunk data:
if (m_Callback != nullptr) if (m_Callback != nullptr)
{ {
m_Callback->Call(a_CBChunkX, a_CBChunkZ); m_Callback->Call(a_CBChunkX, a_CBChunkZ, false);
} }
return; return;
} }
// If the chunk is not valid, queue it in the generator: CBChunk->SetPresence(cChunk::cpQueued);
if (!CBChunk->IsValid()) m_World.GetGenerator().QueueGenerateChunk(a_CBChunkX, a_CBChunkZ, false, m_Callback);
{
m_World.GetGenerator().QueueGenerateChunk(a_CBChunkX, a_CBChunkZ, false, m_Callback);
return;
}
// The chunk was loaded, call the callback:
if (m_Callback != nullptr)
{
m_Callback->Call(a_CBChunkX, a_CBChunkZ);
}
} }
protected: protected:
@ -2474,7 +2474,7 @@ bool cChunkMap::GenerateChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback *
// The chunk is valid, just call the callback: // The chunk is valid, just call the callback:
if (a_Callback != nullptr) if (a_Callback != nullptr)
{ {
a_Callback->Call(a_ChunkX, a_ChunkZ); a_Callback->Call(a_ChunkX, a_ChunkZ, true);
} }
return true; return true;
} }

View File

@ -27,7 +27,7 @@
class cNotifyChunkSender : class cNotifyChunkSender :
public cChunkCoordCallback public cChunkCoordCallback
{ {
virtual void Call(int a_ChunkX, int a_ChunkZ) override virtual void Call(int a_ChunkX, int a_ChunkZ, bool a_IsSuccess) override
{ {
cChunkSender & ChunkSender = m_ChunkSender; cChunkSender & ChunkSender = m_ChunkSender;
m_World.DoWithChunk( m_World.DoWithChunk(

View File

@ -248,13 +248,13 @@ void cChunkGenerator::Execute(void)
LastReportTick = clock(); LastReportTick = clock();
} }
// Skip the chunk if it's already generated and regeneration is not forced: // Skip the chunk if it's already generated and regeneration is not forced. Report as success:
if (!item.m_ForceGenerate && m_ChunkSink->IsChunkValid(item.m_ChunkX, item.m_ChunkZ)) if (!item.m_ForceGenerate && m_ChunkSink->IsChunkValid(item.m_ChunkX, item.m_ChunkZ))
{ {
LOGD("Chunk [%d, %d] already generated, skipping generation", item.m_ChunkX, item.m_ChunkZ); LOGD("Chunk [%d, %d] already generated, skipping generation", item.m_ChunkX, item.m_ChunkZ);
if (item.m_Callback != nullptr) if (item.m_Callback != nullptr)
{ {
item.m_Callback->Call(item.m_ChunkX, item.m_ChunkZ); item.m_Callback->Call(item.m_ChunkX, item.m_ChunkZ, true);
} }
continue; continue;
} }
@ -265,7 +265,7 @@ void cChunkGenerator::Execute(void)
LOGWARNING("Chunk generator overloaded, skipping chunk [%d, %d]", item.m_ChunkX, item.m_ChunkZ); LOGWARNING("Chunk generator overloaded, skipping chunk [%d, %d]", item.m_ChunkX, item.m_ChunkZ);
if (item.m_Callback != nullptr) if (item.m_Callback != nullptr)
{ {
item.m_Callback->Call(item.m_ChunkX, item.m_ChunkZ); item.m_Callback->Call(item.m_ChunkX, item.m_ChunkZ, false);
} }
continue; continue;
} }
@ -275,7 +275,7 @@ void cChunkGenerator::Execute(void)
DoGenerate(item.m_ChunkX, item.m_ChunkZ); DoGenerate(item.m_ChunkX, item.m_ChunkZ);
if (item.m_Callback != nullptr) if (item.m_Callback != nullptr)
{ {
item.m_Callback->Call(item.m_ChunkX, item.m_ChunkZ); item.m_Callback->Call(item.m_ChunkX, item.m_ChunkZ, true);
} }
NumChunksGenerated++; NumChunksGenerated++;
} // while (!bStop) } // while (!bStop)
@ -296,8 +296,8 @@ void cChunkGenerator::DoGenerate(int a_ChunkX, int a_ChunkZ)
m_PluginInterface->CallHookChunkGenerated(ChunkDesc); m_PluginInterface->CallHookChunkGenerated(ChunkDesc);
#ifdef _DEBUG #ifdef _DEBUG
// Verify that the generator has produced valid data: // Verify that the generator has produced valid data:
ChunkDesc.VerifyHeightmap(); ChunkDesc.VerifyHeightmap();
#endif #endif
m_ChunkSink->OnChunkGenerated(ChunkDesc); m_ChunkSink->OnChunkGenerated(ChunkDesc);

View File

@ -238,12 +238,12 @@ void cLightingThread::Execute(void)
void cLightingThread::LightChunk(cLightingChunkStay & a_Item) void cLightingThread::LightChunk(cLightingChunkStay & a_Item)
{ {
// If the chunk is already lit, skip it: // If the chunk is already lit, skip it (report as success):
if (m_World->IsChunkLighted(a_Item.m_ChunkX, a_Item.m_ChunkZ)) if (m_World->IsChunkLighted(a_Item.m_ChunkX, a_Item.m_ChunkZ))
{ {
if (a_Item.m_CallbackAfter != nullptr) if (a_Item.m_CallbackAfter != nullptr)
{ {
a_Item.m_CallbackAfter->Call(a_Item.m_ChunkX, a_Item.m_ChunkZ); a_Item.m_CallbackAfter->Call(a_Item.m_ChunkX, a_Item.m_ChunkZ, true);
} }
return; return;
} }
@ -324,7 +324,7 @@ void cLightingThread::LightChunk(cLightingChunkStay & a_Item)
if (a_Item.m_CallbackAfter != nullptr) if (a_Item.m_CallbackAfter != nullptr)
{ {
a_Item.m_CallbackAfter->Call(a_Item.m_ChunkX, a_Item.m_ChunkZ); a_Item.m_CallbackAfter->Call(a_Item.m_ChunkX, a_Item.m_ChunkZ, true);
} }
} }

View File

@ -60,7 +60,8 @@ public:
void Stop(void); void Stop(void);
/** Queues the entire chunk for lighting */ /** Queues the entire chunk for lighting.
The callback, if specified, is called after the lighting has been processed. */
void QueueChunk(int a_ChunkX, int a_ChunkZ, std::unique_ptr<cChunkCoordCallback> a_CallbackAfter); void QueueChunk(int a_ChunkX, int a_ChunkZ, std::unique_ptr<cChunkCoordCallback> a_CallbackAfter);
/** Blocks until the queue is empty or the thread is terminated */ /** Blocks until the queue is empty or the thread is terminated */

View File

@ -20,7 +20,7 @@ protected:
cSpawnPrepare & m_SpawnPrepare; cSpawnPrepare & m_SpawnPrepare;
virtual void Call(int a_ChunkX, int a_ChunkZ) override virtual void Call(int a_ChunkX, int a_ChunkZ, bool a_IsSuccess) override
{ {
m_SpawnPrepare.PreparedChunkCallback(a_ChunkX, a_ChunkZ); m_SpawnPrepare.PreparedChunkCallback(a_ChunkX, a_ChunkZ);
} }

View File

@ -164,32 +164,6 @@ void cWorldStorage::QueueSaveChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallba
void cWorldStorage::UnqueueLoad(int a_ChunkX, int a_ChunkZ)
{
m_LoadQueue.RemoveIf([=](cChunkCoordsWithCallback & a_Item)
{
return (a_Item.m_ChunkX == a_ChunkX) && (a_Item.m_ChunkZ == a_ChunkZ);
}
);
}
void cWorldStorage::UnqueueSave(const cChunkCoords & a_Chunk)
{
m_SaveQueue.RemoveIf([=](cChunkCoordsWithCallback & a_Item)
{
return (a_Item.m_ChunkX == a_Chunk.m_ChunkX) && (a_Item.m_ChunkZ == a_Chunk.m_ChunkZ);
}
);
}
void cWorldStorage::InitSchemas(int a_StorageCompressionFactor) void cWorldStorage::InitSchemas(int a_StorageCompressionFactor)
{ {
// The first schema added is considered the default // The first schema added is considered the default
@ -266,7 +240,7 @@ bool cWorldStorage::LoadOneChunk(void)
// Call the callback, if specified: // Call the callback, if specified:
if (ToLoad.m_Callback != nullptr) if (ToLoad.m_Callback != nullptr)
{ {
ToLoad.m_Callback->Call(ToLoad.m_ChunkX, ToLoad.m_ChunkZ); ToLoad.m_Callback->Call(ToLoad.m_ChunkX, ToLoad.m_ChunkZ, res);
} }
return res; return res;
} }
@ -286,19 +260,21 @@ bool cWorldStorage::SaveOneChunk(void)
} }
// Save the chunk, if it's valid: // Save the chunk, if it's valid:
bool Status = false;
if (m_World->IsChunkValid(ToSave.m_ChunkX, ToSave.m_ChunkZ)) if (m_World->IsChunkValid(ToSave.m_ChunkX, ToSave.m_ChunkZ))
{ {
m_World->MarkChunkSaving(ToSave.m_ChunkX, ToSave.m_ChunkZ); m_World->MarkChunkSaving(ToSave.m_ChunkX, ToSave.m_ChunkZ);
if (m_SaveSchema->SaveChunk(cChunkCoords(ToSave.m_ChunkX, ToSave.m_ChunkZ))) if (m_SaveSchema->SaveChunk(cChunkCoords(ToSave.m_ChunkX, ToSave.m_ChunkZ)))
{ {
m_World->MarkChunkSaved(ToSave.m_ChunkX, ToSave.m_ChunkZ); m_World->MarkChunkSaved(ToSave.m_ChunkX, ToSave.m_ChunkZ);
Status = true;
} }
} }
// Call the callback, if specified: // Call the callback, if specified:
if (ToSave.m_Callback != nullptr) if (ToSave.m_Callback != nullptr)
{ {
ToSave.m_Callback->Call(ToSave.m_ChunkX, ToSave.m_ChunkZ); ToSave.m_Callback->Call(ToSave.m_ChunkX, ToSave.m_ChunkZ, Status);
} }
return true; return true;
} }

View File

@ -63,13 +63,15 @@ public:
cWorldStorage(void); cWorldStorage(void);
~cWorldStorage(); ~cWorldStorage();
/** Queues a chunk to be loaded, asynchronously.
The callback, if specified, will be called with the result of the load operation. */
void QueueLoadChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_Callback = nullptr); void QueueLoadChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_Callback = nullptr);
/** Queues a chunk to be saved, asynchronously.
The callback, if specified, will be called with the result of the save operation. */
void QueueSaveChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_Callback = nullptr); void QueueSaveChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_Callback = nullptr);
void UnqueueLoad(int a_ChunkX, int a_ChunkZ);
void UnqueueSave(const cChunkCoords & a_Chunk);
bool Start(cWorld * a_World, const AString & a_StorageSchemaName, int a_StorageCompressionFactor); // Hide the cIsThread's Start() method, we need to provide args bool Start(cWorld * a_World, const AString & a_StorageSchemaName, int a_StorageCompressionFactor); // Hide the cIsThread's Start() method, we need to provide args
void Stop(void); // Hide the cIsThread's Stop() method, we need to signal the event void Stop(void); // Hide the cIsThread's Stop() method, we need to signal the event
void WaitForFinish(void); void WaitForFinish(void);