views:

483

answers:

9

I have a strange bug in a multithreaded app:

public class MyClass
{
   private readonly Hashtable HashPrefs;
   public MyClass(int id)
  {
     HashPrefs = new Hashtable();
  } 

  public void SomeMethodCalledFromAnotherThread(string hashKey,string hashValue)
 {
    if (HashPrefs.Contains(hashKey))  // <-- throws NullReferenceException
    {

    }
 }
}

One thread does the :

 SomeQueue.Add(new MyClass(1));

And another thread does the :

 SomeQueue.Dequeue().SomeMethodCalledFromAnotherThread(SomeClass.SomeMethod(),"const value");

But how can the second thread call the method before the constructor is finished?

Edit: I added the part with the function parameters, as it seems this might be relevant. As far as I can tell, the hashKey being passed on cannot be null, as SomeMethod() always returns a relevant string.

As others have pointed out,if the problem was a null haskKey parameter passed to the Contains() , the exception would be ArgumentNullException.

+7  A: 

Yes, readonly fields can be accessed via reflection and their values changed. So the answer to your question is yes, it is possible.

However there are many other things that could also be creating issues for you. Multi-threaded code is difficult to write and the problems that can arise are difficult to diagnose.


As a side note, are you sure that you aren't getting an ArgumentNullException? If somekey is null, the Hashtable.Contains method will throw an ArgumentNullException. Perhaps that is the issue?

Andrew Hare
For a moment there I SOO wanted to believe it was an ArgumentNullException. Great call, but no! It' a NullReference :-(
Radu094
+1  A: 

Are you sure that it's HashPrefs.Contains that is throwing the exception, and not whatever "somekey" represents? If you're not, could you please post the full exception details, as returned by its ToString method?

Nicole Calinoiu
+2  A: 

First of all, the other thread cannot access the instance until the constructor is complete, since the instance itself won't be assigned until the constructor is finished. That being said, the other thread could access the variable that holds the instance before the constructor completes, but it would be null at that time. That would generate the NullReferenceException, but it would come from the method accessing the instance, not from the method inside the instance.

All of that being said, the only real solution that I can see to this is to use an external lock that synchronizes the code around retrieving the instance. If you're accessing a Hashset (or any other type where instance member's aren't guaranteed to be thread safe) you'll need to perform locking around the operations anyhow.

Consider the following class as the one containing a reference to MyClass and the creator of the other threads:

public class MasterClass
{
    private MyClass myClass;
    private object syncRoot = new object(); // this is what we'll use to synchronize the code

    public void Thread1Proc()
    {
        lock(syncRoot)
        {
            myClass = new MyClass();
        }
    }

    public void Thread2Proc()
    {
        lock(syncRoot)
        {
            myClas.SomeMethodCalledFromAnotherThread();
        }
    }
}

This is a very (possibly overly) simplistic view of how the synchronization should take place, but the general idea of this approach is to wrap all code that interacts with the shared object inside the lock block. You truly should do some studying on how to write multithreaded processes, and the various approaches aimed at sharing resources among multiple threads.

EDIT

A completely different possibility is that the values of the instance variable are being cached in a thread-specific manner. If you aren't locking (which creates a memory barrier), then try marking the instance variable as volatile, which will ensure that the reads and writes occur in the proper order.

Adam Robinson
+1  A: 

Just do

HashPrefs = Hashtable.Synchronized(new Hashtable());

No need to reinvent the wheel.

leppie
+1. I don't generally use the old non-generic collection types, but this is a good find!
Adam Robinson
Quite likely to lead him towards wrong path, as I strongly suspect that after checking for `Contains()`, the branch for `false` will add an element. And of course a "synchronized" `Hashtable` won't synchronize those two calls as a single unit.
Pavel Minaev
I fear the problem is not necesarly with the syncronization of the queue, as much as it is with my Hastable becoming null at some point
Radu094
+4  A: 

Reflection is one way to do this (SetField).

A second way to get this curiosity is if you give away the reference too early, by passing this (or sometihng involving an implicit this, such as a field captured into an anonymous method / lambda) out of the .ctor:

public MyClass(int id)
{
    Program.Test(this); // oopsie ;-p
    HashPrefs = new Hashtable();
}

Or more likely (given the question):

SomeQueue.Add(this);

Another relevant question might be - can it not be assigned in the first place? And the answer to that is yes, especially if you are using serialization. Contrary to popular belief, you can actually bypass the constructors, if you have a reason; DataContractSerializer is a good example of something that does this...

The following will create a MyClass with a null field:

MyClass obj = (MyClass)
    System.Runtime.Serialization.FormatterServices.GetUninitializedObject(
        typeof(MyClass));
Marc Gravell
Nice point with the serialization. Just out of curiosity: if the Hashtable is there when the object is serialized, wouldn't the deserializaton create an object with a correct hashtable in place?
Radu094
In the example given, it probably would (assuming that the field existed when serialized, and hasn't been renamed). If you are doing some funky things with custom serializers, maybe not. I'll admit that these are edge cases, but hey! it is a crazy world. The `GetUninitializedObject` approach will work "as is", of course.
Marc Gravell
WCF object deserialization creates objects in this fashion. To work around this, I've made these readonly fields into readonly properties that initialize the underlying field on first access.
John Rayner
A: 

Several things you can try:

  1. Try initializing the Hashtable as a member variable (outside of the constructor), and with public property of the same name, see if someone is calling it to make an assignment, like so:

    public class MyClass {
    private readonly Hashtable hashPrefs = new Hashtable();
    
    
    public MyClass(int id)
    {
    
    
    }
    
    
    public Hashtable HashPrefs
    {
        set
        {
            throw new InvalidOperationException("This shouldn't happen");
        }
    }
    

    }

  2. Secondly, what type is "SomeQueue"? Is that just a regular List<>? Or is it some special self-implemented Queue that relies on some XML/binary serialization? If so, have you marked HashPrefs as [Serializable]? Or as a [DataMember]?

enderminh
+1  A: 

Are you absolutely sure that HashPrefs.Contains is throwing the NPE? I would think SomeQueue.Dequeue().SomeMethodCalledFromAnotherThread() would be the more likely candidate... because chances are the constructor and Add operations haven't finished yet when you try to Dequeue the element.

In fact, it might not be a bad idea to check SomeQueue.Count* before Dequeueing an element.

Edit: Or even better, null check before calling SomeMethodCalledFromAnotherThread, assuming that your other thread is checking the queue in a loop... because if it's not, lock the queue prior to the Add and Dequeue operations.

MyClass mine = SomeQueue.Dequeue();

if (null != mine)
    mine.SomeMethodCalledFromAnotherThread();

*An extension method added in .NET 3.5.

R. Bemrose
This is a multithreading issue, meaning you can't check someQueue.Count before dequeuing. That count means nothing since between the time the Count propery comes back and the time you call Dequeue, the queue could be empty.
Brian Hasden
In that case, he could store the Dequeued element and check it for null before calling a method on it.
R. Bemrose
That makes a lot more sense. Upvoted for the edit.
Brian Hasden
A: 

A couple ideas come to mind:

  • The hashtable is being concurrently modified leading to an inconsistent internal state.
  • The call to contains performs equality checks which attempt to dereference a value that has a null field. What type is `somekey`?
Yuliy
I edited the question. somekey is a string passed as a parameter
Radu094
+2  A: 

Your instructions might be reordered so that a reference to an instance of a MyClass is added to the queue before it is fully constructed. Here's an article about double-checked locking that sort of touches on this topic. From the article:

Vance, a devlead on the CLR JIT team explained that the issue is around the CLR memory model… Essentially the memory model allows for non-volatile reads\writes to be reordered as long as that change can not be noticed from the point of view of a single thread.

Take a look at System.Threading.Thread.MemoryBarrier. You might need to do something like this:

 MyClass temp = new MyClass(1);
 System.Threading.Thread.MemoryBarrier();
 SomeQueue.Add(temp);

The MemoryBarrier ensures that all the instructions before it are executed before proceeding.

MikeB
Ouch! Sounds like multithreading is back on the table.
Radu094