What could cause ContinuationTaskFromResultTask`1[TAntecedentResult].InnerInvoke() to throw a NullReferenceException? [duplicate] - c#

For the following piece of code (.NET v4.0.30319) I am getting a null reference exception indicated below in the second continuation.
Most interestingly this issue has only occurred in machines with 8GB RAM but other users have 16GB and more and they haven't reported any issue, and it is a very intermittent issue which leads me to suspect a garbage collection issue.
The GetData() can be called multiple times so the first continuation of _businessObjectTask will only be called once as _businessObjects will have been populated from that point forward.
I imagine the Object reference not set to an instance of an object exception is being thrown because either
_businessObjectTask is null and it can't continue from a null task.
items variable which is passed in as a parameter is null somehow
The line number in my log file (748) points to the one highlighted below and not the lambda expression which points to #1 above instead of #2. I've played around in Visual Studio and each of the lines following businessObjectTask.ContinueWith() are considered different i.e. if it was a null reference within the lambda expression it would give a different line number to 748
Any help would be greatly appreciated.
Edit:
This isn't related to What is a NullReferenceException, and how do I fix it? because that is a much more basic explanation of null reference whereas this is much more complicated and subtle.
Exception
Full details of the stack trace (edited for simplicity with dummy class and namespace names)
Object reference not set to an instance of an object.
at ApplicationNamespace.ClassName`1.<>c__DisplayClass4e.<GetData>b__44(Task`1 t) in e:\ClassName.cs:line 748
at System.Threading.Tasks.ContinuationTaskFromResultTask`1.InnerInvoke()
at System.Threading.Tasks.Task.Execute()
Code
private static IDictionary<string, IBusinessObject> _businessObjects;
private Task<IDictionary<string, IBusinessObject>> _businessObjectTask;
public Task GetData(IList<TBusinessItem> items))
{
Log.Info("Starting GetData()");
if (_businessObjects == null)
{
var businessObjectService = ServiceLocator.Current.GetInstance<IBusinessObjectService>();
_businessObjectTask = businessObjectService.GetData(CancellationToken.None)
.ContinueWith
(
t =>
{
_businessObjects = t.Result.ToDictionary(e => e.ItemId);
return _businessObjects;
},
CancellationToken.None,
TaskContinuationOptions.OnlyOnRanToCompletion,
TaskScheduler.Current
);
}
var taskSetLEData = _businessObjectTask.ContinueWith // Line 748 in my code - "Object reference not set to an instance of an object." thrown here
(
task =>
{
items.ToList().ForEach
(
item =>
{
IBusinessObject businessObject;
_businessObjects.TryGetValue(item.Id, out businessObject);
item.BusinessObject = businessObject;
}
);
},
CancellationToken.None,
TaskContinuationOptions.OnlyOnRanToCompletion,
TaskScheduler.Default
);
}
Resolution:
So having used what I've learned from this question I went back to the original code and figured it all out.
Turns out the reason for this NRE was because the _businessObjectTask is non-static where as the _businessObjects is static.
This means that _businessObjects is null the first time GetData() is called which then sets _businessObjectTask to non-null. Then when _businessObjectTask.ContinueWith gets called it is non-null and continues without problem.
However if a second instance of this class above is instantiated, _businessObjects has already been populated so _businessObjectTask remains null. Then when _businessObjectTask.ContinueWith gets called, a NRE on _businessObjectTask is thrown.
There were a couple of options I could have taken but I ended up removing the _businessObjectTask to a synchronous method call which means that I dont need to use the continuation anymore and I set _businessObjects once.

This is a synchronization problem.
You are assuming that _businessObjectTask is always assigned before _businessObjects.
That is, however, not guaranteed. It is possible that your continuation that assign _businessObjects is executed inline and therefore before businessObjectService.GetData(...).ContinueWith(...) returns.
// This assignment could happend AFTER the inner assignment.
_businessObjectTask = businessObjectService.GetData(CancellationToken.None)
.ContinueWith
(
t =>
{
// This assignment could happen BEFORE the outer assignment.
_businessObjects = t.Result.ToDictionary(e => e.ItemId);
Therefore, it is possible that _businessObjects is not null although _businessObjectTask is null.
If a concurrent thread would enter your GetData method at that time it would clearly not enter
if (_businessObjects == null) // not entered because it's not null
{
...
}
...and instead continue with
var taskSetLEData = _businessObjectTask.ContinueWith // line 748
...which will cause a null reference exception since _businessObjectTask is null.
Here's how you could simplify your code and resolve this synchronization problem:
private Lazy<Task<IDictionary<string, IBusinessObject>>> _lazyTask =
new Lazy<Task<IDictionary<string, IBusinessObject>>>(FetchBusinessObjects);
private static async Task<IDictionary<string, IBusinessObject>> FetchBusinessObjects()
{
var businessObjectService = ServiceLocator.Current.GetInstance<IBusinessObjectService>();
return await businessObjectService.GetData(CancellationToken.None).ToDictionary(e => e.ItemId);
}
public async Task GetData(IList<TBusinessItem> items)
{
Log.Info("Starting GetData()");
var businessObjects = await _lazyTask.Value;
items.ToList().ForEach
(
item =>
{
IBusinessObject businessObject;
businessObjects.TryGetValue(item.Id, out businessObject);
item.BusinessObject = businessObject;
}
);
}
Notes:
Using Lazy<T> to ensure that the business object service is only invoked once (per instance of this class, whatever it is).
Using async/await to simplify code.
You may want to consider declaring _lazyTask as static. In your code there seem to be a mixup between static/non-static fields. I cannot know which is right for you.

Related

Activator.CreateInstance works in one method but throws AmbiguousMatchException in another

I have two generic methods in the same class, and each uses exactly the same code to create instances. One works, the other throws an AmbiguousMatchException. Here is the code:
private static Dictionary<Type, AccessBase> _dictionary;
public static T Access<T>(Type type) where T : AccessBase
{
T instantiated;
if (_dictionary.ContainsKey(type))
{
instantiated = _dictionary[type] as T;
}
else
{
instantiated = (T)Activator.CreateInstance(type, _manager); //<- Works!
_dictionary.Add(type, instantiated);
}
return instantiated;
}
public static void RegisterAccess<T>(Type type) where T : AccessBase
{
if (type == null)
{
throw new ArgumentNullException(nameof(type));
}
if (_dictionary.ContainsKey(type))
{
return;
}
var instantiated = (T)Activator.CreateInstance(type, _manager); //<- Fails!
if (instantiated == null)
{
throw new ArgumentException($"{nameof(type)} cannot be registered");
}
_dictionary.Add(type, instantiated);
}
I would welcome any suggestions as to why and what to do about it... I have been tearing what is left of my hair out over this one!
Thanks to all of you for your input. I finally found the problem, which as it turns out is quite simple actually. At the point of failure the value of the _manager field is null... As soon as it is no longer null, it works.
To trap this I tried calling the first method at the same time as the second, and both failed. Tracking it through, I determined that the cause was the null value, since if you do it later in the process it works fine.
The original purpose of the code was to pre-register the access classes so that when they are needed they are ready to go and avoid the constant 'new' calls to generate these classes as they are needed. Given that it is now clear that this cannot happen at that point, the question is whether this is required at all. If we do it at a later point, there is not much reason to do it as it will get repeated every time a database is opened with each one not being added as it already exists.
I think we will end up abandoning the process that uses the second method, the pre-registration, in favour of the first method which is already used anyway and which automatically adds the instantiated classes only when they are needed and which already works fine at the later point.

Instantiated Object by a Lambda on the Thread is always resolved to null when compared

In the following example, _instance is always resolving to null. How should I be getting the value safely out of the thread?
class GenericThreadManager<TMyClass> where TMyClass : new()
{
private TMyClass _instance;
private object synLock = new object();
private Thread generalThread;
public GenericThreadManager()
{
//wrapped code
lock (this.synLock)
{
generalThread = new Thread(_instance => this._instance = new TMyClass());
generalThread.Start(); // instantiates the object
while (this._instance == null) // allways compares to null even after
// thread completes
{
Thread.Sleep(100);
}
Thread.Sleep(100);
}
//wrapped code
}
Edits based on comments (thank you for the comments)
The while loop after the thread is just to debug / test if the thread is ever completing.
The context – is that this class instantiates an object that takes a long time to build (i.e. loading lots of data) on a thread. This class then governs access to the object until the instantiation is complete. Crudely I was doing this by checking if the object was null.
Although there might be better ways to solve the problem – I would like to understand where I’ve gone wrong in my understanding of threaded code / and Lambda’s.
Crudely – I understood generalThread = new Thread(_instance => this._instance = new TMyClass()); to be setting the threadstart to the Lambda expression:
_instance => this._instance = new TMyClass()
I understand this expression creates a new instance of TMyClass() and place reference to that object in _instance.
I then understood that the threads my not be synchronised, so the value of _instance wouldn’t be reliably set – unless it was set to volatile.
With it set to volatile I get the following compile error:
Error CS0677 'GenericThreadManager<TMyClass>._instance': a volatile field cannot be of the type 'TMyClass'
How do I resolve the above error or synchronise the threads to move the reference to the object to _instance.
# Panagiotis Kanavos could you explain why the each constructor would see a different lock. The GenericThreadManager is making a private instance of TMyClass, a different object, in the GenericThreadManager constructor. It locking on synLock, so in theory, any other methods on GenericThreadManager should block on the lock (this.synLock) if I understand correctly?

Null reference - Task ContinueWith()

For the following piece of code (.NET v4.0.30319) I am getting a null reference exception indicated below in the second continuation.
Most interestingly this issue has only occurred in machines with 8GB RAM but other users have 16GB and more and they haven't reported any issue, and it is a very intermittent issue which leads me to suspect a garbage collection issue.
The GetData() can be called multiple times so the first continuation of _businessObjectTask will only be called once as _businessObjects will have been populated from that point forward.
I imagine the Object reference not set to an instance of an object exception is being thrown because either
_businessObjectTask is null and it can't continue from a null task.
items variable which is passed in as a parameter is null somehow
The line number in my log file (748) points to the one highlighted below and not the lambda expression which points to #1 above instead of #2. I've played around in Visual Studio and each of the lines following businessObjectTask.ContinueWith() are considered different i.e. if it was a null reference within the lambda expression it would give a different line number to 748
Any help would be greatly appreciated.
Edit:
This isn't related to What is a NullReferenceException, and how do I fix it? because that is a much more basic explanation of null reference whereas this is much more complicated and subtle.
Exception
Full details of the stack trace (edited for simplicity with dummy class and namespace names)
Object reference not set to an instance of an object.
at ApplicationNamespace.ClassName`1.<>c__DisplayClass4e.<GetData>b__44(Task`1 t) in e:\ClassName.cs:line 748
at System.Threading.Tasks.ContinuationTaskFromResultTask`1.InnerInvoke()
at System.Threading.Tasks.Task.Execute()
Code
private static IDictionary<string, IBusinessObject> _businessObjects;
private Task<IDictionary<string, IBusinessObject>> _businessObjectTask;
public Task GetData(IList<TBusinessItem> items))
{
Log.Info("Starting GetData()");
if (_businessObjects == null)
{
var businessObjectService = ServiceLocator.Current.GetInstance<IBusinessObjectService>();
_businessObjectTask = businessObjectService.GetData(CancellationToken.None)
.ContinueWith
(
t =>
{
_businessObjects = t.Result.ToDictionary(e => e.ItemId);
return _businessObjects;
},
CancellationToken.None,
TaskContinuationOptions.OnlyOnRanToCompletion,
TaskScheduler.Current
);
}
var taskSetLEData = _businessObjectTask.ContinueWith // Line 748 in my code - "Object reference not set to an instance of an object." thrown here
(
task =>
{
items.ToList().ForEach
(
item =>
{
IBusinessObject businessObject;
_businessObjects.TryGetValue(item.Id, out businessObject);
item.BusinessObject = businessObject;
}
);
},
CancellationToken.None,
TaskContinuationOptions.OnlyOnRanToCompletion,
TaskScheduler.Default
);
}
Resolution:
So having used what I've learned from this question I went back to the original code and figured it all out.
Turns out the reason for this NRE was because the _businessObjectTask is non-static where as the _businessObjects is static.
This means that _businessObjects is null the first time GetData() is called which then sets _businessObjectTask to non-null. Then when _businessObjectTask.ContinueWith gets called it is non-null and continues without problem.
However if a second instance of this class above is instantiated, _businessObjects has already been populated so _businessObjectTask remains null. Then when _businessObjectTask.ContinueWith gets called, a NRE on _businessObjectTask is thrown.
There were a couple of options I could have taken but I ended up removing the _businessObjectTask to a synchronous method call which means that I dont need to use the continuation anymore and I set _businessObjects once.
This is a synchronization problem.
You are assuming that _businessObjectTask is always assigned before _businessObjects.
That is, however, not guaranteed. It is possible that your continuation that assign _businessObjects is executed inline and therefore before businessObjectService.GetData(...).ContinueWith(...) returns.
// This assignment could happend AFTER the inner assignment.
_businessObjectTask = businessObjectService.GetData(CancellationToken.None)
.ContinueWith
(
t =>
{
// This assignment could happen BEFORE the outer assignment.
_businessObjects = t.Result.ToDictionary(e => e.ItemId);
Therefore, it is possible that _businessObjects is not null although _businessObjectTask is null.
If a concurrent thread would enter your GetData method at that time it would clearly not enter
if (_businessObjects == null) // not entered because it's not null
{
...
}
...and instead continue with
var taskSetLEData = _businessObjectTask.ContinueWith // line 748
...which will cause a null reference exception since _businessObjectTask is null.
Here's how you could simplify your code and resolve this synchronization problem:
private Lazy<Task<IDictionary<string, IBusinessObject>>> _lazyTask =
new Lazy<Task<IDictionary<string, IBusinessObject>>>(FetchBusinessObjects);
private static async Task<IDictionary<string, IBusinessObject>> FetchBusinessObjects()
{
var businessObjectService = ServiceLocator.Current.GetInstance<IBusinessObjectService>();
return await businessObjectService.GetData(CancellationToken.None).ToDictionary(e => e.ItemId);
}
public async Task GetData(IList<TBusinessItem> items)
{
Log.Info("Starting GetData()");
var businessObjects = await _lazyTask.Value;
items.ToList().ForEach
(
item =>
{
IBusinessObject businessObject;
businessObjects.TryGetValue(item.Id, out businessObject);
item.BusinessObject = businessObject;
}
);
}
Notes:
Using Lazy<T> to ensure that the business object service is only invoked once (per instance of this class, whatever it is).
Using async/await to simplify code.
You may want to consider declaring _lazyTask as static. In your code there seem to be a mixup between static/non-static fields. I cannot know which is right for you.

Null Pointer exception in C#

I am facing NullPointerException in below code as it is happening very rarely and I tried to debug to replicate the issue but no luck. Can anybody help me what can cause NullPointerException here.
private static void MyTaskCompletedCallback(IAsyncResult res)
{
var worker = (AsyncErrorDelegate)((AsyncResult)res).AsyncDelegate;
var async = (AsyncOperation)res.AsyncState;
worker.EndInvoke(res);
lock (IsAsyncOpOccuring)
{
IsBusy = false;
}
var completedArgs = new AsyncCompletedEventArgs(null, false, null);
async.PostOperationCompleted(e => OnTaskCompleted((AsyncCompletedEventArgs)e), completedArgs);
}
Null Pointer exception is reported at
var async = (AsyncOperation)res.AsyncState;
Code from where I am invoking it
var context = HttpContext.Current;
AsyncErrorDelegate bkWorker = SendErrorMail;
AsyncCallback completedCallback = MyTaskCompletedCallback;
lock (IsAsyncOpOccuring)
{
if (IsBusy)
{
//Do we need to do something if repeated async getting call in case of error occuring at same time by different users.
}
AsyncOperation async = AsyncOperationManager.CreateOperation(null);
bkWorker.BeginInvoke(context,completedCallback, async);
IsBusy = true;
}
Null Pointer exception is reported at var async = (AsyncOperation)res.AsyncState;
We can logically deduce that this is not actually the case.
If the line before worked, we know that res is not null. AsyncState is object, so no custom operators are involved here, which means the cast is thus a type-check - which can either return null (without erroring), or can raise an invalid-cast exception.
If you are seeing a NullReferenceException, that leaves 2 options:
res is null and it is the line above that is erroring (which: we shouldn't actually expect - that will not happen)
the error is actually coming from EndInvoke, the line after
(the exact line often gets slightly confused when an exception is involved).
I suggest you add logging between each, to track what is happening. I also suggest you explicitly try around the EndInvoke, since that can throw exceptions (it re-throws any exception from the async operation).
In the more general case, a third option would have been:
AsyncOperation is a struct, and AsyncState is null
However, in this case we can rule that out by deduction, because if AsyncOperation were a struct, the following would never box to null (only an empty AsyncOperation? would box to null):
AsyncOperation async = AsyncOperationManager.CreateOperation(null);
bkWorker.BeginInvoke(context,completedCallback, async);
Should
var async = (AsyncOperation)asyncResult.AsyncState;
not be
var async = (AsyncOperation)res.AsyncState;
?

Static and Generic working together .NET

I have this code:
public class EntityMapper<T> where T : IMappingStrategy, new()
{
private static T currentStrategy;
public static T CurrentStrategy
{
get
{
if (currentStrategy == null)
currentStrategy = new T();
return currentStrategy;
}
}
}
Then:
public static void Main()
{
EntityMapper<ServerMappingStrategy>.CurrentStrategy.ToString();
EntityMapper<ClientMappingStrategy>.CurrentStrategy.ToString();
EntityMapper<ServerMappingStrategy>.CurrentStrategy.ToString();
}
Well, the question is:
Why when i'm debugging i can see that the constructor of ServerBussinessMappingStrategy is called only once time?
This work well, but i undertand why always EntityMapper return the correct instance that i need, only instantiating once time the ServerMappingStrategy class.
Regards!
PD: Sorry my english jeje ;)
The static field is persisted for the duration of your AppDomain, and it is cached when first created:
public static T CurrentStrategy
{
get
{
if (currentStrategy == null) // <====== first use detected
currentStrategy = new T(); // <==== so create new and cache it
return currentStrategy; // <=========== return cached value
}
}
Actually, there is an edge case when it could run twice (or more), but it is unlikely.
This is a pretty common pattern for deferred initialization, and is used pretty much identically in a number of places in the BCL. Note that if it had to happen at most once, it would need either synchronization (lock etc) or something like a nested class with a static initializer.
Normally, it will only get called once. That is, unless you have a race condition.
Let's say two threads execute this statement the same time:
EntityMapper<ServerMappingStrategy>.CurrentStrategy.ToString();
Let's say thread A will run up until currentStrategy == null but gets paused before new T() when Windows suddenly gives control to thread B which then makes the comparison again, currentStrategy is still null, invokes the constructor and assigns the new instance to currentStrategy. Then, at some point, Windows gives the control back to thread A that calls the constructor again. This is important because normally static members are (sort of) expected to be thread safe. So if I were you, I would wrap that bit into a lock clause.
P.S. this snippet won't compile as T might be a struct that cannot be a null. Instead of comparing to null, compare to default(T) or specify that T has to be a class.

Categories

Resources