Cleaning up CallContext in TPL - c#

Depending on whether I'm using async/await based code or TPL based code, I'm getting two different behaviors regarding the clean-up of logical CallContext.
I can set and clear logical CallContext exactly as I expect if I use the following async/await code:
class Program
{
static async Task DoSomething()
{
CallContext.LogicalSetData("hello", "world");
await Task.Run(() =>
Debug.WriteLine(new
{
Place = "Task.Run",
Id = Thread.CurrentThread.ManagedThreadId,
Msg = CallContext.LogicalGetData("hello")
}))
.ContinueWith((t) =>
CallContext.FreeNamedDataSlot("hello")
);
return;
}
static void Main(string[] args)
{
DoSomething().Wait();
Debug.WriteLine(new
{
Place = "Main",
Id = Thread.CurrentThread.ManagedThreadId,
Msg = CallContext.LogicalGetData("hello")
});
}
}
The above outputs the following:
{ Place = Task.Run, Id = 9, Msg = world }
{ Place = Main, Id = 8, Msg = }
Notice the Msg = which indicates that CallContext on the main thread has been freed and is empty.
But when I switch to pure TPL / TAP code I can't achieve the same effect...
class Program
{
static Task DoSomething()
{
CallContext.LogicalSetData("hello", "world");
var result = Task.Run(() =>
Debug.WriteLine(new
{
Place = "Task.Run",
Id = Thread.CurrentThread.ManagedThreadId,
Msg = CallContext.LogicalGetData("hello")
}))
.ContinueWith((t) =>
CallContext.FreeNamedDataSlot("hello")
);
return result;
}
static void Main(string[] args)
{
DoSomething().Wait();
Debug.WriteLine(new
{
Place = "Main",
Id = Thread.CurrentThread.ManagedThreadId,
Msg = CallContext.LogicalGetData("hello")
});
}
}
The above outputs the following:
{ Place = Task.Run, Id = 10, Msg = world }
{ Place = Main, Id = 9, Msg = world }
Is there anything I can do to coerce TPL to "free" the logical CallContext the same way as the async/await code does?
I am not interested in alternatives to CallContext.
I'm hoping to get the above TPL/TAP code fixed so that I can use it in projects targeting the .net 4.0 framework. If that is not possible in .net 4.0, I'm still curious if it can be done in .net 4.5.

In an async method the CallContext is copied on write:
When an async method starts, it notifies its logical call context to activate copy-on-write behavior. This means the current logical call context is not actually changed, but it is marked so that if your code does call CallContext.LogicalSetData, the logical call context data is copied into a new current logical call context before it is changed.
From Implicit Async Context ("AsyncLocal")
That means that in your async version the CallContext.FreeNamedDataSlot("hello") continuation is redundant as even without it:
static async Task DoSomething()
{
CallContext.LogicalSetData("hello", "world");
await Task.Run(() =>
Console.WriteLine(new
{
Place = "Task.Run",
Id = Thread.CurrentThread.ManagedThreadId,
Msg = CallContext.LogicalGetData("hello")
}));
}
The CallContext in Main wouldn't contain the "hello" slot:
{ Place = Task.Run, Id = 3, Msg = world }
{ Place = Main, Id = 1, Msg = }
In the TPL equivalent all code outside the Task.Run (which should be Task.Factory.StartNew as Task.Run was added in .Net 4.5) runs on the same thread with the same exact CallContext. If you want to clean it you need to do that on that context (and not in the continuation):
static Task DoSomething()
{
CallContext.LogicalSetData("hello", "world");
var result = Task.Factory.StartNew(() =>
Debug.WriteLine(new
{
Place = "Task.Run",
Id = Thread.CurrentThread.ManagedThreadId,
Msg = CallContext.LogicalGetData("hello")
}));
CallContext.FreeNamedDataSlot("hello");
return result;
}
You can even abstract a scope out of it to make sure you always clean up after yourself:
static Task DoSomething()
{
using (CallContextScope.Start("hello", "world"))
{
return Task.Factory.StartNew(() =>
Debug.WriteLine(new
{
Place = "Task.Run",
Id = Thread.CurrentThread.ManagedThreadId,
Msg = CallContext.LogicalGetData("hello")
}));
}
}
Using:
public static class CallContextScope
{
public static IDisposable Start(string name, object data)
{
CallContext.LogicalSetData(name, data);
return new Cleaner(name);
}
private class Cleaner : IDisposable
{
private readonly string _name;
private bool _isDisposed;
public Cleaner(string name)
{
_name = name;
}
public void Dispose()
{
if (_isDisposed)
{
return;
}
CallContext.FreeNamedDataSlot(_name);
_isDisposed = true;
}
}
}

A good question. The await version may not work the way you may think it does here. Let's add another logging line inside DoSomething:
class Program
{
static async Task DoSomething()
{
CallContext.LogicalSetData("hello", "world");
await Task.Run(() =>
Debug.WriteLine(new
{
Place = "Task.Run",
Id = Thread.CurrentThread.ManagedThreadId,
Msg = CallContext.LogicalGetData("hello")
}))
.ContinueWith((t) =>
CallContext.FreeNamedDataSlot("hello")
);
Debug.WriteLine(new
{
Place = "after await",
Id = Thread.CurrentThread.ManagedThreadId,
Msg = CallContext.LogicalGetData("hello")
});
}
static void Main(string[] args)
{
DoSomething().Wait();
Debug.WriteLine(new
{
Place = "Main",
Id = Thread.CurrentThread.ManagedThreadId,
Msg = CallContext.LogicalGetData("hello")
});
Console.ReadLine();
}
}
Output:
{ Place = Task.Run, Id = 10, Msg = world }
{ Place = after await, Id = 11, Msg = world }
{ Place = Main, Id = 9, Msg = }
Note the "world" is still there after await, because it was there before await. And it is not there after DoSomething().Wait() because it wasn't there before it, in the first place.
Interestingly enough, the async version of DoSomething creates a copy-on-write clone of the LogicalCallContext for its scope, upon the first LogicalSetData. It does that even when there is no asynchrony inside it - try await Task.FromResult(0). I presume the whole ExecutionContext gets cloned for the scope of the async method, upon the 1st write operation.
OTOH, for the non-async version there is no "logical" scope and no outer ExecutionContext here, so the copy-on-write clone of ExecutionContext becomes current for the Main thread (but the continuations and the Task.Run lambdas still get their own clones). So, you'd either need to move CallContext.LogicalSetData("hello", "world") inside the Task.Run lambda, or clone the context manually:
static Task DoSomething()
{
var ec = ExecutionContext.Capture();
Task task = null;
ExecutionContext.Run(ec, _ =>
{
CallContext.LogicalSetData("hello", "world");
var result = Task.Run(() =>
Debug.WriteLine(new
{
Place = "Task.Run",
Id = Thread.CurrentThread.ManagedThreadId,
Msg = CallContext.LogicalGetData("hello")
}))
.ContinueWith((t) =>
CallContext.FreeNamedDataSlot("hello")
);
task = result;
}, null);
return task;
}

Related

Why does JsonConvert.SerializeObject fail when called from a nested task?

I have a console program which starts a long running task, which in turn starts 4 x long running tasks. By long running I mean all these tasks are supposed to run indefinitely.
However, calling JsonConvert.Serialize from any of the nested tasks crashes the app silently, without any error messages.
I've recreated this behaviour with a simple console program:
public class TestClass
{
public string ATestProperty { get; set; }
}
internal class Program
{
static void Main(string[] args)
{
var outerTask = Task.Factory.StartNew(() =>
{
var nestedTask = Task.Factory.StartNew(() =>
{
Console.WriteLine("Inside nestedTask");
var testClass = new TestClass { ATestProperty = "Hi from test class" };
var serialisedClass = JsonConvert.SerializeObject(testClass);
Console.WriteLine(serialisedClass);
});
});
Console.WriteLine("DONE");
}
}
This prints
DONE
Inside nestedTask
If I debug and step through the code, the program terminates right after executing
var serialisedClass = JsonConvert.SerializeObject(testClass);
I'm not sure what I'm missing, perhaps it is a combination of a task + serialisation behaviour that I'm not aware of. I've tried System.Text.Json to serialise my data too with the same results.
You can add Task.WaitAll(Task task) after each block of nested task.
Example:
var outerTask = Task.Factory.StartNew(() =>
{
var nestedTask = Task.Factory.StartNew(() =>
{
Console.WriteLine("Inside nestedTask");
var testClass = new TestClass { ATestProperty = "Hi from test class" };
var serialisedClass = JsonConvert.SerializeObject(testClass);
Console.WriteLine(serialisedClass);
});
Task.WaitAll(nestedTask);
});
Task.WaitAll(outerTask);
Console.WriteLine("DONE");

How to track Task's status with extension method?

I am new to asynchronous programming. Trying to create correct extension method which can print status of task if it has changed. But i dont know how to do it. Thats what i have now:
static class MyAsync
{
static void Main()
{
Task t = MyAsync.PrintCountPrimesAsync(35);
t.Tracking();
Thread.Sleep(1000);
}
public static async Task PrintCountPrimesAsync(int n) =>
Console.WriteLine($"CountOfPrimes = { await CustomMath.GetPrimesCountAsync(100000, 100000)}");
public static async Task Tracking(this Task task)
{
await Task.Run(() =>
{
TaskStatus current = task.Status;
while (!task.IsCompleted)
{
if (current != task.Status)
{
Console.WriteLine(task.Status);
current = task.Status;
}
}
});
}
}
class CustomMath
{
public static Task<int> GetPrimesCountAsync(int start, int count)
{
return Task.Run(() =>
ParallelEnumerable.Range(start, count).Count(n =>
Enumerable.Range(2, (int)Math.Sqrt(n) - 1).All(i => n % i > 0)));
}
}
The ideal answer to this is "don't", but if you absolutely must, ContinueWith acts as a callback that might be suitable here:
public static void Tracking(this Task task)
=> _ = task.ContinueWith(static x => Console.WriteLine(x.Status));
This only tracks completion (with or without fault), but: that's pretty much the only interesting and reliable state transition anyway.
I linked the SO post that says it's unpractical to do that.
So, if you really need this, this is something you could use:
static void Main(string[] args)
{
var t = GetPrimes();
while (t.Status != TaskStatus.RanToCompletion) ;
}
But if do it like this specifically, it's going to do the same job as await opreator would do, but this blocks the thread.
So basically you would need to have some loop watching over tasks' statuses. You could add some Task.Delays no to block the thread.
But still, I would not recommend going that path.
I'd try to tackle this with a callback.
Try this:
class CustomMath
{
public static Task<int> GetPrimesCountAsync(int start, int count, Action<TaskStatus> track)
{
Task<int> task = null;
task = Task.Run(() =>
ParallelEnumerable.Range(start, count).Count(n =>
{
track(task.Status);
return Enumerable.Range(2, (int)Math.Sqrt(n) - 1).All(i => n % i > 0);
}));
return task;
}
}
I'm using the Action<TaskStatus> track and injecting it in to the calculation. It then allows the calculation to control when to report back the status.
Now you can run this code:
async Task Main()
{
var x = await CustomMath.GetPrimesCountAsync(1, 5, ts => Console.WriteLine(ts.ToString()));
Console.WriteLine(x);
}
The output I get is:
Running
Running
Running
Running
Running
4

Async seems to be synchronous

I am not sure if I am missing something here but more for loop seems to be executing synchronously even though I await all tasks out side of it.
Here is my code below:
static void Main(string[] args) {
var t = Start();
}
public static async Task < List < Task < TaskInfo >>> Start() {
var listOfTasks = new List < Task < TaskInfo >> ();
for (var i = 0; i <= 100; i++) {
var process = new Processor();
listOfTasks.Add(process.Process(i));
}
await Task.WhenAll(listOfTasks);
return listOfTasks;
}
I pass in the taskId to log out just to see the order the tasks execute.
Am I missing something really obvious here?
EDIT:
Changed code to this based on the answers and comments below and it still appears synchronously:
public class StartWork
{
public int TaskId { get; set; }
public Processor Processor { get;}
public StartWork()
{
Processor = new Processor();
}
}
static void Main(string[] args)
{
var t = Start();
}
public static async Task<TaskInfo[]> Start()
{
var tasks = new List<StartWork>();
for (int i = 1; i < 100; i++)
{
var work = new StartWork
{
TaskId = i
};
tasks.Add(work);
}
return await Task.WhenAll(tasks.Select(i => i.Processor.Process(i.TaskId)));
}
The function I am calling in processor class:
public Task<TaskInfo> Process(int taskId)
{
try
{
taskId = taskId + 1;
stopwatch.Start();
using (var bus = RabbitHutch.CreateBus(xxDev))
{
#event = new AutoResetEvent(false);
var replyTo = Guid.NewGuid().ToString();
var messageQueue = bus.Advanced.QueueDeclare(replyTo, autoDelete: true);
bus.Advanced.Consume(messageQueue, (payload, properties, info) =>
{
ReceivePdf(payload, properties, info);
return Task.FromResult(0);
});
taskInfo.InputFile = inputFile;
var html = File.ReadAllText(inputFile);
taskInfo.Html = html;
var message = PrepareMessage(new RenderRequest()
{
Html = Encoding.UTF8.GetBytes(html),
Options = new RenderRequestOptions()
{
PageSize = "A4",
ImageQuality = 70,
PageLoadRetryAttempts = 3
}
});
var correlation = Guid.NewGuid().ToString();
Console.WriteLine($"CorrelationId: {correlation}, TaskId {taskId}");
var props = new MessageProperties
{
CorrelationId = correlation,
ReplyTo = replyTo,
Expiration = "6000"
};
Publish(bus, props, message);
taskInfo.CorrelationId = Guid.Parse(correlation);
#event.WaitOne();
stopwatch.Stop();
taskInfo.TimeTaken = stopwatch.Elapsed;
return Task.FromResult(taskInfo);
}
}
catch (Exception e)
{
taskInfo.OutputFile = Empty;
return Task.FromResult(taskInfo);
}
}
void ReceivePdf(byte[] payload, MessageProperties properties, MessageReceivedInfo info)
{
var file = Format(outputFile, properties.CorrelationId);
taskInfo.OutputFile = file;
Console.WriteLine("Output written to " + file);
File.WriteAllBytes(file, payload);
var remaining = Interlocked.Decrement(ref outstandingRequests);
if (remaining == 0)
{
#event.Set();
}
}
This is a synchronous task
listOfTasks.Add(process.Process(i));
You are just adding items to the list.
This is also a synchronous task
process.Process(i);
The task that the function above returns is asynchronous and it will execute asynchronously in the whenAll call of your code.
Bear in mind that when all will wait for all tasks to run and if the task is trivial, since they start one after the other, will most times run sequentially by chance.
You will see some difference if the task code executed differentiated in execution time based on input.
First async doesn't mean multithread, async is used to run background task without blocking UI or to run I/O operations without bloking main thread.
Usually the operating system handles async with multithreading, but there is no guarantee.
If you want be sure to start multiple threads use Thread.Start.
Anyway in your code you force your code to run synchronously, because you call the async method start in the Main method without await.
You need to change the code to:
static async void Main(string[] args)
{
var t = await Start();
}
or without waiting to (but the program risk to terminate before the task complete):
static void Main(string[] args)
{
Task.Run(async () => {
var t = await Start();
});
}

Wait for a deferred value in a lambda before returning it in a function

I have a function call (in a SignalR hub) that is supposed to return a MyState object.
The MyState object is not quite ready at the time it is needed by the caller and has to be processed first by some background process prior being eligible as being ready to be sent.
That's why I want the action that retrieves the state to be queued in a stack to be processed later. This is part of an existing framework.
I am not good at all with the synchronization methods of the .NET framework, and everything I looked for is in JavaScript. So I would need a bit help to fill what is missing here:
public MyState GetState() {
MyState state;
// var waitMe = someObjectThatCanWait(); ?
actions.Enqueue(sync => {
// This lambda will be executed later by a function that processes the actions queue
state = sync.GetSyncState());
// waitMe.NowYouCanGo() ?
});
// waitMe.Wait() ?
return state;
}
EDIT I have something that works. It is a bit obscure to me I wonder
whether this is ok.
public MyState GetState() {
MyState state;
var waitMe = new Barrier(2);
actions.Enqueue(sync => {
// This lambda will be executed later by a function that processes the actions queue
state = sync.GetSyncState());
waitMe.SignalAndWait();
});
waitMe.SignalAndWait();
return state;
}
EDIT2 I have something more suitable since it does not block the lambda
and the function at the same time
public MyState GetState() {
MyState state;
var waitMe = new ManualResetEventSlim();
actions.Enqueue(sync => {
// This lambda will be executed later by a function that processes the actions queue
state = sync.GetSyncState());
waitMe.Set();
});
waitMe.Wait(1500);
return state;
}
Here is an example of similar workflow using TaskCompletionSource:
class Program
{
private static readonly ConcurrentQueue<(string Data, TaskCompletionSource<string> Tcs)> Queue = new ConcurrentQueue<(string Data, TaskCompletionSource<string> Tcs)>();
static void Main()
{
var cts = new CancellationTokenSource();
Task.Run(() => ProcessQueue(cts.Token), cts.Token);
ListenForTasks().GetAwaiter().GetResult();
cts.Cancel();
}
private static async Task ListenForTasks()
{
while (true)
{
var input = Console.ReadLine();
if (input == "q")
{
break;
}
var tcs = new TaskCompletionSource<string>();
Queue.Enqueue((input, tcs));
Console.WriteLine("waiting...");
var result = await tcs.Task;
Console.WriteLine($"waited: {result}");
}
}
private static void ProcessQueue(CancellationToken ct)
{
while (true)
{
ct.ThrowIfCancellationRequested();
if (!Queue.TryDequeue(out var item))
{
continue;
}
//emulate work
Thread.Sleep(1000);
Console.WriteLine($"processed {item.Data}");
item.Tcs.SetResult(item.Data + "_PROCESSED");
}
}
}

Wrapping a Mutex with IDisposable and testing it but the test never ends

I'm trying to wrap a Mutex with an IDisposable class like this:
public class NamedMutex : IDisposable
{
private static readonly object _syncLock = new object();
private readonly Mutex _namedMutex;
private readonly bool _createdNew;
public NamedMutex(string name)
{
if (string.IsNullOrEmpty(name)) throw new ArgumentNullException("name");
//lock (_syncLock)
{
_namedMutex = new Mutex(initiallyOwned: false, name: name, createdNew: out _createdNew);
}
_namedMutex.WaitOne();
}
public void Dispose()
{
//lock (_syncLock)
{
//if (_createdNew)
_namedMutex.ReleaseMutex();
_namedMutex.Dispose();
}
}
}
as you can see from the commented out code I've tried pretty much everything I could think of to make it work but either is my test wrong or something's not right with the above implementation because the test either never ends (probably a dead-lock that I am not able to identify or it crashes with the unsynchronized exception).
This is my test that I adapted for LINQPad:
void Main()
{
var sw = Stopwatch.StartNew();
var task1 = Task.Run(async () =>
{
using (new NamedMutex("foo"))
{
Console.WriteLine(3);
await Task.Delay(TimeSpan.FromSeconds(3));
}
});
var task2 = Task.Run(async () =>
{
using (new NamedMutex("foo"))
{
Console.WriteLine(2);
await Task.Delay(TimeSpan.FromSeconds(2));
}
});
Task.WaitAll(task1, task2);
//Assert.IsTrue(sw.Elapsed.TotalSeconds >= 5);
sw.Elapsed.Dump(); // LINQPad
}
This happens because of await. After your await Task.Delay(..) you might no longer be on the same thread you were before await statement. So in some cases you are trying to release your mutex from the thread which does not own it - hence your problem. That's easy to verify by writing current thread before and after await:
class Program {
public static void Main() {
while (true) {
var sw = Stopwatch.StartNew();
var task1 = Task.Run(async () => {
using (new NamedMutex("foo")) {
Console.WriteLine("first before await: " + Thread.CurrentThread.ManagedThreadId);
await Task.Delay(TimeSpan.FromSeconds(2));
Console.WriteLine("first after await: " + Thread.CurrentThread.ManagedThreadId);
}
});
var task2 = Task.Run(async () => {
using (new NamedMutex("foo")) {
Console.WriteLine("second before await: " + Thread.CurrentThread.ManagedThreadId);
await Task.Delay(TimeSpan.FromSeconds(1));
Console.WriteLine("second after await: " + Thread.CurrentThread.ManagedThreadId);
}
});
Task.WaitAll(task1, task2);
//Assert.IsTrue(sw.Elapsed.TotalSeconds >= 5);
Console.WriteLine(sw.Elapsed);
}
}
}
To expand on Evk's answer, and to get to a workaround, it is still possible to wrap a Mutex with an IDisposable. You just have to make sure that you have complete control over the Thread that is acquiring the Mutex and releasing it, and you have to make sure that the context doesn't switch in that thread between acquiring and releasing the mutex.
So just spin up your own thread. Something like:
class NamedMutex : IDisposable
{
private readonly Thread _thread;
private readonly ManualResetEventSlim _disposalGate;
private readonly Mutex _namedMutex;
public NamedMutex(string name)
{
var constructorGate = new ManualResetEventSlim();
_disposalGate = new ManualResetEventSlim();
_thread = new Thread(() =>
{
// Code here to acquire the mutex
_namedMutex = new Mutex(initiallyOwned: false, name: name, createdNew: out _createdNew);
constructorGate.Set(); // Tell the constructor it can go on
_disposalGate.Wait(); // Wait for .Dispose to be called
// Code here to release the mutex
_namedMutex.ReleaseMutex();
_namedMutex.Dispose();
});
_thread.Start();
constructorGate.Wait();
}
public void Dispose()
{
_disposalGate.Set();
}
}

Categories