Dealing with infinite recursion redux

In a previous post about infinite recursion I proposed the use of the ThreadStaticAttribute to deal with infinite recursion. This approach is fine so long as your recursion doesn’t cross threads, if it does the static variable will be reset for each new thread and offer not benefit.

Here’s an example, in .NET 4.0, to show you what I mean. In this example I have a simple logging setup, with a list of IListeners which handle messages that a logged with the LogMessage function. It’ll get into a infinite recursive loop becuase the LogToDiskListener attempts to log a message, and the ThreadStaticAttribute doesn’t help because each listener is executed in it’s own thread.

        [ThreadStatic]
        private static bool _isLoggingMessage;

        private static List<IListener> _listeners = new List<IListener>();

        public static void Main()
        {
            _listeners.Add(new LogToDiskListener());
            LogMessage("Starting main function");
        }

        private static void LogMessage(string message)
        {
            if (_isLoggingMessage)
                return;

            _isLoggingMessage = true;

            _listeners.AsParallel()
                .ForAll(listener => listener.HandleMessage(message));

            _isLoggingMessage = false;
        }

        public class LogToDiskListener : IListener
        {
            public void HandleMessage(string message)
            {
                LogMessage("Writing message to disk");

                // Write the message to disk
            }
        }

        public interface IListener
        {
            void HandleMessage(string message);
        }

The code above won’t throw a StackOverFlowException, but will continue to use CPU and increase memory usage until it starves itself of threads.

Fortunately there is a particular solution to this scenario, using the CallContext.LogicalSetData method. Any data set with this method will continue to flow down through child threads, offering a solution to the recursion. Below is a example that wont loop indefinitely:

        private const string IsLoggingMessageKey = "LoggingExample_IsLoggingMessage";

        private static bool IsLoggingMessage
        {
            get 
            { 
                return ((bool?)CallContext.LogicalGetData(IsLoggingMessageKey))
                    .GetValueOrDefault(); 
            }
            set 
            { 
                CallContext.LogicalSetData(IsLoggingMessageKey, value); 
            }
        }

        private static List<IListener> _listeners = new List<IListener>();

        public static void Main()
        {
            _listeners.Add(new LogToDiskListener());
            LogMessage("Starting main function");
        }

        private static void LogMessage(string message)
        {
            if (IsLoggingMessage)
                return;

            _listeners.AsParallel()
                .ForAll(listener =>
                {
                    IsLoggingMessage = true;
                    listener.HandleMessage(message);
                });
        }

        public class LogToDiskListener : IListener
        {
            public void HandleMessage(string message)
            {
                LogMessage("Writing message to disk");

                // Write the message to disk
            }
        }

        public interface IListener
        {
            void HandleMessage(string message);
        }

In the new version of the I’ve replaced the field with a ThreadStaticAttribue with a property that wraps around some calls to the CallContext. I’ve also removed the call to set it to false, since it’s value is lost as soon as all the threads that are using are finished. I’ve also move the place where the new property is set to within the child threads.

Phil

Comments are closed.