Files
caliverse_server/ServerCore/Sync/Synchronizer.cs
2025-05-01 07:20:41 +09:00

435 lines
14 KiB
C#

using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Google.Protobuf.WellKnownTypes;
namespace ServerCore;
//=============================================================================================
// 병렬 프로그래밍 환경에서 특정 객체의 Read & Write 처리를 ReaderWriterLockSlim 활용하여
// 동기화해주는 클래스 이다.
//
// author : kangms
//
//=============================================================================================
public class Synchronizer<TObject>
where TObject : class
{
private readonly ReaderWriterLockSlim m_rw_lock;
private readonly TObject m_value;
public Synchronizer(TObject value)
{
ArgumentNullReferenceCheckHelper.throwIfNull(value, () => $"value is null !!!");
m_value = value;
var dynamic_obj = (value as dynamic);
ArgumentNullException.ThrowIfNull(dynamic_obj, "dynamic_obj is null !!!");
m_rw_lock = dynamic_obj.getRWLock();
}
//=============================================================================================
// 호출자의 읽기를 위한 로직을 동기화 해준다.
// ReaderWriterLockSlim의 EnterReadLock() 호출하여 동기화 해준다.
//=============================================================================================
public TResult callReadFunc<TResult>(Func<TObject, TResult> readFunc)
{
ArgumentNullReferenceCheckHelper.throwIfNull(readFunc, () => $"readFunc is null !!!");
m_rw_lock.EnterReadLock();
try
{
return readFunc(m_value);
}
finally
{
m_rw_lock.ExitReadLock();
}
}
//=============================================================================================
// 호출자의 쓰기를 위한 로직을 동기화 해준다.
// ReaderWriterLockSlim의 EnterWriteLock() 호출하여 동기화 해준다.
//=============================================================================================
public TResult callWriteFunc<TResult>(Func<TObject, TResult> writeFunc)
{
ArgumentNullReferenceCheckHelper.throwIfNull(writeFunc, () => $"writeFunc is null !!!");
m_rw_lock.EnterWriteLock();
try
{
return writeFunc(m_value);
}
finally
{
m_rw_lock.ExitWriteLock();
}
}
public IDisposable tryReadLock()
{
m_rw_lock.EnterReadLock();
return new Unlocker(m_rw_lock, isReadLock: true);
}
public IDisposable tryWriteLock()
{
m_rw_lock.EnterWriteLock();
return new Unlocker(m_rw_lock, isReadLock: false);
}
}
//=============================================================================================
// Synchronizer를 복수개로 처리할 때 사용하는 클래스 이다.
//
// author : kangms
//
//=============================================================================================
public class MultipleSynchronizer<TObject>
where TObject : class
{
private readonly ConcurrentDictionary<string, Synchronizer<TObject>> m_synchronizers = new();
public MultipleSynchronizer(Dictionary<string, TObject> toSyncObjects)
{
foreach (var each in toSyncObjects)
{
var synchronizer = new Synchronizer<TObject>(each.Value);
m_synchronizers[each.Key] = synchronizer;
}
}
//=============================================================================================
// 등록된 객체중에서 locks 파라메터에 의해 선택적으로 Read & Write Lock을 설정해 준다.
//=============================================================================================
public MultiLock tryLockAll(IEnumerable<(string Key, bool IsReadLock)> locks)
{
return new MultiLock(m_synchronizers, locks);
}
//=============================================================================================
// 등록된 모든 객체를 Read Lock 설정을 해준다.
//=============================================================================================
public MultiReadLock tryReadLockAll()
{
return new MultiReadLock(m_synchronizers);
}
//=============================================================================================
// 등록된 모든 객체를 Write Lock 설정을 해준다.
//=============================================================================================
public MultiWriteLock tryWriteLockAll()
{
return new MultiWriteLock(m_synchronizers);
}
public Synchronizer<TObject> this[string index] => m_synchronizers[index];
public class MultiLock : IDisposable
{
private readonly List<IDisposable> m_lock_objects;
public MultiLock(ConcurrentDictionary<string, Synchronizer<TObject>> syncObjects, IEnumerable<(string Key, bool IsReadLock)> locks)
{
m_lock_objects = new List<IDisposable>();
foreach (var (key, isReadLock) in locks)
{
if (true == syncObjects.TryGetValue(key, out var found_object))
{
m_lock_objects.Add(isReadLock ? found_object.tryReadLock() : found_object.tryWriteLock());
}
}
}
public void Dispose()
{
foreach (var lock_obj in m_lock_objects)
{
lock_obj.Dispose();
}
}
}
public class MultiReadLock : IDisposable
{
private readonly List<IDisposable> m_lock_objects;
public MultiReadLock(IEnumerable<KeyValuePair<string, Synchronizer<TObject>>> syncObjects)
{
m_lock_objects = new List<IDisposable>();
foreach (var each in syncObjects)
{
m_lock_objects.Add(each.Value.tryReadLock());
}
}
public void Dispose()
{
foreach (var lock_obj in m_lock_objects)
{
lock_obj.Dispose();
}
}
}
public class MultiWriteLock : IDisposable
{
private readonly List<IDisposable> m_lock_objects;
public MultiWriteLock(IEnumerable<KeyValuePair<string, Synchronizer<TObject>>> syncObjects)
{
m_lock_objects = new List<IDisposable>();
foreach (var each in syncObjects)
{
m_lock_objects.Add(each.Value.tryWriteLock());
}
}
public void Dispose()
{
foreach (var lock_obj in m_lock_objects)
{
lock_obj.Dispose();
}
}
}
}
//=============================================================================================
// 병렬 프로그래밍 환경에서 특정 객체의 Read & Write 처리를 ReaderWriterLockSlim 활용하여
// async 기반으로 동기화해주는 클래스 이다.
//
// author : kangms
//
//=============================================================================================
public class SynchronizerAsync<TObject>
where TObject : class
{
private readonly ReaderWriterLockSlim m_rw_lock;
private readonly TObject m_value;
public SynchronizerAsync(TObject value)
{
m_value = value;
m_rw_lock = (value as dynamic).getRWLock();
}
//=============================================================================================
// 호출자의 읽기를 위한 로직을 async 기반으로 동기화 해준다.
// ReaderWriterLockSlim의 tryReadLockAsync() 호출하여 동기화 해준다.
//=============================================================================================
public async Task<TResult> callReadFuncAsync<TResult>(Func<TObject, Task<TResult>> readFunc)
{
ArgumentNullReferenceCheckHelper.throwIfNull(readFunc, () => $"readFunc is null !!!");
await m_rw_lock.tryReadLockAsync();
try
{
return await readFunc(m_value);
}
finally
{
m_rw_lock.ExitReadLock();
}
}
//=============================================================================================
// 호출자의 쓰기를 위한 로직을 async 기반으로 동기화 해준다.
// ReaderWriterLockSlim의 tryWriteLockAsync() 호출하여 동기화 해준다.
//=============================================================================================
public async Task<TResult> callWriteFuncAsync<TResult>(Func<TObject, Task<TResult>> writeFunc)
{
ArgumentNullReferenceCheckHelper.throwIfNull(writeFunc, () => $"writeFunc is null !!!");
await m_rw_lock.tryWriteLockAsync();
try
{
return await writeFunc(m_value);
}
finally
{
m_rw_lock.ExitWriteLock();
}
}
public async Task<IDisposable> tryReadLockAsync()
{
return await m_rw_lock.tryReadLockAsync();
}
public async Task<IDisposable> tryWriteLockAsync()
{
return await m_rw_lock.tryWriteLockAsync();
}
}
//=============================================================================================
// SynchronizerAsync를 복수개로 처리할 때 사용하는 클래스 이다.
//
// author : kangms
//
//=============================================================================================
public class MultipleSynchronizerAsync<TObject>
where TObject : class
{
private readonly ConcurrentDictionary<string, SynchronizerAsync<TObject>> m_synchronizers = new();
public MultipleSynchronizerAsync(Dictionary<string, TObject> toSyncObjects)
{
foreach (var each in toSyncObjects)
{
var synchronizer = new SynchronizerAsync<TObject>(each.Value);
m_synchronizers[each.Key] = synchronizer;
}
}
//=============================================================================================
// 등록된 객체중에서 locks 파라메터에 의해 선택적으로 Read & Write Lock을 설정해 준다.
//=============================================================================================
public MultiLockAsync tryLockAllAsync(IEnumerable<(string Key, bool IsReadLock)> locks)
{
return new MultiLockAsync(m_synchronizers, locks);
}
//=============================================================================================
// 등록된 모든 객체를 Read Lock 설정을 해준다.
//=============================================================================================
public MultiReadLockAsync tryReadLockAllAsync()
{
return new MultiReadLockAsync(m_synchronizers);
}
//=============================================================================================
// 등록된 모든 객체를 Write Lock 설정을 해준다.
//=============================================================================================
public MultiWriteLockAsync tryWriteLockAllAsync()
{
return new MultiWriteLockAsync(m_synchronizers);
}
public SynchronizerAsync<TObject> this[string index] => m_synchronizers[index];
public class MultiLockAsync : IDisposable
{
private readonly List<IDisposable> m_lock_objects;
public MultiLockAsync(ConcurrentDictionary<string, SynchronizerAsync<TObject>> syncObjects, IEnumerable<(string Key, bool IsReadLock)> locks)
{
m_lock_objects = new List<IDisposable>();
foreach (var (key, isReadLock) in locks)
{
if (true == syncObjects.TryGetValue(key, out var found_object))
{
m_lock_objects.Add(isReadLock ? found_object.tryReadLockAsync() : found_object.tryWriteLockAsync());
}
}
}
public void Dispose()
{
foreach (var lock_obj in m_lock_objects)
{
lock_obj.Dispose();
}
}
}
public class MultiReadLockAsync : IDisposable
{
private readonly List<IDisposable> m_lock_objects;
public MultiReadLockAsync(IEnumerable<KeyValuePair<string, SynchronizerAsync<TObject>>> syncObjects)
{
m_lock_objects = new List<IDisposable>();
foreach (var each in syncObjects)
{
m_lock_objects.Add(each.Value.tryReadLockAsync());
}
}
public void Dispose()
{
foreach (var lock_obj in m_lock_objects)
{
lock_obj.Dispose();
}
}
}
public class MultiWriteLockAsync : IDisposable
{
private readonly List<IDisposable> m_lock_objects;
public MultiWriteLockAsync(IEnumerable<KeyValuePair<string, SynchronizerAsync<TObject>>> syncObjects)
{
m_lock_objects = new List<IDisposable>();
foreach (var each in syncObjects)
{
m_lock_objects.Add(each.Value.tryWriteLockAsync());
}
}
public void Dispose()
{
foreach (var lock_obj in m_lock_objects)
{
lock_obj.Dispose();
}
}
}
}
//=============================================================================================
// Unlocker가 소멸될 때 ReaderWriterLockSlim가 Exit 될 수 있도록 해주는 클래스 이다.
//=============================================================================================
public class Unlocker : IDisposable
{
private readonly ReaderWriterLockSlim m_ref_rw_lock;
private readonly bool m_is_read_lock;
public Unlocker(ReaderWriterLockSlim readWriteLockObject, bool isReadLock)
{
m_ref_rw_lock = readWriteLockObject;
m_is_read_lock = isReadLock;
}
public void Dispose()
{
if (m_is_read_lock)
{
m_ref_rw_lock.ExitReadLock();
}
else
{
m_ref_rw_lock.ExitWriteLock();
}
}
}