Skip to content
Permalink
Browse files

client restructure <wip>

  • Loading branch information...
Splamy committed Jun 12, 2019
1 parent 84fdcb9 commit 435ce1628e15a737b2138b189087a6596610af7d
@@ -445,7 +445,8 @@ private void TryCatchCommand(ExecutionInformation info, bool answer, Action acti
}
catch (CommandException ex)
{
Log.Debug(ex, "Command Error ({0})", ex.Message);
NLog.LogLevel commandErrorLevel = answer ? NLog.LogLevel.Debug : NLog.LogLevel.Warn;
Log.Log(commandErrorLevel, ex, "Command Error ({0})", ex.Message);
if (answer)
{
info.Write(TextMod.Format(config.Commands.Color, strings.error_call_error.Mod().Color(Color.Red).Bold(), ex.Message))
@@ -14,34 +14,14 @@ namespace TS3Client
using System.Collections.Concurrent;
using System.Threading;

using EvloopType = System.Action<object>;

internal static class EventDispatcherHelper
{
public const string DispatcherTitle = "TS3 Dispatcher";
public const string EventLoopTitle = "TS3 MessageLoop";

public static IEventDispatcher Create(EventDispatchType dispatcherType)
{
IEventDispatcher dispatcher;
switch (dispatcherType)
{
case EventDispatchType.None: dispatcher = new NoEventDispatcher(); break;
case EventDispatchType.CurrentThread: dispatcher = new CurrentThreadEventDisptcher(); break;
case EventDispatchType.ExtraDispatchThread: dispatcher = new ExtraThreadEventDispatcher(); break;
case EventDispatchType.DoubleThread: dispatcher = new DoubleThreadEventDispatcher(); break;
case EventDispatchType.AutoThreadPooled: dispatcher = new AutoThreadPooledEventDispatcher(); break;
case EventDispatchType.NewThreadEach: dispatcher = new NewThreadEachEventDispatcher(); break;
default: throw new NotSupportedException();
}
return dispatcher;
}

internal static string CreateLogThreadName(string threadName, Id id) => threadName + (id == Id.Null ? "" : $"[{id}]");

internal static string CreateDispatcherTitle(Id id) => CreateLogThreadName(DispatcherTitle, id);

internal static string CreateEventLoopTitle(Id id) => CreateLogThreadName(DispatcherTitle, id);
}

/// <summary> Provides a function to run a receiving loop and asynchronously
@@ -54,128 +34,33 @@ internal interface IEventDispatcher : IDisposable
/// <param name="dispatcher">The method to call asynchronously when a new
/// notification comes in.</param>
/// <param name="ctx">The current connection context.</param>
void Init(EvloopType eventLoop, Action<LazyNotification> dispatcher, object ctx, Id id);
void Init(Action<LazyNotification> dispatcher, Id id);
/// <summary>Dispatches the notification.</summary>
/// <param name="lazyNotification"></param>
void Invoke(LazyNotification lazyNotification);
/// <summary>Starts the eventLoop synchronously or asynchronous,
/// depending on the dispatcher type.
/// </summary>
void EnterEventLoop();
void DoWork();
}

internal sealed class CurrentThreadEventDisptcher : IEventDispatcher
{
private EvloopType eventLoop;
private Action<LazyNotification> dispatcher;
private object ctx;
private Id id;

public void Init(EvloopType eventLoop, Action<LazyNotification> dispatcher, object ctx, Id id)
{
this.eventLoop = eventLoop;
this.dispatcher = dispatcher;
this.ctx = ctx;
this.id = id;
}
public void EnterEventLoop()
{
Util.SetLogId(id);
eventLoop(ctx);
}
public void Invoke(LazyNotification lazyNotification) => dispatcher.Invoke(lazyNotification);
public void DoWork() { }
public void Dispose() { }
}

internal sealed class ExtraThreadEventDispatcher : IEventDispatcher
{
private EvloopType eventLoop;
private Action<LazyNotification> dispatcher;
private object ctx;
private Id id;
private Thread dispatchThread;
private readonly ConcurrentQueue<LazyNotification> eventQueue = new ConcurrentQueue<LazyNotification>();
private readonly AutoResetEvent eventBlock = new AutoResetEvent(false);
private volatile bool run;

public void Init(EvloopType eventLoop, Action<LazyNotification> dispatcher, object ctx, Id id)
public void Init(Action<LazyNotification> dispatcher, Id id)
{
run = true;
this.eventLoop = eventLoop;
this.dispatcher = dispatcher;
this.ctx = ctx;
this.id = id;
}

public void Invoke(LazyNotification lazyNotification)
{
eventQueue.Enqueue(lazyNotification);
eventBlock.Set();
}

public void EnterEventLoop()
{
dispatchThread = new Thread(() =>
{
Util.SetLogId(id);
DispatchLoop();
})
{ Name = EventDispatcherHelper.CreateDispatcherTitle(id) };
dispatchThread.Start();

Util.SetLogId(id);
eventLoop(ctx);
}

private void DispatchLoop()
{
while (run)
{
eventBlock.WaitOne();
while (!eventQueue.IsEmpty)
{
if (eventQueue.TryDequeue(out var lazyNotification))
dispatcher.Invoke(lazyNotification);
}
}
}

public void DoWork()
{
if (Thread.CurrentThread.ManagedThreadId != dispatchThread.ManagedThreadId)
return;
if (eventQueue.TryDequeue(out var lazyNotification))
dispatcher.Invoke(lazyNotification);
}

public void Dispose()
{
run = false;
eventBlock.Set();
}
}

internal sealed class DoubleThreadEventDispatcher : IEventDispatcher
{
private EvloopType eventLoop;
private Action<LazyNotification> dispatcher;
private object ctx;
private Id id;
private Thread eventLoopThread;
private Thread dispatchThread;
private readonly ConcurrentQueue<LazyNotification> eventQueue = new ConcurrentQueue<LazyNotification>();
private readonly AutoResetEvent eventBlock = new AutoResetEvent(false);
private volatile bool run;

public void Init(EvloopType eventLoop, Action<LazyNotification> dispatcher, object ctx, Id id)
{
run = true;
this.eventLoop = eventLoop;
this.dispatcher = dispatcher;
this.ctx = ctx;
this.id = id;
}

public void Invoke(LazyNotification lazyNotification)
@@ -184,28 +69,6 @@ public void Invoke(LazyNotification lazyNotification)
eventBlock.Set();
}

public void EnterEventLoop()
{
dispatchThread = new Thread(() =>
{
Util.SetLogId(id);
DispatchLoop();
})
{
Name = EventDispatcherHelper.CreateDispatcherTitle(id)
};
dispatchThread.Start();
eventLoopThread = new Thread(() =>
{
Util.SetLogId(id);
eventLoop(ctx);
})
{
Name = EventDispatcherHelper.CreateEventLoopTitle(id)
};
eventLoopThread.Start();
}

private void DispatchLoop()
{
while (run)
@@ -236,39 +99,22 @@ public void Dispose()

internal sealed class NoEventDispatcher : IEventDispatcher
{
public void Init(EvloopType eventLoop, Action<LazyNotification> dispatcher, object ctx, Id id) { }
public void EnterEventLoop() { }
public void Init(Action<LazyNotification> dispatcher, Id id) { }
public void Invoke(LazyNotification lazyNotification) { }
public void DoWork() { }
public void Dispose() { }
}

internal sealed class AutoThreadPooledEventDispatcher : IEventDispatcher
{
private EvloopType eventLoop;
private Action<LazyNotification> dispatcher;
private object ctx;
private Id id;
private Thread eventLoopThread;

public void Init(EvloopType eventLoop, Action<LazyNotification> dispatcher, object ctx, Id id)
public void Init(Action<LazyNotification> dispatcher, Id id)
{
this.eventLoop = eventLoop;
this.dispatcher = dispatcher;
this.ctx = ctx;
this.id = id;
}

public void EnterEventLoop()
{
eventLoopThread = new Thread(() =>
{
Util.SetLogId(id);
eventLoop(ctx);
})
{ Name = EventDispatcherHelper.CreateEventLoopTitle(id) };
eventLoopThread.Start();
}
public void Invoke(LazyNotification lazyNotification) => ThreadPool.QueueUserWorkItem(Call, lazyNotification);
private void Call(object obj)
{
@@ -278,81 +124,4 @@ private void Call(object obj)
public void DoWork() { }
public void Dispose() { }
}

internal sealed class NewThreadEachEventDispatcher : IEventDispatcher
{
private EvloopType eventLoop;
private Action<LazyNotification> dispatcher;
private object ctx;
private Id id;
private Thread eventLoopThread;

public void Init(EvloopType eventLoop, Action<LazyNotification> dispatcher, object ctx, Id id)
{
this.eventLoop = eventLoop;
this.dispatcher = dispatcher;
this.ctx = ctx;
this.id = id;
}

public void EnterEventLoop()
{
eventLoopThread = new Thread(() =>
{
Util.SetLogId(id);
eventLoop(ctx);
})
{ Name = EventDispatcherHelper.CreateEventLoopTitle(id) };
eventLoopThread.Start();
}

public void Invoke(LazyNotification lazyNotification)
{
eventLoopThread = new Thread(() =>
{
Util.SetLogId(id);
dispatcher(lazyNotification);
})
{ Name = EventDispatcherHelper.CreateLogThreadName("TS3 MessageDispatcher", id) };
eventLoopThread.Start();
}

public void DoWork() { }
public void Dispose() { }
}

public enum EventDispatchType
{
/// <summary>
/// All events will be dropped.
/// </summary>
None,
/// <summary>
/// Will use the same thread that entered the <see cref="Ts3BaseFunctions.Connect"/>
/// for receiving and invoking all events. This method is not recommended since it mostly
/// only produces deadlocks. (Usually only for debugging)
/// </summary>
CurrentThread,
/// <summary>
/// Will use the thread that entered the <see cref="Ts3BaseFunctions.Connect"/> for
/// receiving and starts a second thread for invoking all events. This is the best method for
/// lightweight dipatching with no parallelization.
/// </summary>
ExtraDispatchThread,
/// <summary>
/// Will start one thread for receiving and a second thread for invoking all events.
/// This is the best method for lightweight asynchronous dispatching with no parallelization.
/// </summary>
DoubleThread,
/// <summary>
/// This method will use the <see cref="ThreadPool"/> from .NET to dispatch all events.
/// This is the best method for high parallelization with low overhead when using many instances.
/// </summary>
AutoThreadPooled,
/// <summary>
/// This method will create a new Thread for each event. This method is not recommended
/// due to high overhead and resource consumption. Only try it when all else fails.
/// </summary>
NewThreadEach,
}
}

0 comments on commit 435ce16

Please sign in to comment.
You can’t perform that action at this time.