I have a class which starts a Task and want to ensure that the Task stops when the object is garbage collected.
I have implemented the IDisposable pattern to ensure that if the object is disposed manually or used within a using block, then the Task stops correctly. However, I cant guarantee that the end user will call Dispose() or use the object within a using block. I know that the Garbage Collector will eventually call the Finalizer - does this mean that the task is left running?
public class MyClass : IDisposable
{
private readonly CancellationTokenSource feedCancellationTokenSource =
new CancellationTokenSource();
private readonly Task feedTask;
public MyClass()
{
feedTask = Task.Factory.StartNew(() =>
{
while (!feedCancellationTokenSource.IsCancellationRequested)
{
// do finite work
}
});
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
feedCancellationTokenSource.Cancel();
feedTask.Wait();
feedCancellationTokenSource.Dispose();
feedTask.Dispose();
}
}
~MyClass()
{
Dispose(false);
}
}
It was suggested in this question to add a volatile bool which is set from the Finalizer and observed from the task. Is this recommended, or is there a better way to achieve what I need?
(I'm using .NET 4 hence the use of TaskFactory.StartNew rather than Task.Run)
EDIT:
To give some context to the question - which is not actually shown in the above code snippet: I am creating a Network client class which has a mechanism to keep alive by regularly sending packets to the server. I chose not to put all this detail in the example as it wasn't relevant to my specific question. However, what I actually want is the ability for the user to set a KeepAlive boolean property to true, which will start a task to send data to the server every 60 seconds. If the user sets the property to false then the task stops. IDisposable got me 90% of the way there, however it relies on the user disposing it properly (explicitly or via using). I don't want to expose keep alive tasks to the user for them to cancel explicitly, I just want a "simple" KeepAlive = true/false to start/stop the task AND I want the task to stop when the user is finished with the object - even if they don't dispose of it properly. I'm starting to think that this isn't possible!
I'll sketch an answer. I'm not 100% confident that this will work. Finalization is a complicated issue and I'm not proficient in it.
There can be no object reference from the task to whatever object is supposed to be finalized.
You can't touch other objects from a finalizer that are not known to be safe. The built-in .NET classes do not usually document this safety property. You can't rely on that (usually).
class CancellationFlag { public volatile bool IsSet; }
You can now share an instance of this class between the task and MyClass. The task must poll the flag and MyClass must set it.
In order to ensure that the task never accidentally references the outer object I'd structure the code like this:
Task.Factory.StartNew(TaskProc, state); //no lambda
static void TaskProc(object state) { //static
}
This way you can explicitly thread any state through state. This would, at least, be an instance of CancellationFlag but under no circumstances a reference to MyClass.
I created the program below to explore the differences...
From my observations with it, it looks like it makes no difference whether it's a cancellation token or a volatile bool, what really matters is that the Task.StartNew method isn't called using a lambda expression.
Edit: to clarify: if the lambda refers to a static method, it's actually fine: the problem comes when the lambda causes a reference to the containing class to be included: so either a reference to a member variable of the parent class or else a reference to an instance method of the parent class.
Please do give this a try and let me know if you come to the same conclusion.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication7
{
class Program
{
static void Main(string[] args)
{
Logger.LogFile = #"c:\temp\test\log.txt";
Task.Run(() =>
{
// two instances (not disposed properly)
// if left to run, this background task keeps running until the application exits
var c1 = new MyClassWithVolatileBoolCancellationFlag();
// if left to run, this background task cancels correctly
var c2 = new MyClassWithCancellationSourceAndNoLambda();
//
var c3 = new MyClassWithCancellationSourceAndUsingTaskDotRun();
//
var c4 = new MyClassWithCancellationSourceAndUsingTaskDotRunButNoParentReference();
}).GetAwaiter().GetResult();
// instances no longer referenced at this point
Logger.Log("Press Enter to exit");
Console.ReadLine(); // press enter to allow the console app to exit normally: finalizer gets called on both instances
}
static class Logger
{
private static object LogLock = new object();
public static string LogFile;
public static void Log(string toLog)
{
try
{
lock (LogLock)
using (var f = File.AppendText(LogFile))
f.WriteLine(toLog);
Console.WriteLine(toLog);
}
catch (Exception ex)
{
Console.WriteLine("Logging Exception: " + ex.ToString());
}
}
}
// finalizer gets called eventually (unless parent process is terminated)
public class MyClassWithCancellationSourceAndUsingTaskDotRunButNoParentReference : IDisposable
{
private CancellationTokenSource cts = new CancellationTokenSource();
private readonly Task feedTask;
public MyClassWithCancellationSourceAndUsingTaskDotRunButNoParentReference()
{
Logger.Log("New MyClassWithCancellationSourceAndUsingTaskDotRunButNoParentReference Instance");
var token = cts.Token; // NB: by extracting the struct here (instead of in the lambda in the next line), we avoid the parent reference (via the cts member variable)
feedTask = Task.Run(() => Background(token)); // token is a struct
}
private static void Background(CancellationToken token) // must be static or else a reference to the parent class is passed
{
int i = 0;
while (!token.IsCancellationRequested) // reference to cts means this class never gets finalized
{
Logger.Log("Background task for MyClassWithCancellationSourceAndUsingTaskDotRunButNoParentReference running. " + i++);
Thread.Sleep(1000);
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
cts.Cancel();
if (disposing)
{
feedTask.Wait();
feedTask.Dispose();
Logger.Log("MyClassWithCancellationSourceAndUsingTaskDotRunButNoParentReference Disposed");
}
else
{
Logger.Log("MyClassWithCancellationSourceAndUsingTaskDotRunButNoParentReference Finalized");
}
}
~MyClassWithCancellationSourceAndUsingTaskDotRunButNoParentReference()
{
Dispose(false);
}
}
// finalizer doesn't get called until the app is exiting: background process keeps running
public class MyClassWithCancellationSourceAndUsingTaskDotRun : IDisposable
{
private CancellationTokenSource cts = new CancellationTokenSource();
private readonly Task feedTask;
public MyClassWithCancellationSourceAndUsingTaskDotRun()
{
Logger.Log("New MyClassWithCancellationSourceAndUsingTaskDotRun Instance");
//feedTask = Task.Factory.StartNew(Background, cts.Token);
feedTask = Task.Run(() => Background());
}
private void Background()
{
int i = 0;
while (!cts.IsCancellationRequested) // reference to cts & not being static means this class never gets finalized
{
Logger.Log("Background task for MyClassWithCancellationSourceAndUsingTaskDotRun running. " + i++);
Thread.Sleep(1000);
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
cts.Cancel();
if (disposing)
{
feedTask.Wait();
feedTask.Dispose();
Logger.Log("MyClassWithCancellationSourceAndUsingTaskDotRun Disposed");
}
else
{
Logger.Log("MyClassWithCancellationSourceAndUsingTaskDotRun Finalized");
}
}
~MyClassWithCancellationSourceAndUsingTaskDotRun()
{
Dispose(false);
}
}
// finalizer gets called eventually (unless parent process is terminated)
public class MyClassWithCancellationSourceAndNoLambda : IDisposable
{
private CancellationTokenSource cts = new CancellationTokenSource();
private readonly Task feedTask;
public MyClassWithCancellationSourceAndNoLambda()
{
Logger.Log("New MyClassWithCancellationSourceAndNoLambda Instance");
feedTask = Task.Factory.StartNew(Background, cts.Token);
}
private static void Background(object state)
{
var cancelled = (CancellationToken)state;
if (cancelled != null)
{
int i = 0;
while (!cancelled.IsCancellationRequested)
{
Logger.Log("Background task for MyClassWithCancellationSourceAndNoLambda running. " + i++);
Thread.Sleep(1000);
}
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
cts.Cancel();
if (disposing)
{
feedTask.Wait();
feedTask.Dispose();
Logger.Log("MyClassWithCancellationSourceAndNoLambda Disposed");
}
else
{
Logger.Log("MyClassWithCancellationSourceAndNoLambda Finalized");
}
}
~MyClassWithCancellationSourceAndNoLambda()
{
Dispose(false);
}
}
// finalizer doesn't get called until the app is exiting: background process keeps running
public class MyClassWithVolatileBoolCancellationFlag : IDisposable
{
class CancellationFlag { public volatile bool IsSet; }
private CancellationFlag cf = new CancellationFlag();
private readonly Task feedTask;
public MyClassWithVolatileBoolCancellationFlag()
{
Logger.Log("New MyClassWithVolatileBoolCancellationFlag Instance");
feedTask = Task.Factory.StartNew(() =>
{
int i = 0;
while (!cf.IsSet)
{
Logger.Log("Background task for MyClassWithVolatileBoolCancellationFlag running. " + i++);
Thread.Sleep(1000);
}
});
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
cf.IsSet = true;
if (disposing)
{
feedTask.Wait();
feedTask.Dispose();
Logger.Log("MyClassWithVolatileBoolCancellationFlag Disposed");
}
else
{
Logger.Log("MyClassWithVolatileBoolCancellationFlag Finalized");
}
}
~MyClassWithVolatileBoolCancellationFlag()
{
Dispose(false);
}
}
}
}
Update:
Added a few more tests (now included above): and came to the same conclusion as "usr": the finalizer never gets called if there's a reference to the parent class (which makes sense: an active reference exists, therefore the GC doesn't kick in)
Related
Background:
I have an application I am developing that deals with a large number of addons for another application. One if its primary uses is to safely modify file records in files with fewer records so that they may be treated as one file (almost as if it is combing the files together into one set of records. To do this safely it keeps track of vital information about those files and changes made to them so that those changes can be undone if they don't work as expected.
When my application starts, it analyzes those files and keeps essential properties in a cache (to reduce load times). If a file is missing from the cache, the most important stuff is retrieved and then a background worker must process the file for more information. If a file that was previously modified has been updated with a new version of the file, the UI must confirm this with the user and its modification data removed. All of this information, including information on its modification is stored in the cache.
My Problem:
My problem is that neither of these processes are guaranteed to run (the confirmation window or the background file processor). If either of them run, then the cache must be updated by the main thread. I don't know enough about worker threads, and which thread runs the BackgroundWorker.RunWorkerCompleted event handler in order to effectively decide how to approach guaranteeing that the cache updater is run after either (or both) processes are completed.
To sum up: if either process is run, they both must finish and (potentially) wait for the other to be completed before running the cache update code. How can I do this?
ADJUNCT INFO (My current intervention that doesn't seem to work very well):
I have a line in the RunWorkerCompleted handler that waits until the form reference is null before continuing and exiting but maybe this was a mistake as it sometimes locks my program up.
SpinWait.SpinUntil(() => overwriteForm == null);
I haven't included any more code because I anticipate that this is more of a conceptual question than a code one. However, if necessary, I can supply code if it helps.
I think CountDownTask is what you need
using System;
using System.Threading;
public class Program
{
public class AtomicInteger
{
protected int value = 0;
public AtomicInteger(int value)
{
this.value = value;
}
public int DecrementAndGet()
{
int answer = Interlocked.Decrement(ref value);
return answer;
}
}
public interface Runnable
{
void Run();
}
public class CountDownTask
{
private AtomicInteger count;
private Runnable task;
private Object lk = new Object();
private volatile bool runnable;
private bool cancelled;
public CountDownTask(Int32 count, Runnable task)
{
this.count = new AtomicInteger(count);
this.task = task;
this.runnable = false;
this.cancelled = false;
}
public void CountDown()
{
if (count.DecrementAndGet() == 0)
{
lock (lk)
{
runnable = true;
Monitor.Pulse(lk);
}
}
}
public void Await()
{
lock (lk)
{
while (!runnable)
{
Monitor.Wait(lk);
}
if (cancelled)
{
Console.WriteLine("Sorry! I was cancelled");
}
else {
task.Run();
}
}
}
public void Cancel()
{
lock (lk)
{
runnable = true;
cancelled = true;
Monitor.Pulse(lk);
}
}
}
public class HelloWorldTask : Runnable
{
public void Run()
{
Console.WriteLine("Hello World, I'm last one");
}
}
public static void Main()
{
Thread.CurrentThread.Name = "Main";
Console.WriteLine("Current Thread: " + Thread.CurrentThread.Name);
CountDownTask countDownTask = new CountDownTask(3, new HelloWorldTask());
Thread worker1 = new Thread(() => {
Console.WriteLine("Worker 1 run");
countDownTask.CountDown();
});
Thread worker2 = new Thread(() => {
Console.WriteLine("Worker 2 run");
countDownTask.CountDown();
});
Thread lastThread = new Thread(() => countDownTask.Await());
lastThread.Start();
worker1.Start();
worker2.Start();
//countDownTask.Cancel();
Console.WriteLine("Main Thread Run");
countDownTask.CountDown();
Thread.Sleep(1000);
}
}
let me explain (but you can refer Java CountDownLatch)
1. To ensure a task must run after another tasks, we need create a Wait function to wait for they done, so I used
while(!runnable) {
Monitor.Wait(lk);
}
2. When there is a task done, we need count down, and if count down to zero (it means all of the tasks was done) we will need notify to blocked thread to wake up and process task
if(count.decrementAndGet() == 0) {
lock(lk) {
runnable = true;
Monitor.Pulse(lk);
}
}
Let read more about volatile, thanks
While dung ta van's "CountDownTask" answer isn't quite what I needed, it heavily inspired the solution below (see it for more info). Basically all I did was add some extra functionality and most importantly: made it so that each task "vote" on the outcome (true or false). Thanks dung ta van!
To be fair, dung ta van's solution DOES work to guarantee execution which as it turns out isn't quite what I needed. My solution adds the ability to make that execution conditional.
This was my solution which worked:
public enum PendingBool
{
Unknown = -1,
False,
True
}
public interface IRunnableTask
{
void Run();
}
public class AtomicInteger
{
int integer;
public int Value { get { return integer; } }
public AtomicInteger(int value) { integer = value; }
public int Decrement() { return Interlocked.Decrement(ref integer); }
public static implicit operator int(AtomicInteger ai) { return ai.integer; }
}
public class TaskElectionEventArgs
{
public bool VoteResult { get; private set; }
public TaskElectionEventArgs(bool vote) { VoteResult = vote; }
}
public delegate void VoteEventHandler(object sender, TaskElectionEventArgs e);
public class SingleVoteTask
{
private AtomicInteger votesLeft;
private IRunnableTask task;
private volatile bool runTask = false;
private object _lock = new object();
public event VoteEventHandler VoteCast;
public event VoteEventHandler TaskCompleted;
public bool IsWaiting { get { return votesLeft.Value > 0; } }
public PendingBool Result
{
get
{
if (votesLeft > 0)
return PendingBool.Unknown;
else if (runTask)
return PendingBool.True;
else
return PendingBool.False;
}
}
public SingleVoteTask(int numberOfVotes, IRunnableTask taskToRun)
{
votesLeft = new AtomicInteger(numberOfVotes);
task = taskToRun;
}
public void CastVote(bool vote)
{
votesLeft.Decrement();
runTask |= vote;
VoteCast?.Invoke(this, new TaskElectionEventArgs(vote));
if (votesLeft == 0)
lock (_lock)
{
Monitor.Pulse(_lock);
}
}
public void Await()
{
lock(_lock)
{
while (votesLeft > 0)
Monitor.Wait(_lock);
if (runTask)
task.Run();
TaskCompleted?.Invoke(this, new TaskElectionEventArgs(runTask));
}
}
}
Implementing the above solution was as simple as creating the SingleVoteTask in the UI thread and then having each thread affecting the outcome cast a vote.
This question already has answers here:
does nulling a System.Threading.Timer stop it?
(4 answers)
Closed 3 years ago.
Please see example below. Even though the reference to obj is set to null, the obj is not released and Obj_Elapsed continues printing i. Notice there is no reference to the timer out of the scope of the ObjectWithTimer constructor.
public class Program
{
public static void Main(string[] args)
{
object obj = new ObjectWithTimer();
Console.ReadLine();
Console.WriteLine("obj=null");
obj = null;
Console.ReadLine();
}
}
public class ObjectWithTimer
{
private int i;
public System.Timers.Timer t;
public ObjectWithTimer()
{
t = new System.Timers.Timer(5000);
t.Elapsed += Obj_Elapsed;
t.Enabled = true;
}
public void Obj_Elapsed(object sender, ElapsedEventArgs e)
{
i++;
Console.WriteLine(i);
}
}
Setting null in this instance and/or going out of scope is not good enough, The Timer has resources it's managing and needs to be cleaned up.
Since System.Timers.Timer Implements IDisposable, ideally so should your wrapper class
public class ObjectWithTimer : IDisposable
{
// Flag: Has Dispose already been called?
private bool _disposed = false;
private int _i;
public System.Timers.Timer Timer { get; }
public ObjectWithTimer()
{
Timer = new System.Timers.Timer(5000);
Timer.Elapsed += Obj_Elapsed;
Timer.Enabled = true;
}
public void Obj_Elapsed(object sender, ElapsedEventArgs e)
{
_i++;
Console.WriteLine(_i);
}
// Public implementation of Dispose pattern callable by consumers.
public void Dispose() =>Dispose(true);
// Protected implementation of Dispose pattern.
protected virtual void Dispose(bool disposing)
{
if (_disposed) return;
if (disposing) Timer?.Dispose();
_disposed = true;
}
}
You should in turn then also dispose of the wrapper called at some stage, and not just set it null, the easiest way to do that is with the using statement
Provides a convenient syntax that ensures the correct use of
IDisposable objects.
public static void Main(string[] args)
{
using(object obj = new ObjectWithTimer())
{
Console.ReadLine();
Console.WriteLine("obj=null");
}
Console.ReadLine();
}
Implementing a Dispose method
You implement a Dispose method to release unmanaged resources used by
your application. The .NET garbage collector does not allocate or
release unmanaged memory.
Note : This wasn't a complete tutorial on the IDisposable pattern, just an example. Please do your own research and diligence on this implementation
Additional Resouces
Do you need to dispose of objects and set them to null?
Why do we need Dispose() method on some object? Why doesn't the garbage collector do this work?
All, I am monitoring my outlook inbox for some emails and parses that based on the content. I do this by running a console application and triggering a timer as shown below. The problem is this gets garbage collected after some time and I have to restart the app manually. I cannot run this inside a windows service as I get some permission issues while calling the Outlook api. Please see my code below
I tried doing a GC.SuppressFinalize(), GC.KeepAlive() on the timer object but no avail.
class Program
{
private static System.Timers.Timer _timer = new System.Timers.Timer();
static void Main(string[] args)
{
_timer.Interval = 10000;
_timer.Start();
_timer.Elapsed += Timer_Elapsed1;
Console.ReadLine();
}
}
private static void Timer_Elapsed1(object sender, System.Timers.ElapsedEventArgs e)
{
try
{
_timer.Stop();
AddNumbers(2, 2);
Console.WriteLine("The current time now is :{0}", DateTime.Now.ToString());
GC.SuppressFinalize(_timer);
_timer.Start();
}
catch (Exception ex)
{
_timer.Start();
Console.WriteLine("Timer restarted from exception");
}
finally
{
_timer.Start();
}
}
private static void AddNumbers(int x, int y)
{
var sum = x + y;
Console.WriteLine(sum);
}
In the scope of your example application your Timer is not being garbage collected so you must be getting another error. Also, please see my note and example below about GC.SuppressFinalize because what you're implying and what it does are two different things.
Loop instead of Timer
Just a suggestion: When using a Timer and the Elapsed event calls the timer to stop and then start again once complete this is a sign you need to have a simple loop running instead with a thread wait.
Since this is a console app and there are no other threads required I'll post a simple looping example that, IMO, would be more efficient and easier to manage.
using System;
using System.Threading;
namespace Question_Answer_Console_App
{
class Program
{
private const int SleepTimeMS = 10000;
static void Main(string[] args)
{
ThreadPool.QueueUserWorkItem(new WaitCallback((state) =>
{
while (true)
{
Thread.Sleep(SleepTimeMS);
AddNumbers(2, 2);
Console.WriteLine("The current time now is :{0}", DateTime.Now.ToString());
}
}));
Console.Read();
}
private static void AddNumbers(int x, int y)
{
var sum = x + y;
Console.WriteLine(sum);
}
}
}
GC.SuppressFinalize Information
Also, GC.SuppressFinalize does not have anything to do with garbage collection; it just tells the garbage collector, that when collected, not to run the finalizer on the object (the destructor method in C#). This is useful when there are items already disposed and you don't want to reproduce the work... For example if you're object is IDisposable, and you place the Dispose method in the destructor, and the user properly disposes of it when it is being used then you may want to skip the destructor. Here's an example of using GC.SuppressFinalize properly.
public class SomethingDisposable : IDisposable
{
public void Dispose()
{
//Dispose of some unmanaged resources or something.
GC.SuppressFinalize(this); //We can safely skip the Destructor method (Finalizer)
}
~SomethingDisposable() => Dispose(); //Just incase the object is garbage collected and never properly disposed.
}
GC.SuppressFinalize Example
And just for show here's a console app that illustrates the GC.SuppressFinalize in work. I make 2 IDisposable objects. The first I actually dispose of properly with a using statement and the second I don't. Once they are out of scope they are up for garbage collection. I intentionally call the garbage collector and wait for all finalizers to be called so that we can immediately see results.
Notice the disposed object DOES NOT call the finalizer and the object that was not disposed does (which in turn calls dispose... which is the proper way to implement it.)
using System;
namespace Question_Answer_Console_App
{
class Program
{
private const string DisposableTestId = "Disposable-Test";
private const string FinalizingTestId = "Finalizing-Test";
static void Main(string[] args)
{
TestDisposing();
Console.WriteLine();
TestFinalizing();
GC.Collect();
GC.WaitForPendingFinalizers();
Console.Read();
}
private static void TestDisposing()
{
using (var disposingTest = new TestSuppressFinalize(DisposableTestId))
PrintTesting(disposingTest);
}
private static void TestFinalizing()
{
var finalizingTest = new TestSuppressFinalize(FinalizingTestId);
PrintTesting(finalizingTest);
}
private static void PrintTesting(TestSuppressFinalize finalizingTest)
=> Console.WriteLine($"Testing {finalizingTest.TestId.ToString()}");
}
public class TestSuppressFinalize : IDisposable
{
public TestSuppressFinalize(string testId) => TestId = testId;
public string TestId { get; }
public void Dispose()
{
Console.WriteLine($"Disposed {TestId.ToString()}");
GC.SuppressFinalize(this);
}
~TestSuppressFinalize()
{
Console.WriteLine($"Finalized {TestId.ToString()}");
Dispose();
}
}
}
Output:
Testing Disposable-Test
Disposed Disposable-Test
Testing Finalizing-Test
Finalized Finalizing-Test
Disposed Finalizing-Test
In my project I use a manager to control a plugin. The main idea is that this plugin must work only in single thread in multythreads WPF application. There is only one instance of plugin in PluginController.
So when I call Start method: it stops plugin (if running) and start it with new argument. Few times a second plugin notificate caller about it's state, and ViewModel shows it in the WPF window.
When I call method Start some times one after one, i see that the previous instance of ViewModel is not destroyed, but only sleeps after Stop. And it calls Update method as god as a new one instance. So my interface twiches becouse two instances are updating it's state. In log is see alternately lines from first one and second one.
But when I call Start(...) then Stop() and then Start(...) again everything works fine.
So
SomeManager.Start(...);
SomeManager.Start(...);
works with errors. And
SomeManager.Start(...);
SomeManager.Stop();
SomeManager.Start(...);
works fine. Can anybody explain me my mistake?
Down lied simplified code.
public static SomeManager
{
public static void Start(SomeArg arg)
{
Stop(); // forgotten code
var vm = GetMainPageVM();
vm.SomeVM = new SomeViewModel(arg);
vm.SomeVM.StartCommand.Execute(null);
}
public static void Stop()
{
var vm = GetMainPageVM();
if (vm.SomeVM != null)
{
vm.SomeVM.Stop();
vm.SomeVM.Dispose();
vm.SomeVM = null;
}
}
}
public sealed SomeViewModel : ViewModelBase, IDisposable
{
private readonly Guid _guid = Guid.NewGuid();
private IPlugin _plugin;
private SomeArg _arg;
public ICommand StartCommand {get; }
public CancellationTokenSource Source {get; }
public SomeViewModel(SomeArg arg)
{
this._arg = arg;
this._plugin = PluginController.GetPluginByName("SomePlugin");
StartCommand = new RelayCommand(StartAsync);
}
~SomeViewModel()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{ ... }
private async Task StartAsync()
{
var progress = new Progress<ISomeProgress>(Update);
try
{
await StartImplementationAsync(progress).ConfigureAwait(false);
}
catch (Exception e) { ... }
}
private async Task StartImplementationAsync(Progress<ISomeProgress> progress)
{
var result = await this._plugin.startAsync(
this._arg,
progress,
this.Source.Token
).ConfigureAwait(false);
}
public void Stop()
{
this._plugin.Stop();
}
private void Update() {log.Debug($"this._guid" ....); }
}
public sealed SomePlugin: IPlugin
{
public async Task<SomeResult> StartAsync(SomeArg args, IProgress<SomeProgress>, CancellationToken cancellationToken)
{ ... }
public void Stop() { ... }
}
UPDATE: I think the problem in simple words is : how to correctly cancel async operation in IDisposable object in normal case with CancellationTokenSource.Cancel() and in unnormal case when Dispose() or Finalizer is called
If you profile a simple client application that uses SocketAsyncEventArgs, you will notice Thread and ExecutionContext allocations.
The source of the allocations is SocketAsyncEventArgs.StartOperationCommon that creates a copy of the ExecutionContext with ExecutionContext.CreateCopy().
ExecutionContext.SuppressFlow seems like a good way to suppress this allocation. However this method itself will generate allocations when ran in a new thread.
How can I avoid these allocations?
SocketAsyncEventArgs
public class SocketAsyncEventArgs : EventArgs, IDisposable {
//...
// Method called to prepare for a native async socket call.
// This method performs the tasks common to all socket operations.
internal void StartOperationCommon(Socket socket) {
//...
// Prepare execution context for callback.
if (ExecutionContext.IsFlowSuppressed()) {
// This condition is what you need to pass.
// Fast path for when flow is suppressed.
m_Context = null;
m_ContextCopy = null;
} else {
// Flow is not suppressed.
//...
// If there is an execution context we need
//a fresh copy for each completion.
if(m_Context != null) {
m_ContextCopy = m_Context.CreateCopy();
}
}
// Remember current socket.
m_CurrentSocket = socket;
}
[Pure]
public static bool IsFlowSuppressed()
{
return Thread.CurrentThread.GetExecutionContextReader().IsFlowSuppressed;
}
//...
}
ExecutionContext
[Serializable]
public sealed class ExecutionContext : IDisposable, ISerializable
{
//...
// Misc state variables.
private ExecutionContext m_Context;
private ExecutionContext m_ContextCopy;
private ContextCallback m_ExecutionCallback;
//...
internal struct Reader
{
ExecutionContext m_ec;
//...
public bool IsFlowSuppressed
{
#if !FEATURE_CORECLR
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
get { return IsNull ? false : m_ec.isFlowSuppressed; }
}
} //end of Reader
internal bool isFlowSuppressed
{
get
{
return (_flags & Flags.IsFlowSuppressed) != Flags.None;
}
set
{
Contract.Assert(!IsPreAllocatedDefault);
if (value)
_flags |= Flags.IsFlowSuppressed;
else
_flags &= ~Flags.IsFlowSuppressed;
}
}
[System.Security.SecurityCritical] // auto-generated_required
public static AsyncFlowControl SuppressFlow()
{
if (IsFlowSuppressed())
{
throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_CannotSupressFlowMultipleTimes"));
}
Contract.EndContractBlock();
AsyncFlowControl afc = new AsyncFlowControl();
afc.Setup();
return afc;
}
//...
}//end of ExecutionContext.
AsyncFlowControl
public struct AsyncFlowControl: IDisposable
{
private bool useEC;
private ExecutionContext _ec;
//...
[SecurityCritical]
internal void Setup()
{
useEC = true;
Thread currentThread = Thread.CurrentThread;
_ec = currentThread.GetMutableExecutionContext();
_ec.isFlowSuppressed = true;
_thread = currentThread;
}
}
Thread
// deliberately not [serializable]
[ClassInterface(ClassInterfaceType.None)]
[ComDefaultInterface(typeof(_Thread))]
[System.Runtime.InteropServices.ComVisible(true)]
public sealed class Thread : CriticalFinalizerObject, _Thread
{
//...
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
internal ExecutionContext.Reader GetExecutionContextReader()
{
return new ExecutionContext.Reader(m_ExecutionContext);
}
}
The only way to set isFlowSuppressed to true, to pass the condition in the StartOperationCommon method, is by calling Setup method, and the only call to Setup is in SuppressFlow method, wich you have discussed.
As you can see, SuppressFlow is the only solution.
Actually, SuppressFlow doesn't allocate. It returns a AsyncFlowControl, which is a struct. The proper solution basically is to call SendAsync and ReceiveAsync as follows:
public static bool SendAsyncSuppressFlow(this Socket self, SocketAsyncEventArgs e)
{
var control = ExecutionContext.SuppressFlow();
try
{
return self.SendAsync(e);
}
finally
{
control.Undo();
}
}
public static bool ReceiveAsyncSuppressFlow(this Socket self, SocketAsyncEventArgs e)
{
var control = ExecutionContext.SuppressFlow();
try
{
return self.ReceiveAsync(e);
}
finally
{
control.Undo();
}
}
I created these extension methods to make this a bit simpler and more explicit.
Traces with dotMemory showed that memory allocations really do go down to zero.