-
-
Notifications
You must be signed in to change notification settings - Fork 4
Threads
The reason why the application does not close after Main
is "done" when running the plugin, is because events are picked up by a listener thread.
This is a foreground thread that will hold the application open, as long as the SDK is connected to TouchPortal.
The listener thread will then become the "new main" thread.
This is also the most common way of doing this.
_listenerThread = new Thread(ListenerThread) { IsBackground = false };
_listenerThread.Start();
private void ListenerThread()
{
while (true)
{
try
{
var message = _streamReader.ReadLine()
?? throw new IOException("Socket closed."); //Catch right underneath, and then the thread exits.
//React on the message/event here:
Console.WriteLine(message);
}
catch (IOException exception)
{
//Do some close logic.
return;
}
catch (Exception exception)
{
//Ignore, log, etc.
}
}
}
The listener thread can be closes in this scenarios:
- Socket closed (message returned is null), ex. TouchPortal Exits.
- IOException is thrown.
- Close message is sent. ex. Plugin was stopped from Settings.
- Plugin closes. ex. Developer Calls the Close method.
When the listener thread is closed, it would normally take down the whole process, since no more foreground threads is running.
However, to be sure, it could be a good idea to call Environment.Exit(0);
from your plugin OnClosedEvent
Method.
This way the plugin will exit, even if there are other foreground threads running.
All methods of the ITouchPortalEventHandler
will be called from the listener thread.
- By default the listener thread is locked until the call returns.
This might be changed in the future, if we run the plugin logic on a ThreadPool/Dispatcher/Execution thread instead.
At least it is room for improvements here.
- Exceptions are swallowed by a global exception handler.
However, you might get some issued if using
async void
, that escapes this handler.
Wrap this then with atry/catch
before calling anything, and handle it yourself.
This might be fixed with a custom SynchronizationContext, but that is probably not worth it.
See Parallelization for tips on how to get around this.
Async/await is a good approach in two scenarios responsiveness and scalability.
None of these fit well for the kind of task of this SDK, so for now we don't se to much reason to use the async implementation yet.
It might also introduce a lot of drawbacks like:
- A listener thread must still be created, and then we a lot more threads in the process.
- Adds complexity to the design.
- More edge-cases can occur, think: Mono, dotnet, FullFramework, linux, mac, windows etc.
- Harder for non-experienced developers.
Therefor we use ReadLine
and not ReadLineAsync
in this SDK today.
We are open to the idea to implement this using Async calls instead, however, this needs to be planned and executed well...
and right now, it's just not worth it.
However, you are free to use async await for parallelization of your actions.
Or come up with some ideas...
Or if you use another approach, please tell us about it. :)
- Home
- Development
- Getting started
- TODO: Entry.tp
- TODO: Commands
- TODO: Events
- Logging
- Parallelization
- TODO: Graphics / Images
- TODO: Plugins with GUI
- Create a .tpp package
- TODO: Creating a installer
- Troubleshooting
- How the SDK works
- Other SDKs