Skip to content

Commit

Permalink
fix: Fixes serialization threading, moves world save to end of loop, …
Browse files Browse the repository at this point in the history
…and eliminates Parallel.ForEach. (#1530)
  • Loading branch information
kamronbatman authored Oct 3, 2023
1 parent e18115d commit 1f0acdd
Show file tree
Hide file tree
Showing 12 changed files with 228 additions and 58 deletions.
4 changes: 2 additions & 2 deletions Projects/Server/Guild.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ protected BaseGuild()
[CommandProperty(AccessLevel.GameMaster, readOnly: true)]
public DateTime Created { get; set; } = Core.Now;

long ISerializable.SavePosition { get; set; } = -1;
public long SavePosition { get; set; } = -1;

BufferWriter ISerializable.SaveBuffer { get; set; }
public BufferWriter SaveBuffer { get; set; }

public int TypeRef { get; private set; }

Expand Down
6 changes: 3 additions & 3 deletions Projects/Server/IEntity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@ public Entity(Serial serial, Point3D loc, Map map) : this(serial)

public Entity(Serial serial) => Serial = serial;

DateTime ISerializable.Created { get; set; } = Core.Now;
public DateTime Created { get; set; } = Core.Now;

long ISerializable.SavePosition { get; set; } = -1;
public long SavePosition { get; set; } = -1;

BufferWriter ISerializable.SaveBuffer { get; set; }
public BufferWriter SaveBuffer { get; set; }

public int TypeRef => -1;

Expand Down
4 changes: 2 additions & 2 deletions Projects/Server/Items/Item.cs
Original file line number Diff line number Diff line change
Expand Up @@ -760,9 +760,9 @@ public virtual void GetProperties(IPropertyList list)
[CommandProperty(AccessLevel.GameMaster, readOnly: true)]
public DateTime Created { get; set; } = Core.Now;

long ISerializable.SavePosition { get; set; } = -1;
public long SavePosition { get; set; } = -1;

BufferWriter ISerializable.SaveBuffer { get; set; }
public BufferWriter SaveBuffer { get; set; }

[CommandProperty(AccessLevel.Counselor)]
public Serial Serial { get; }
Expand Down
13 changes: 13 additions & 0 deletions Projects/Server/Main.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public static class Core
{
private static readonly ILogger logger = LogFactory.GetLogger(typeof(Core));

private static bool _performSnapshot;
private static bool _crashed;
private static string _baseDirectory;

Expand Down Expand Up @@ -556,6 +557,12 @@ public static void RunEventLoop()

Timer.CheckTimerPool(); // Check for pool depletion so we can async refill it.

if (_performSnapshot)
{
// Return value is the offset that can be used to fix timers that should drift
World.Snapshot();
}

_tickCount = 0;
_now = DateTime.MinValue;

Expand All @@ -578,8 +585,14 @@ public static void RunEventLoop()
{
CurrentDomain_UnhandledException(null, new UnhandledExceptionEventArgs(e, true));
}
finally
{
World.SleepSerializationThreads();
}
}

internal static void RequestSnapshot() => _performSnapshot = true;

public static void VerifySerialization()
{
_itemCount = 0;
Expand Down
4 changes: 2 additions & 2 deletions Projects/Server/Mobiles/Mobile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2258,9 +2258,9 @@ public virtual void GetProperties(IPropertyList list)
[CommandProperty(AccessLevel.GameMaster, readOnly: true)]
public DateTime Created { get; set; } = Core.Now;

long ISerializable.SavePosition { get; set; } = -1;
public long SavePosition { get; set; } = -1;

BufferWriter ISerializable.SaveBuffer { get; set; }
public BufferWriter SaveBuffer { get; set; }

[CommandProperty(AccessLevel.Counselor)]
public Serial Serial { get; }
Expand Down
2 changes: 1 addition & 1 deletion Projects/Server/Network/PingServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public static void Start()

public static void Slice()
{
if (!Enabled || Core.Closing)
if (!Enabled)
{
return;
}
Expand Down
15 changes: 7 additions & 8 deletions Projects/Server/Serialization/GenericEntityPersistence.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Server.Logging;

namespace Server;
Expand Down Expand Up @@ -55,13 +54,10 @@ public GenericEntityPersistence(string name, int priority, uint minSerial, uint

public override void Serialize()
{
// TODO: Hand off to a scheduler instead
Parallel.ForEach(EntitiesBySerial.Values, SerializeEntity);
}

protected virtual void SerializeEntity(T entity)
{
entity.Serialize(World.SerializedTypes);
foreach (var entity in EntitiesBySerial.Values)
{
World.PushToCache(entity);
}
}

public override void WriteSnapshot(string basePath)
Expand Down Expand Up @@ -160,6 +156,7 @@ public void AddEntity(T entity)
_pendingAdd[entity.Serial] = entity;
break;
}
case WorldState.PendingSave:
case WorldState.Running:
{
ref var entityEntry = ref CollectionsMarshal.GetValueRefOrAddDefault(EntitiesBySerial, entity.Serial, out bool exists);
Expand Down Expand Up @@ -216,6 +213,7 @@ public void RemoveEntity(T entity)
_pendingDelete[entity.Serial] = entity;
break;
}
case WorldState.PendingSave:
case WorldState.Running:
{
EntitiesBySerial.Remove(entity.Serial);
Expand Down Expand Up @@ -299,6 +297,7 @@ public R FindEntity<R>(Serial serial, bool returnDeleted, bool returnPending) wh

return null;
}
case WorldState.PendingSave:
case WorldState.Running:
{
return EntitiesBySerial.TryGetValue(serial, out var entity) ? entity as R : null;
Expand Down
22 changes: 15 additions & 7 deletions Projects/Server/Serialization/GenericPersistence.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,33 +14,41 @@
*************************************************************************/

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;

namespace Server;

public abstract class GenericPersistence : Persistence
public abstract class GenericPersistence : Persistence, IGenericSerializable
{
private BufferWriter _saveBuffer;

public string Name { get; }

public GenericPersistence(string name, int priority) : base(priority) => Name = name;

public override void Serialize()
{
_saveBuffer ??= new BufferWriter(true, World.SerializedTypes);
_saveBuffer.Seek(0, SeekOrigin.Begin);
World.PushToCache(this);
}

public long SavePosition { get; set; }

public BufferWriter SaveBuffer { get; set; }

public void Serialize(ConcurrentQueue<Type> types)
{
SaveBuffer ??= new BufferWriter(true, types);

Serialize(_saveBuffer);
SaveBuffer.Seek(0, SeekOrigin.Begin);
Serialize(SaveBuffer);
}

public abstract void Serialize(IGenericWriter writer);

public override void WriteSnapshot(string basePath)
{
string binPath = Path.Combine(basePath, Name, $"{Name}.bin");
var buffer = _saveBuffer!.Buffer.AsSpan(0, (int)_saveBuffer.Position);
var buffer = SaveBuffer!.Buffer.AsSpan(0, (int)SaveBuffer.Position);
AdhocPersistence.WriteSnapshot(new FileInfo(binPath), buffer);
}

Expand Down
4 changes: 2 additions & 2 deletions Projects/Server/Serialization/ISerializable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

namespace Server;

public interface ISerializable
public interface ISerializable : IGenericSerializable
{
// Should be serialized/deserialized with the index so it can be referenced by IGenericReader
DateTime Created { get; set; }
Expand Down Expand Up @@ -48,7 +48,7 @@ public void InitializeSaveBuffer(byte[] buffer, ConcurrentQueue<Type> types)
}
}

public void Serialize(ConcurrentQueue<Type> types)
void IGenericSerializable.Serialize(ConcurrentQueue<Type> types)
{
SaveBuffer ??= new BufferWriter(true, types);

Expand Down
6 changes: 4 additions & 2 deletions Projects/Server/Serialization/Persistence.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,10 @@ private static Dictionary<ulong, string> LoadTypes(string path)

internal static void SerializeAll()
{
// TODO: Hand off to a scheduler
Parallel.ForEach(_registry, p => p.Serialize());
foreach (var p in _registry)
{
p.Serialize();
}
}

internal static void PostSerializeAll()
Expand Down
24 changes: 24 additions & 0 deletions Projects/Server/World/IGenericSerializable.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2023 - ModernUO Development Team *
* Email: [email protected] *
* File: IWorldSerializable.cs *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*************************************************************************/

using System;
using System.Collections.Concurrent;

namespace Server;

public interface IGenericSerializable
{
void Serialize(ConcurrentQueue<Type> types);
}
Loading

0 comments on commit 1f0acdd

Please sign in to comment.