mirror of
https://github.com/snltty/linker.git
synced 2025-10-28 03:13:15 +08:00
616 lines
22 KiB
C#
616 lines
22 KiB
C#
using System;
|
||
using System.Collections.Concurrent;
|
||
using System.Collections.Generic;
|
||
using System.Diagnostics;
|
||
using System.Linq;
|
||
using System.Runtime.InteropServices;
|
||
using System.Text;
|
||
using System.Threading;
|
||
using System.Threading.Tasks;
|
||
|
||
namespace cmonitor.share.lib
|
||
{
|
||
/// <summary>
|
||
/// InitLocal 和 InitGlobal 都可以初始化,都初始化时,需要启动 StartLoop,将InitGlobal同步数据到Local
|
||
/// AddAttributeAction 设置状态变化回调,需要启动 Loop,监听数据变化
|
||
/// </summary>
|
||
public sealed class ShareMemory
|
||
{
|
||
private const int shareMemoryAttributeIndex = 0;
|
||
private const int shareMemoryAttributeSize = 1;
|
||
private const int shareMemoryVersionSize = 8;
|
||
private const int shareMemoryVersionIndex = 1;
|
||
private const int shareMemoryHeadSize = shareMemoryAttributeSize + shareMemoryVersionSize;
|
||
|
||
private string key;
|
||
private int length;
|
||
private int itemSize;
|
||
private byte[] bytes;
|
||
private object lockObj = new object();
|
||
private long mainVersion = 0;
|
||
IShareMemory accessorLocal = null;
|
||
IShareMemory accessorGlobal = null;
|
||
|
||
private readonly ShareMemoryAttribute[] itemAttributes = Array.Empty<ShareMemoryAttribute>();
|
||
private readonly long[] itemVersions = Array.Empty<long>();
|
||
ConcurrentDictionary<int, List<Action<ShareMemoryAttribute>>> attributeActions = new ConcurrentDictionary<int, List<Action<ShareMemoryAttribute>>>();
|
||
private CancellationTokenSource cancellationTokenSource;
|
||
private ConcurrentQueue<ShareItemAttributeChanged> attributeChangeds = new ConcurrentQueue<ShareItemAttributeChanged>();
|
||
|
||
private readonly Dictionary<string, ShareItemInfo> dic = new Dictionary<string, ShareItemInfo>();
|
||
|
||
public ShareMemory(string key, int length, int itemSize)
|
||
{
|
||
this.key = key;
|
||
this.length = length;
|
||
this.itemSize = itemSize;
|
||
bytes = new byte[length * itemSize];
|
||
itemAttributes = new ShareMemoryAttribute[length];
|
||
itemVersions = new long[length];
|
||
}
|
||
|
||
public void InitLocal()
|
||
{
|
||
try
|
||
{
|
||
if (accessorLocal == null)
|
||
{
|
||
accessorLocal = ShareMemoryFactory.Create(key, length, itemSize);
|
||
|
||
if (accessorLocal.Init() == false)
|
||
{
|
||
accessorLocal = null;
|
||
}
|
||
}
|
||
}
|
||
catch (Exception)
|
||
{
|
||
}
|
||
}
|
||
|
||
private byte[] gloablBytes;
|
||
public void InitGlobal()
|
||
{
|
||
try
|
||
{
|
||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && accessorGlobal == null)
|
||
{
|
||
gloablBytes = new byte[bytes.Length];
|
||
accessorGlobal = ShareMemoryFactory.Create($"Global\\{key}", length, itemSize);
|
||
if (accessorGlobal.Init() == false)
|
||
{
|
||
accessorGlobal = null;
|
||
}
|
||
}
|
||
}
|
||
catch (Exception)
|
||
{
|
||
}
|
||
}
|
||
public void AddAttributeAction(int index, Action<ShareMemoryAttribute> action)
|
||
{
|
||
if (attributeActions.TryGetValue(index, out List<Action<ShareMemoryAttribute>> actions) == false)
|
||
{
|
||
actions = new List<Action<ShareMemoryAttribute>>();
|
||
attributeActions.TryAdd(index, actions);
|
||
}
|
||
if (actions.Any(c => c == action) == false)
|
||
{
|
||
actions.Add(action);
|
||
}
|
||
}
|
||
public void RemoveAttributeAction(int index, Action<ShareMemoryAttribute> action)
|
||
{
|
||
if (attributeActions.TryGetValue(index, out List<Action<ShareMemoryAttribute>> actions) == false)
|
||
{
|
||
return;
|
||
}
|
||
actions.Remove(action);
|
||
}
|
||
|
||
|
||
public void StartLoop()
|
||
{
|
||
cancellationTokenSource?.Cancel();
|
||
cancellationTokenSource = new CancellationTokenSource();
|
||
Task.Factory.StartNew(async () =>
|
||
{
|
||
while (cancellationTokenSource.IsCancellationRequested == false)
|
||
{
|
||
try
|
||
{
|
||
SyncMemory();
|
||
AttributeCallback();
|
||
ReadItems();
|
||
}
|
||
catch (Exception)
|
||
{
|
||
}
|
||
|
||
await Task.Delay(10);
|
||
}
|
||
|
||
}, TaskCreationOptions.LongRunning);
|
||
|
||
Task.Factory.StartNew(async () =>
|
||
{
|
||
while (cancellationTokenSource.IsCancellationRequested == false)
|
||
{
|
||
try
|
||
{
|
||
while (attributeChangeds.TryDequeue(out ShareItemAttributeChanged result))
|
||
{
|
||
result.Action(result.Attribute);
|
||
}
|
||
}
|
||
catch (Exception)
|
||
{
|
||
}
|
||
|
||
await Task.Delay(30);
|
||
}
|
||
|
||
}, TaskCreationOptions.LongRunning);
|
||
}
|
||
|
||
private void AttributeCallback()
|
||
{
|
||
IShareMemory accessor = accessorLocal ?? accessorGlobal;
|
||
if (accessor == null) return;
|
||
|
||
bool allUpdated = false;
|
||
for (int index = 0; index < length; index++)
|
||
{
|
||
ShareMemoryAttribute attribute = ReadAttribute(accessorLocal, index);
|
||
bool updated = ReadVersionUpdated(accessorLocal, index);
|
||
allUpdated |= updated;
|
||
if (updated)
|
||
{
|
||
itemAttributes[index] &= (~ShareMemoryAttribute.Updated);
|
||
attribute |= ShareMemoryAttribute.Updated;
|
||
}
|
||
if (attribute != itemAttributes[index])
|
||
{
|
||
itemAttributes[index] = attribute;
|
||
if (attributeActions.TryGetValue(index, out List<Action<ShareMemoryAttribute>> actions) && actions.Count > 0)
|
||
{
|
||
foreach (var action in actions)
|
||
{
|
||
attributeChangeds.Enqueue(new ShareItemAttributeChanged { Action = action, Attribute = attribute });
|
||
}
|
||
}
|
||
}
|
||
|
||
}
|
||
if (allUpdated)
|
||
{
|
||
mainVersion++;
|
||
}
|
||
}
|
||
private void SyncMemory()
|
||
{
|
||
if (accessorGlobal != null && accessorLocal != null)
|
||
{
|
||
lock (lockObj)
|
||
{
|
||
accessorGlobal.ReadArray(0, gloablBytes, 0, gloablBytes.Length);
|
||
accessorLocal.WriteArray(0, gloablBytes, 0, itemSize);
|
||
for (int index = 0; index < length; index++)
|
||
{
|
||
//检查更新状态
|
||
if (ReadVersionUpdated(accessorGlobal, index) == false)
|
||
{
|
||
continue;
|
||
}
|
||
|
||
int _index = index * itemSize;
|
||
int keyLen = BitConverter.ToInt32(gloablBytes, _index + shareMemoryHeadSize);
|
||
if (keyLen > 0)
|
||
{
|
||
accessorLocal.WriteArray(_index, gloablBytes, _index, itemSize);
|
||
}
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
}
|
||
private void ReadItems()
|
||
{
|
||
IShareMemory accessor = accessorLocal ?? accessorGlobal;
|
||
if (accessor == null) return;
|
||
try
|
||
{
|
||
lock (lockObj)
|
||
{
|
||
accessor.ReadArray(0, bytes, 0, bytes.Length);
|
||
|
||
for (int index = 0; index < length; index++)
|
||
{
|
||
ShareMemoryAttribute attribute = ReadAttribute(accessor, index);
|
||
long itemVersion = ReadVersion(accessor, index);
|
||
bool skip = (itemVersion <= itemVersions[index] || (attribute & ShareMemoryAttribute.HiddenForList) == ShareMemoryAttribute.HiddenForList);
|
||
itemVersions[index] = itemVersion;
|
||
if (skip)
|
||
{
|
||
continue;
|
||
}
|
||
|
||
//key length
|
||
int _index = index * itemSize + shareMemoryHeadSize;
|
||
int keyLen = BitConverter.ToInt32(bytes, _index);
|
||
_index += 4;
|
||
if (keyLen > 0 && keyLen + 8 + shareMemoryHeadSize < itemSize)
|
||
{
|
||
//key
|
||
string key = Encoding.UTF8.GetString(bytes, _index, keyLen);
|
||
_index += keyLen;
|
||
|
||
//val length
|
||
string val = string.Empty;
|
||
int valLen = BitConverter.ToInt32(bytes, _index);
|
||
_index += 4;
|
||
//value
|
||
if (keyLen + 8 + shareMemoryHeadSize + valLen <= itemSize)
|
||
{
|
||
val = Encoding.UTF8.GetString(bytes, _index, valLen);
|
||
}
|
||
|
||
dic[key] = new ShareItemInfo
|
||
{
|
||
Index = index,
|
||
Value = val
|
||
};
|
||
}
|
||
}
|
||
}
|
||
|
||
}
|
||
catch (Exception)
|
||
{
|
||
}
|
||
}
|
||
|
||
public Dictionary<string, ShareItemInfo> ReadItems(out long version)
|
||
{
|
||
version = mainVersion;
|
||
return dic;
|
||
}
|
||
public string ReadValueString(int index)
|
||
{
|
||
byte[] bytes = ReadValueArray(index);
|
||
if (bytes.Length == 0) return string.Empty;
|
||
|
||
return Encoding.UTF8.GetString(bytes);
|
||
}
|
||
public long ReadValueInt64(int index)
|
||
{
|
||
IShareMemory accessor = accessorLocal ?? accessorGlobal;
|
||
if (accessor == null || index >= length) return 0;
|
||
index = index * itemSize + shareMemoryHeadSize;
|
||
|
||
int keylen = accessor.ReadInt(index);
|
||
index += 4 + keylen;
|
||
if (keylen == 0) return 0;
|
||
|
||
int vallen = accessor.ReadInt(index);
|
||
index += 4;
|
||
if (vallen == 0 || keylen + 8 + shareMemoryHeadSize + vallen > itemSize) return 0;
|
||
|
||
return accessor.ReadInt64(index);
|
||
}
|
||
public byte[] ReadValueArray(int index)
|
||
{
|
||
IShareMemory accessor = accessorLocal ?? accessorGlobal;
|
||
if (accessor == null || index >= length) return Array.Empty<byte>();
|
||
index = index * itemSize + shareMemoryHeadSize;
|
||
|
||
int keylen = accessor.ReadInt(index);
|
||
index += 4 + keylen;
|
||
|
||
int vallen = accessor.ReadInt(index);
|
||
index += 4;
|
||
if (vallen == 0 || keylen + 8 + shareMemoryHeadSize + vallen > itemSize) return Array.Empty<byte>();
|
||
|
||
byte[] bytes = new byte[vallen];
|
||
accessor.ReadArray(index, bytes, 0, bytes.Length);
|
||
|
||
return bytes;
|
||
}
|
||
public int ReadValueArray(int index, byte[] bytes)
|
||
{
|
||
IShareMemory accessor = accessorLocal ?? accessorGlobal;
|
||
if (accessor == null || index >= length) return 0;
|
||
index = index * itemSize + shareMemoryHeadSize;
|
||
|
||
int keylen = accessor.ReadInt(index);
|
||
index += 4 + keylen;
|
||
|
||
int vallen = accessor.ReadInt(index);
|
||
index += 4;
|
||
if (vallen == 0 || keylen + 8 + shareMemoryHeadSize + vallen > itemSize) return 0;
|
||
|
||
accessor.ReadArray(index, bytes, 0, bytes.Length);
|
||
|
||
return vallen;
|
||
}
|
||
|
||
public bool Update(int index, string key, string value,
|
||
ShareMemoryAttribute addAttri = ShareMemoryAttribute.None,
|
||
ShareMemoryAttribute removeAttri = ShareMemoryAttribute.None)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(key))
|
||
{
|
||
return Update(index, Array.Empty<byte>(), Encoding.UTF8.GetBytes(value), addAttri, removeAttri);
|
||
}
|
||
else
|
||
{
|
||
return Update(index, Encoding.UTF8.GetBytes(key), Encoding.UTF8.GetBytes(value), addAttri, removeAttri);
|
||
}
|
||
}
|
||
|
||
public bool Update(int index, byte[] key, byte[] value,
|
||
ShareMemoryAttribute addAttri = ShareMemoryAttribute.None,
|
||
ShareMemoryAttribute removeAttri = ShareMemoryAttribute.None)
|
||
{
|
||
try
|
||
{
|
||
if (accessorLocal == null && accessorGlobal == null) return false;
|
||
if (index > length) return false;
|
||
if (key.Length + 8 + shareMemoryHeadSize + value.Length > itemSize) return false;
|
||
|
||
lock (lockObj)
|
||
{
|
||
int valIndex = index * itemSize + shareMemoryHeadSize;
|
||
int startIndex = valIndex;
|
||
int keylen = key.Length;
|
||
int vallen = value.Length;
|
||
if (key.Length > 0)
|
||
{
|
||
if (accessorLocal != null)
|
||
{
|
||
accessorLocal.WriteInt(valIndex, keylen);
|
||
accessorLocal.WriteArray(valIndex + 4, key, 0, key.Length);
|
||
}
|
||
if (accessorGlobal != null)
|
||
{
|
||
accessorGlobal.WriteInt(valIndex, keylen);
|
||
accessorGlobal.WriteArray(valIndex + 4, key, 0, key.Length);
|
||
}
|
||
valIndex += 4 + key.Length;
|
||
}
|
||
else
|
||
{
|
||
int keyLen = 0;
|
||
if (accessorLocal != null)
|
||
{
|
||
keyLen = accessorLocal.ReadInt(valIndex);
|
||
}
|
||
if (keyLen == 0 && accessorGlobal != null)
|
||
{
|
||
keyLen = accessorGlobal.ReadInt(valIndex);
|
||
}
|
||
valIndex += 4 + keyLen;
|
||
}
|
||
|
||
if (accessorLocal != null)
|
||
{
|
||
accessorLocal.WriteInt(valIndex, vallen);
|
||
accessorLocal.WriteArray(valIndex + 4, value, 0, value.Length);
|
||
}
|
||
if (accessorGlobal != null)
|
||
{
|
||
accessorGlobal.WriteInt(valIndex, vallen);
|
||
accessorGlobal.WriteArray(valIndex + 4, value, 0, value.Length);
|
||
}
|
||
IncrementVersion(index);
|
||
if (removeAttri > 0)
|
||
{
|
||
RemoveAttribute(index, removeAttri);
|
||
}
|
||
if (addAttri > 0)
|
||
{
|
||
AddAttribute(index, addAttri);
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|
||
catch (Exception)
|
||
{
|
||
}
|
||
return false;
|
||
}
|
||
|
||
private ShareMemoryAttribute ReadAttribute(IShareMemory accessor, int index)
|
||
{
|
||
if (accessor == null || index >= length) return ShareMemoryAttribute.None;
|
||
|
||
ShareMemoryAttribute stateByte = (ShareMemoryAttribute)accessor.ReadByte(index * itemSize + shareMemoryAttributeIndex);
|
||
return stateByte;
|
||
}
|
||
private bool ReadAttributeEqual(IShareMemory accessor, int index, ShareMemoryAttribute attribute)
|
||
{
|
||
if (accessor == null || index >= length) return false;
|
||
|
||
ShareMemoryAttribute attributeByte = (ShareMemoryAttribute)accessor.ReadByte(index * itemSize + shareMemoryAttributeIndex);
|
||
return (attributeByte & attribute) == attribute;
|
||
}
|
||
|
||
private void AddAttribute(IShareMemory accessor, int index, ShareMemoryAttribute attribute)
|
||
{
|
||
if (accessor == null || index >= length) return;
|
||
byte attributeValue = accessor.ReadByte(index * itemSize + shareMemoryAttributeIndex);
|
||
byte attributeByte = (byte)attribute;
|
||
attributeValue |= attributeByte;
|
||
accessor.WriteByte(index * itemSize + shareMemoryAttributeIndex, attributeValue);
|
||
|
||
IncrementVersion(accessor, index);
|
||
}
|
||
private void RemoveAttribute(IShareMemory accessor, int index, ShareMemoryAttribute attribute)
|
||
{
|
||
if (accessor == null || index >= length) return;
|
||
byte attributeValue = accessor.ReadByte(index * itemSize + shareMemoryAttributeIndex);
|
||
byte attributeByte = (byte)attribute;
|
||
attributeValue &= (byte)(~attributeByte);
|
||
accessor.WriteByte(index * itemSize + shareMemoryAttributeIndex, attributeValue);
|
||
|
||
IncrementVersion(accessor, index);
|
||
}
|
||
|
||
private DateTime startTime = new DateTime(1970, 1, 1);
|
||
private void IncrementVersion(IShareMemory accessor, int index)
|
||
{
|
||
if (accessor == null || index >= length) return;
|
||
|
||
long version = (long)(DateTime.UtcNow.Subtract(startTime)).TotalMilliseconds;
|
||
accessor.WriteInt64(index * itemSize + shareMemoryVersionIndex, version);
|
||
}
|
||
private long ReadVersion(IShareMemory accessor, int index)
|
||
{
|
||
if (accessor == null || index >= length) return 0;
|
||
long version = accessor.ReadInt64(index * itemSize + shareMemoryVersionIndex);
|
||
return version;
|
||
}
|
||
private bool ReadVersionUpdated(IShareMemory accessor, int index)
|
||
{
|
||
long version = ReadVersion(accessor, index);
|
||
return version > itemVersions[index];
|
||
}
|
||
private bool ReadVersionUpdated(IShareMemory accessor, int index, ref long version)
|
||
{
|
||
long _version = ReadVersion(accessor, index);
|
||
bool res = _version > version;
|
||
version = _version;
|
||
return res;
|
||
}
|
||
|
||
public ShareMemoryAttribute ReadAttribute(int index)
|
||
{
|
||
IShareMemory accessor = accessorLocal ?? accessorGlobal;
|
||
if (accessor == null) return ShareMemoryAttribute.None;
|
||
|
||
return ReadAttribute(accessor, index);
|
||
}
|
||
public bool ReadAttributeEqual(int index, ShareMemoryAttribute attribute)
|
||
{
|
||
IShareMemory accessor = accessorLocal ?? accessorGlobal;
|
||
if (accessor == null) return false;
|
||
|
||
return ReadAttributeEqual(accessor, index, attribute);
|
||
}
|
||
|
||
public void AddAttribute(int index, ShareMemoryAttribute attribute)
|
||
{
|
||
AddAttribute(accessorLocal, index, attribute);
|
||
AddAttribute(accessorGlobal, index, attribute);
|
||
}
|
||
public void RemoveAttribute(int index, ShareMemoryAttribute attribute)
|
||
{
|
||
RemoveAttribute(accessorLocal, index, attribute);
|
||
RemoveAttribute(accessorGlobal, index, attribute);
|
||
}
|
||
|
||
public void IncrementVersion(int index)
|
||
{
|
||
IncrementVersion(accessorLocal, index);
|
||
IncrementVersion(accessorGlobal, index);
|
||
}
|
||
public long ReadVersion(int index)
|
||
{
|
||
IShareMemory accessor = accessorLocal ?? accessorGlobal;
|
||
if (accessor == null) return 0;
|
||
|
||
return ReadVersion(accessor, index);
|
||
}
|
||
public bool ReadVersionUpdated(int index)
|
||
{
|
||
IShareMemory accessor = accessorLocal ?? accessorGlobal;
|
||
if (accessor == null) return false;
|
||
|
||
return ReadVersionUpdated(accessor, index);
|
||
}
|
||
public bool ReadVersionUpdated(int index, ref long version)
|
||
{
|
||
IShareMemory accessor = accessorLocal ?? accessorGlobal;
|
||
if (accessor == null) return false;
|
||
|
||
return ReadVersionUpdated(accessor, index, ref version);
|
||
}
|
||
|
||
public void ListenClose(int shareIndex)
|
||
{
|
||
Task.Run(async () =>
|
||
{
|
||
while (true)
|
||
{
|
||
if (ReadAttributeEqual(shareIndex, ShareMemoryAttribute.Closed))
|
||
{
|
||
RemoveAttribute(shareIndex, ShareMemoryAttribute.Running);
|
||
RemoveAttribute(shareIndex, ShareMemoryAttribute.Running);
|
||
Environment.Exit(0);
|
||
Process.GetCurrentProcess().Kill();
|
||
}
|
||
await Task.Delay(30);
|
||
}
|
||
});
|
||
}
|
||
}
|
||
|
||
public enum ShareMemoryAttribute : byte
|
||
{
|
||
None = 0,
|
||
Updated = 0b0000_0001,
|
||
Closed = 0b0000_0010,
|
||
Running = 0b0000_0100,
|
||
HiddenForList = 0b0000_1000,
|
||
Error = 0b0001_0000,
|
||
All = 0b1111_1111
|
||
}
|
||
|
||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||
public struct ShareItem
|
||
{
|
||
public byte Attr;
|
||
|
||
public ulong Version;
|
||
|
||
public ulong Time;
|
||
|
||
public byte KeyLength;
|
||
|
||
/// <summary>
|
||
/// 255长度
|
||
/// </summary>
|
||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 255)]
|
||
public byte[] Key;
|
||
|
||
public byte ValLength;
|
||
|
||
/// <summary>
|
||
/// 9966长度
|
||
/// </summary>
|
||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 9966)]
|
||
public byte[] Value;
|
||
}
|
||
|
||
public sealed class ShareItemAttributeChanged
|
||
{
|
||
public Action<ShareMemoryAttribute> Action { get; set; }
|
||
public ShareMemoryAttribute Attribute { get; set; }
|
||
}
|
||
|
||
public sealed partial class ShareItemInfo
|
||
{
|
||
/// <summary>
|
||
/// 内存下标
|
||
/// </summary>
|
||
public int Index { get; set; }
|
||
/// <summary>
|
||
/// 内存值
|
||
/// </summary>
|
||
public string Value { get; set; }
|
||
|
||
}
|
||
}
|