views:

405

answers:

1

Hi

I have something wich looks to me like race condition while logging to file from multiple thread.

1) I've got a custom logger class (ConfigurableTraceLogger) that is shared by multiple threads in my application. It's has lots of wrapper functions which all call to main core function

protected void TraceData(String category, TraceEventType type, EventId id, string prefix, string format)
{
    foreach (TraceListener item in _listeners)
    {
        IConfigurableTraceListener cl = item as IConfigurableTraceListener;

        if (cl != null && cl.Category == category.ToLower())
        {

            if (DisplayMethodName)
                item.TraceData(new TraceEventCache(), _instanceName, type, (int)id, prefix + format);
            else
                item.TraceData(new TraceEventCache(), _instanceName, type, (int)id, format);

            item.Flush();
        }
    }
}

As you can see my class simply stores different TraceListner-derved class in a collection _listeners. Basically there are only console and text file listeners. What TraceData does it takes the category name (i.e. logging start-up) and finds the right listener. All listeners are defined by config file name

Now I also have my custom listeners in the collection

public class ConfigurableTextWriterTraceListener : TextWriterTraceListener, IConfigurableTraceListener

That custom class overrides nothing except for one property.

protected override string[] GetSupportedAttributes()
{
    return new string[] { "category" };
}

When I start my application after 5 to 10 minutes I get exception on the call

           item.TraceData(new TraceEventCache(), _instanceName, type, (int)id, prefix + format);

Exception is saying:

"Probable I/O race condition detected while copying memory. The I/O package is not thread safe by default. In multithreaded applications, a stream must be accessed in a thread-safe way, such as a thread-safe wrapper returned by TextReader's or TextWriter's Synchronized methods. This also applies to classes like StreamWriter and StreamReader."

After that I keep getting second exception lots of times on the same call to

item.TraceData(new TraceEventCache(), _instanceName, type, (int)id, prefix + format);

Exception

Count cannot be less than zero. Parameter name: count Stack trace: " at System.String.CopyTo(Int32 sourceIndex, Char[] destination, Int32 destinationIndex, Int32 count)\r\n at System.IO.StreamWriter.Write(String value)\r\n at System.Diagnostics.TextWriterTraceListener.Write(String message)\r\n at System.Diagnostics.TraceListener.WriteHeader(String source, TraceEventType eventType, Int32 id)\r\n at System.Diagnostics.TraceListener.TraceData(TraceEventCache eventCache, String source, TraceEventType eventType, Int32 id, Object data)\r\n at Jfc.Configuration.ConfigurableTraceLogger.TraceData(String category, TraceEventType type, EventId id, String prefix, String format, Object[] args)"

It seems to me that my class is not thread safe as well the call to TraceData. But ConfigurableTextWriterTraceListener is said to thread safe after all. However I checked IsThreadSafe propety for my TextWriterTraceListener derived class at run-time and it false. I am trying to figure out where the problem is.

A: 

It means what says - your TraceListener is not thread safe and breaks when accessed from several threads. You need to make your listeners thread safe or find a way to ensure that only one thread accesses any particular instance.

One way to make them thread safe is to use a synchronized queue and make all your calls enqueue data items to the queue while the 'real' traceListener dequeues them and writes them out in a separate thread.

You also have to be careful with your dictionary of listeners - updating the dictionary is not thread safe, but if you never access it before the last update is applied, you can leave it as is

mfeingold
Well thanks! Agree I have to make queue based thread safe logger. But still I have no answer - why race condition isin TextWriterTraceListner-derived TraceData method when MSDN saysthis class is thread-safe? I suppose it means that many thread can call this methods safely. But it seems that they cannot.PS I do not touch the dictionary it is constructed by .NET from config file and logging starts pretty much later.
Captain Comic
'Any public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe' This is taken directly from the TextWriterTraceListener class help
mfeingold
Right - so it is by no means static. Thanks.
Captain Comic