using log4net; using System; using System.Collections.Generic; using System.Threading; namespace OpenSEMI.Core.Msg { public class MsgPool { internal class Msg { public string id { get; private set; } public string Description { get; set; } public Action Handler { get; set; } public Action WithParameterHandler { get; set; } public object Sender { get; private set; } public object Param { get; private set; } public bool HasParam { get; private set; } public Msg() { id = Guid.NewGuid().ToString(); } public Msg(Action handler = null, object sender = null, string desc = null) : this() { id = Guid.NewGuid().ToString(); Sender = sender; Handler = handler; Description = desc; HasParam = false; } public Msg(Action handler = null, object param = null, object sender = null, string desc = null) : this() { id = Guid.NewGuid().ToString(); Sender = sender; WithParameterHandler = handler; Description = desc; Param = param; HasParam = true; } } private ManualResetEvent waitevent = new ManualResetEvent(false); private Thread context; private Action Handler = null; private Queue PostMsgs = new Queue(); private Stack SendMsgs = new Stack(); private readonly object lockMsg = new object(); private readonly object lockSubscribers = new object(); private MsgPool lazylooper; private bool LazySupported; private static object _lock = new object(); private static List Loopers = new List(); private ILog log; private uint lastInvervalTime; public string LooperName { get; set; } public bool IsRuning { get; private set; } public Dictionary ExecutionResults { get; private set; } public int Interval { get; set; } public MsgPool(string loopname = null, bool lazySupported = false) { Interval = 500; LazySupported = lazySupported; ExecutionResults = new Dictionary(); LooperName = loopname; } public MsgPool(int interval, Action handler, string loopername = null, bool lazySupported = false) : this(loopername, lazySupported) { Interval = interval; Handler = handler; LazySupported = lazySupported; } public void SetLog(ILog _log) { log = _log; } public int getCount() { return PostMsgs.Count + SendMsgs.Count; } public void Run() { if (!IsRuning) { IsRuning = true; if (LazySupported) { lazylooper = new MsgPool(null, false); lazylooper.Run(); } context = new Thread(Loop); context.Start(); lock (_lock) { Loopers.Add(this); } } } private void Loop() { lastInvervalTime = GetTickCount(); while (true) { uint millisecondsTimeout = (uint)(((uint)Interval <= GetElapsedMS(lastInvervalTime)) ? 1 : (Interval - (int)GetElapsedMS(lastInvervalTime))); if (IsRuning) { bool flag = waitevent.WaitOne((int)millisecondsTimeout); if (IsRuning) { if (flag) { Msg msg = null; lock (lockMsg) { if (SendMsgs.Count > 0) { msg = SendMsgs.Pop(); } else if (PostMsgs.Count > 0) { msg = PostMsgs.Dequeue(); } } if (msg != null) { try { if (!msg.HasParam && msg.Handler != null) { msg.Handler(this, msg.Sender); } else if (msg.WithParameterHandler != null) { msg.WithParameterHandler(this, msg.Param, msg.Sender); } } catch (Exception ex) { ExecutionResults.Add(msg.id, ex.Message); if (log != null) { log.Error("Msg:" + ex.Message + ",CallStack:" + ex.StackTrace); } } } if (SendMsgs.Count <= 0 && PostMsgs.Count <= 0) { waitevent.Reset(); } } else { if (Handler != null) { Handler(this); } lastInvervalTime = GetTickCount(); } continue; } } break; } } public void Terminate(bool force = false) { if (!force) { if (LazySupported) { LazyMsg(delegate { lazylooper.IsRuning = false; }, this, "Terminate lazy looper"); lazylooper.context.Join(); } PostMsg(delegate { IsRuning = false; }, this, "Terminate looper"); } else { if (LazySupported) { lazylooper.IsRuning = false; lazylooper.waitevent.Set(); } IsRuning = false; waitevent.Set(); } lock (_lock) { Loopers.Remove(this); } } public static void TerminateAll(bool force = false, MsgPool looper = null) { lock (_lock) { if (looper != null) { looper.Terminate(force); } else { foreach (MsgPool looper2 in Loopers) { if (!force) { if (looper2.LazySupported) { looper2.LazyMsg(delegate { looper2.lazylooper.IsRuning = false; }, looper2, "Terminate lazy looper"); } looper2.PostMsg(delegate { looper2.IsRuning = false; }, looper2, "Terminate looper"); } else { if (looper2.LazySupported) { looper2.lazylooper.IsRuning = false; looper2.lazylooper.waitevent.Set(); } looper2.IsRuning = false; looper2.waitevent.Set(); } } Loopers.Clear(); } } } public string PostMsg(Action handler, object sender = null, string description = "handler") { lock (lockMsg) { if (!IsRuning) { return string.Empty; } Msg msg = new Msg(handler, sender, description); PostMsgs.Enqueue(msg); waitevent.Set(); return msg.id; } } public string PostMsg(Action handler, object param, object sender = null, string description = "handler") { lock (lockMsg) { if (!IsRuning) { return string.Empty; } Msg msg = new Msg(handler, param, sender, description); PostMsgs.Enqueue(msg); waitevent.Set(); return msg.id; } } public string SendMsg(Action handler, object sender = null, string description = "handler") { lock (lockMsg) { if (!IsRuning) { return string.Empty; } Msg msg = new Msg(handler, sender, description); SendMsgs.Push(msg); waitevent.Set(); return msg.id; } } public string SendMsg(Action handler, object param, object sender = null, string description = "handler") { lock (lockMsg) { if (!IsRuning) { return string.Empty; } Msg msg = new Msg(handler, param, sender, description); SendMsgs.Push(msg); waitevent.Set(); return msg.id; } } public string LazyMsg(Action handler, object sender = null, string description = "handler") { if (!IsRuning) { return string.Empty; } if (LazySupported) { return lazylooper.PostMsg(handler, sender, description); } return null; } public string LazyMsg(Action handler, object param, object sender = null, string description = "handler") { if (!IsRuning) { return string.Empty; } if (LazySupported) { return lazylooper.PostMsg(handler, param, sender, description); } return null; } private uint GetElapsedMS(uint startMS) { uint tickCount = GetTickCount(); if (startMS > tickCount) { return (uint)(-1 - (int)startMS + (int)tickCount + 1); } return tickCount - startMS; } private uint GetTickCount() { return (uint)Environment.TickCount; } } }