The Multithreading Bug in a C# Project


Recently, I have been working on a project that involves C# and Delphi. The C# is used develop the GUI-related application and the Delphi code provides a COM (Component Object Model) that handles some computation. It is wrapped as a single DLL.

One of the bugs that have been very annoying and reported by the clients is that the computation is stuck somewhere during a one-hour progress bar showing. It is random meaning that not possible to trace to exactly the stuck point. The components and executables are all encrypted and well protected. Therefore, it makes the debug even harder.

I had to insert lots of code snippets to log variables to files. Still, the error is unpredictable. Later, I accidently found out that the parameters have been reset when stepping into the Delphi code at the stuck point. The DLL provides a API to reset the parameters but it is unlikely to be invoked when the computation goes on in the Delphi part. I then guessed that some other processes have accidently invoked this API which stucks the delphi threads.

The C# declares the COM object and uses it like this.

1
2
3
4
5
6
7
8
9
10
11
12
13
private DelphiCOM _tmp;
internal DelphiCOM Obj
{
    get
    {
        if (_tmp == null)
        {
            _tmp = new DelphiCOM();
            _tmp.Reset();
        }
        return (_tmp);
    }
}
private DelphiCOM _tmp;
internal DelphiCOM Obj
{
    get
    {
        if (_tmp == null)
        {
            _tmp = new DelphiCOM();
            _tmp.Reset();
        }
        return (_tmp);
    }
}

The reset() initializes the internal parameter set used in the Delphi COM object. It is observed that sometimes the parameters are randomly reset. With more RAM, it tends to suppress this error.

The cause of such error is that, there is another process which checks the message by the other API, e.g. GetMsg() it uses directly the Obj defined in above C# code. This process is like a timer, which is invoked at a time interval. Therefore, two processes (the computation thread and this message-update thread) may access the above get block to get the property. It is less likely to happen but possible that the computation thread has finished the Reset() and continue to computation but the msg-update thread is in the interleaving part between new and Reset(). The error is partly due to the message blocking and may likely to occur if the system is busy (less RAM).

The solution is simple, adding the lock works like a magic, but it took me a day to figure out this.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private DelphiCOM _tmp;
internal DelphiCOM Obj
{
    get
    {
        lock(this)
        {
            if (_tmp == null)
            {
                _tmp = new DelphiCOM();
                _tmp.Reset();
            }
        }
        return (_tmp);
    }
}
private DelphiCOM _tmp;
internal DelphiCOM Obj
{
    get
    {
        lock(this)
        {
            if (_tmp == null)
            {
                _tmp = new DelphiCOM();
                _tmp.Reset();
            }
        }
        return (_tmp);
    }
}

–EOF (The Ultimate Computing & Technology Blog) —

GD Star Rating
loading...
475 words
Last Post: One Interesting Linux Command (Steam Locomotive in BASH)
Next Post: Using DocStrings in Python

The Permanent URL is: The Multithreading Bug in a C# Project

Leave a Reply