초기커밋
This commit is contained in:
123
ServerCore/Sync/AtomicGuard.cs
Normal file
123
ServerCore/Sync/AtomicGuard.cs
Normal file
@@ -0,0 +1,123 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
|
||||
|
||||
using GAURD_KEY = System.String;
|
||||
|
||||
|
||||
namespace ServerCore;
|
||||
|
||||
//=============================================================================================
|
||||
// 병렬 프로그래밍 환경에서 특정 로직이 동시에 실행되지 않도록 보호하는 클래스 이다.
|
||||
//
|
||||
// author : kangms
|
||||
//
|
||||
//=============================================================================================
|
||||
|
||||
public class AtomicGuard : IDisposable
|
||||
{
|
||||
private string m_try_guard_key = string.Empty;
|
||||
private string m_using_guard_key = string.Empty;
|
||||
|
||||
private AtomicString? m_guard_key_nullable;
|
||||
private AtomicBool? m_atomic_bool_nullable;
|
||||
|
||||
public AtomicGuard(GAURD_KEY guardKey)
|
||||
{
|
||||
m_try_guard_key = guardKey;
|
||||
}
|
||||
|
||||
public AtomicGuard(AtomicString guardKey)
|
||||
{
|
||||
m_guard_key_nullable = guardKey;
|
||||
}
|
||||
|
||||
public AtomicGuard(AtomicString guardKey, AtomicBool atomicBool)
|
||||
{
|
||||
m_guard_key_nullable = guardKey;
|
||||
m_atomic_bool_nullable = atomicBool;
|
||||
}
|
||||
|
||||
public bool tryGuard()
|
||||
{
|
||||
if (null == m_guard_key_nullable)
|
||||
{
|
||||
var err_msg = $"m_guard_key_nullable is null !!! in tryGuard() - guardKey:{m_try_guard_key}";
|
||||
Log.getLogger().error(err_msg);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if(false == m_guard_key_nullable.compareAndSet(string.Empty, m_try_guard_key))
|
||||
{
|
||||
var err_msg = $"Failed to in compareAndSet() !!! in tryGuard() - guardKey:{m_try_guard_key}";
|
||||
Log.getLogger().info(err_msg);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (null == m_atomic_bool_nullable)
|
||||
{
|
||||
var err_msg = $"m_atomic_bool_nullable is null !!! in tryGuard() - guardKey:{m_try_guard_key}";
|
||||
Log.getLogger().error(err_msg);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (false == m_atomic_bool_nullable.set(true))
|
||||
{
|
||||
var err_msg = $"Failed to set true !!! in tryGuard() - guardKey:{m_try_guard_key}";
|
||||
Log.getLogger().info(err_msg);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
m_using_guard_key = m_try_guard_key;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (null == m_guard_key_nullable)
|
||||
{
|
||||
var err_msg = $"m_guard_key_nullable is null !!! in Dispose() - guardKey:{m_try_guard_key}";
|
||||
Log.getLogger().error(err_msg);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var guard_key = m_guard_key_nullable.Value;
|
||||
|
||||
if (false == m_guard_key_nullable.compareAndSet(m_using_guard_key, string.Empty))
|
||||
{
|
||||
var err_msg = $"Failed to compareAndSet() !!! in Dispose() : usingGuardKey:{m_using_guard_key} - guardKey:{guard_key}";
|
||||
Log.getLogger().info(err_msg);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (null == m_atomic_bool_nullable)
|
||||
{
|
||||
var err_msg = $"m_atomic_bool_nullable is null !!! in Dispose() - guardKey:{guard_key}";
|
||||
Log.getLogger().error(err_msg);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (false == m_atomic_bool_nullable.set(false))
|
||||
{
|
||||
var err_msg = $"Failed to set false in Dispose() !!! - guardKey:{guard_key}";
|
||||
Log.getLogger().error(err_msg);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public void bindAtomic(AtomicString guardKey, AtomicBool atomicBool)
|
||||
{
|
||||
m_guard_key_nullable = guardKey;
|
||||
m_atomic_bool_nullable = atomicBool;
|
||||
}
|
||||
}
|
||||
35
ServerCore/Sync/AutoLock.cs
Normal file
35
ServerCore/Sync/AutoLock.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
namespace ServerCore;
|
||||
|
||||
//=============================================================================================
|
||||
// 병렬 프로그래밍 환경에서 특정 로직이 동시에 실행되지 않도록 Monitor를 활용하여 보호하는 클래스 이다.
|
||||
//
|
||||
// author : kangms
|
||||
//
|
||||
//=============================================================================================
|
||||
|
||||
public class AutoLock : IDisposable
|
||||
{
|
||||
private readonly object m_lock;
|
||||
|
||||
public AutoLock(object tolockObject)
|
||||
{
|
||||
m_lock = tolockObject;
|
||||
|
||||
Monitor.Enter(m_lock);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Monitor.Exit(m_lock);
|
||||
}
|
||||
}
|
||||
54
ServerCore/Sync/Counter.cs
Normal file
54
ServerCore/Sync/Counter.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
||||
|
||||
namespace ServerCore;
|
||||
|
||||
public class Counter
|
||||
{
|
||||
private uint m_count = 0;
|
||||
|
||||
private readonly uint m_min;
|
||||
private readonly uint m_max;
|
||||
|
||||
public Counter(uint min = uint.MinValue, uint max = uint.MaxValue)
|
||||
{
|
||||
m_min = min;
|
||||
m_max = max;
|
||||
}
|
||||
|
||||
public uint incCount()
|
||||
{
|
||||
var value = Volatile.Read(ref m_count);
|
||||
if(value >= m_max)
|
||||
{
|
||||
m_count = m_max;
|
||||
return m_count;
|
||||
}
|
||||
|
||||
return Interlocked.Increment(ref m_count);
|
||||
}
|
||||
|
||||
public uint decCount()
|
||||
{
|
||||
var value = Volatile.Read(ref m_count);
|
||||
if (value <= m_min)
|
||||
{
|
||||
m_count = m_min;
|
||||
return m_count;
|
||||
}
|
||||
|
||||
return Interlocked.Decrement(ref m_count);
|
||||
}
|
||||
|
||||
public uint getCount()
|
||||
{
|
||||
return Volatile.Read(ref m_count);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
53
ServerCore/Sync/InstantIdGenerator.cs
Normal file
53
ServerCore/Sync/InstantIdGenerator.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
namespace ServerCore;
|
||||
|
||||
public class InstantIdGeneratorWith32Bit
|
||||
{
|
||||
private UInt32 m_index = 0;
|
||||
public InstantIdGeneratorWith32Bit()
|
||||
{
|
||||
}
|
||||
|
||||
public UInt32 nextId()
|
||||
{
|
||||
Interlocked.Increment(ref m_index);
|
||||
|
||||
if(m_index == UInt32.MaxValue)
|
||||
{
|
||||
throw new OverflowException("UInt32.MaxValue has been reached, at InstantIdGeneratorWith32Bit.nextId() !!!");
|
||||
}
|
||||
|
||||
return m_index;
|
||||
}
|
||||
|
||||
}//InstantIdGeneratorWith32Bit
|
||||
|
||||
public class InstantIdGeneratorWith64Bit
|
||||
{
|
||||
private UInt64 m_index = 0;
|
||||
public InstantIdGeneratorWith64Bit()
|
||||
{
|
||||
}
|
||||
|
||||
public UInt64 nextId()
|
||||
{
|
||||
Interlocked.Increment(ref m_index);
|
||||
|
||||
if (m_index >= UInt64.MaxValue)
|
||||
{
|
||||
throw new OverflowException("UInt64.MaxValue has been reached, at InstantIdGeneratorWith64Bit.nextId() !!!");
|
||||
}
|
||||
|
||||
return m_index;
|
||||
}
|
||||
|
||||
}//InstantIdGeneratorWith64Bit
|
||||
178
ServerCore/Sync/SyncState.cs
Normal file
178
ServerCore/Sync/SyncState.cs
Normal file
@@ -0,0 +1,178 @@
|
||||
namespace ServerCore;
|
||||
|
||||
//=============================================================================================
|
||||
// 병렬 프로그래밍 환경에서 상태값을 동기화할 수 있게 지원하는 제네릭 클래스 이다.
|
||||
//
|
||||
// author : kangms
|
||||
//
|
||||
//=============================================================================================
|
||||
public class SyncState<T> where T : notnull
|
||||
{
|
||||
private T m_state;
|
||||
private readonly object m_mutex = new object(); // mutex
|
||||
|
||||
public SyncState(T state)
|
||||
{
|
||||
m_state = state;
|
||||
}
|
||||
|
||||
public bool isState(T state)
|
||||
{
|
||||
lock (m_mutex)
|
||||
{
|
||||
return (m_state.Equals(state));
|
||||
}
|
||||
}
|
||||
|
||||
public T setState(T state)
|
||||
{
|
||||
lock (m_mutex)
|
||||
{
|
||||
T old_state = m_state;
|
||||
m_state = state;
|
||||
return old_state;
|
||||
}
|
||||
}
|
||||
|
||||
public T getState()
|
||||
{
|
||||
return m_state;
|
||||
}
|
||||
|
||||
// 같지 않으면 변경
|
||||
// exchange..(disconnected, out old_state)
|
||||
public bool exchangeNotEqual(T to_state, out T old_state)
|
||||
{
|
||||
lock (m_mutex)
|
||||
{
|
||||
if (false == m_state.Equals(to_state))
|
||||
{
|
||||
old_state = m_state;
|
||||
m_state = to_state;
|
||||
return true;
|
||||
}
|
||||
|
||||
old_state = m_state;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// 같지 않으면 변경, 예외인 경우는 변경안함
|
||||
// exchange...(disconnected, connecting, out old_state)
|
||||
public bool exchangeNotEqualExcept(T to_state, T except_state, out T old_state)
|
||||
{
|
||||
lock (m_mutex)
|
||||
{
|
||||
// except state
|
||||
if (true == m_state.Equals(except_state))
|
||||
{
|
||||
old_state = m_state;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (false == m_state.Equals(to_state))
|
||||
{
|
||||
old_state = m_state;
|
||||
m_state = to_state;
|
||||
return true;
|
||||
}
|
||||
|
||||
old_state = m_state;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public string? toString()
|
||||
{
|
||||
return Enum.GetName(typeof(T), m_state);
|
||||
}
|
||||
}
|
||||
|
||||
public class AtomicString
|
||||
{
|
||||
private string m_value;
|
||||
|
||||
public AtomicString(string value = "")
|
||||
{
|
||||
m_value = value;
|
||||
}
|
||||
|
||||
public string Value
|
||||
{
|
||||
get { return m_value; }
|
||||
}
|
||||
|
||||
public void set(string newValue)
|
||||
{
|
||||
Interlocked.Exchange(ref m_value, newValue);
|
||||
}
|
||||
|
||||
public bool compareAndSet(string expectedValue, string newValue)
|
||||
{
|
||||
return Interlocked.CompareExchange(ref m_value, newValue, expectedValue) == expectedValue;
|
||||
}
|
||||
}
|
||||
|
||||
public class AtomicInt32
|
||||
{
|
||||
private Int32 m_value;
|
||||
|
||||
public AtomicInt32(Int32 value = 0)
|
||||
{
|
||||
m_value = value;
|
||||
}
|
||||
|
||||
public int Value
|
||||
{
|
||||
get { return m_value; }
|
||||
}
|
||||
|
||||
public void set(int newValue)
|
||||
{
|
||||
Interlocked.Exchange(ref m_value, newValue);
|
||||
}
|
||||
|
||||
public int increment()
|
||||
{
|
||||
return Interlocked.Increment(ref m_value);
|
||||
}
|
||||
|
||||
public int decrement()
|
||||
{
|
||||
return Interlocked.Decrement(ref m_value);
|
||||
}
|
||||
|
||||
public int add(int value)
|
||||
{
|
||||
return Interlocked.Add(ref m_value, value);
|
||||
}
|
||||
|
||||
public bool compareAndSet(int expectedValue, int newValue)
|
||||
{
|
||||
return Interlocked.CompareExchange(ref m_value, newValue, expectedValue) == expectedValue;
|
||||
}
|
||||
}
|
||||
|
||||
public class AtomicBool
|
||||
{
|
||||
private Int32 m_value = 0;
|
||||
|
||||
public AtomicBool(bool value)
|
||||
{
|
||||
m_value = value ? 1 : 0;
|
||||
}
|
||||
|
||||
public bool Value
|
||||
{
|
||||
get { return m_value == 1; }
|
||||
}
|
||||
|
||||
public bool set(bool value)
|
||||
{
|
||||
var comparand = value ? 0 : 1;
|
||||
var in_value = value ? 1 : 0;
|
||||
|
||||
var result = Interlocked.CompareExchange(ref m_value, in_value, comparand);
|
||||
|
||||
return comparand == result;
|
||||
}
|
||||
}
|
||||
434
ServerCore/Sync/Synchronizer.cs
Normal file
434
ServerCore/Sync/Synchronizer.cs
Normal file
@@ -0,0 +1,434 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
35
ServerCore/Sync/SynchronizerHelper.cs
Normal file
35
ServerCore/Sync/SynchronizerHelper.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
||||
|
||||
namespace ServerCore;
|
||||
|
||||
public static class SynchronizerHelper
|
||||
{
|
||||
public static async Task<IDisposable> tryReadLockAsync(this ReaderWriterLockSlim rwLock)
|
||||
{
|
||||
ArgumentNullReferenceCheckHelper.throwIfNull(rwLock, () => $"rwLock is null !!!");
|
||||
|
||||
return await Task.Run(() =>
|
||||
{
|
||||
rwLock.EnterReadLock();
|
||||
return (IDisposable) new Unlocker(rwLock, false);
|
||||
});
|
||||
}
|
||||
|
||||
public static async Task<IDisposable> tryWriteLockAsync(this ReaderWriterLockSlim rwLock)
|
||||
{
|
||||
ArgumentNullReferenceCheckHelper.throwIfNull(rwLock, () => $"rwLock is null !!!");
|
||||
|
||||
return await Task.Run(() =>
|
||||
{
|
||||
rwLock.EnterWriteLock();
|
||||
return (IDisposable) new Unlocker(rwLock, true);
|
||||
});
|
||||
}
|
||||
}
|
||||
40
ServerCore/Sync/TimeSyncHelper.cs
Normal file
40
ServerCore/Sync/TimeSyncHelper.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
||||
|
||||
namespace ServerCore;
|
||||
|
||||
//=============================================================================================
|
||||
// 서버로 부터 수신한 시간을 기준 시간으로 설정시켜
|
||||
// 서버 시간을 기준으로 동기화되어 관리될 수 있는 기능을 지원하는 클래스 이다.
|
||||
//
|
||||
// author : kangms
|
||||
//
|
||||
//=============================================================================================
|
||||
public class TimeSyncHelper
|
||||
{
|
||||
private readonly string m_server_type = string.Empty;
|
||||
private DateTime m_server_time = ConstDateTimeValue.MinTime;
|
||||
private System.Diagnostics.Stopwatch m_stop_watch = new System.Diagnostics.Stopwatch();
|
||||
public TimeSyncHelper(string serverType)
|
||||
{
|
||||
m_server_type = serverType;
|
||||
}
|
||||
public void setupFromTimeServer(DateTime serverTime, TimeSpan ping)
|
||||
{
|
||||
m_server_time = serverTime.toUtcTime().AddMilliseconds(ping.TotalMilliseconds / 2);
|
||||
m_stop_watch = m_stop_watch ?? new System.Diagnostics.Stopwatch();
|
||||
m_stop_watch.Reset();
|
||||
m_stop_watch.Start();
|
||||
}
|
||||
|
||||
public DateTime getCurrentByTimeServer()
|
||||
{
|
||||
return m_server_time + m_stop_watch.Elapsed;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user