0

I'm trying to troubleshoot a process that is mysteriously stopping, and I'm trying to determine if we could have an issue with a variable getting cached and never read again.

In my scenario, I have an operation that occurs on a timer (I'll call that Poll) and a pair of outside functions that get called to signal the start and stop of an external operation. Due to details not really worth going into, this hypothetical class does not want the Poll operation to execute if the external operation is taking place. Additionally, the external operation could occur multiple times simultaneously, so I'm using a counter to indicate the number of operations in progress.

The current logic looks more or less like this simplified example:

class MyClass
{
    private int operationsInProgress;

    private void Poll() // Pretend we have a timer in place that's calling this periodically
    {
        var inProgress = operationsInProgress;

        if (inProgress > 0) return;

        DoSomething();
    }

    public void StartExternalOperation()
    {
        Interlocked.Increment(ref operationsInProgress);
    }

    public void EndExternalOperation()
    {
        Interlocked.Decrement(ref operationsInProgress);
    }
}

Assumptions:

  • Both StartExternalOperation and EndExternalOperation are being called the same number of times--they're executed within a try...finally and there is no path that leaves an orphaned Start sitting out there
  • At some point after an indeterminate number of executions, DoSomething() never gets called again. In my real scenario, I don't know exactly why, but I have eliminated all other causes that I can find other than this one.

Is it possible because I'm not using any kind of fence (either through Interlocked or a volatile variable) on the read operation that the value is getting cached and the actual variable isn't getting read again?

Since this is an issue that we can't reliably reproduce, I need to know if what I'm suspecting could reasonably be the cause. I could change the code to use something more formal like a ReaderWriterLock (or Slim) and I know I would have proper logic, I just need to know if what I've described could legitimately be the cause.

Adam Robinson
  • 182,639
  • 35
  • 285
  • 343
  • Are you sure that `Poll` is being called? I saw something like this when using `System.Timers.Timer`, and it turned out that the timer was throwing an exception (which never got reported because the timer event handler squashes exceptions). But you said you're using `System.Threading.Timer`. Hmmm . . . Might be that `Interlocked.Add(ref operationsInProgress, 0);` would be useful? – Jim Mischel Dec 23 '16 at 05:07
  • @JimMischel This was a possibility that we have considered (and can't definitively rule out, other than this not affecting anything else), but at this point we don't have anything that is pointing us at the possibility that it isn't getting called. The function here is simplified to demonstrate the logic, but there's robust exception handling and logging in the actual implementation. – Adam Robinson Dec 23 '16 at 05:12
  • Seems prudent to put a log line in that method, or use some other means of proving that the method is still being executed. – Jim Mischel Dec 23 '16 at 05:14
  • It seems like the problem isn't in the logic but in the implementation. Unless someone comes along who recognizes the exact problem and can produce an exact solution, I'm not sure there can be much done to solve your problem. All I can say is to log it more thoroughly than you are currently doing and see if a pattern turns up. – Abion47 Dec 23 '16 at 05:16
  • @JimMischel: There actually is a logging call if it skips the operation, but it's logged as verbose and turning that on would result in a mountain of other debugging information getting written...not a deal breaker, but it's still something we'd like to try as a next step. – Adam Robinson Dec 23 '16 at 05:17
  • @Abion47: The question relates specifically to whether or not the execution function of a `System.Threading.Timer` is subject to caching non-volatile variables (similar to if you spun up a thread with an infinite loop and you checked the value of that variable inside of the loop). – Adam Robinson Dec 23 '16 at 05:19
  • I've never seen a case where a variable is cached in this circumstance. Doesn't mean it couldn't be, but I'd consider it unlikely. I consider it more likely that either your assertion #1 above is false, or something is preventing the `Poll()` method from being called. – Jim Mischel Dec 23 '16 at 05:20
  • I'm tempted to close as a duplicate of [this question](http://stackoverflow.com/q/24808291/517852). – Mike Zboray Dec 23 '16 at 07:27
  • It is the eternal System.Threading.Timer question, a class whose objects are subject to *garbage collection*. If you don't ensure that the Timer object is visibly in use then it gets collected and of course stops ticking. Standard mistake is to have only one reference to the object and make it a local variable of a method. The code snippet is lacking. – Hans Passant Dec 23 '16 at 08:09
  • @HansPassant: I appreciate what you're saying, but please bear in mind that what I'm asking about deals specifically with whether or the value is subject to caching; had I been asking "why is my timer stopping?", I would have included such detail :). But including enough detail to troubleshoot THAT problem would be impractical, and would likely only be of use to me (rather than answering the general question). – Adam Robinson Dec 23 '16 at 14:08

0 Answers0