Monday, December 26, 2011

C#: Timer, Deferred Evaluation & GC

Following is the snippet of benign (at least by appearance) code that had been cause one of the bugs we faced recently.

The class schedules the given jobs on a timer. The start time for the jobs is around 10 hours from the point the scheduler Start is executed. The timer fires every 24 hrs periodically.

As unexpected, the timer never fired, not even once in production.

We did not face any issue related to this code in development environment, the only difference was that the delay time and period for the timer were shorter in the unit test.

Key suspect in such code behavior is GC. GC has high probability of execution in a long running process than a short running unit test.

The issue was indeed GC, the timer was being garbage collected hence the timer callback was not being executed.

Here is the detailed analysis of the code.
  •  The Loc 1 in the code is a Select expression which is implemented  by deferred execution.
  •  The Loc 2 in the code exists to ensure the Select is executed and timer is created/scheduled.
  •  The key issue with the code is no reference to the Timer object created is retained.  Hence, the Timer object is garbage collected.
  • Even though the Loc 1 seems to indicate that the reference to the Timer is assigned to the _timers field, it's not so. The _timers field holds the reference to the Select iterator. In other words, the lambda code itself.
GetType() on _timers will return System.Linq.Enumerable+WhereSelectListIterator`2 [System.Threading.TimerCallback,System.Threading.Timer]
  • If the timer is garbage collected, what is the observed behavior in the Dispose method of the JobScheduler ? 
The foreach loop actually creates new timers as a result of Select execution again and disposes them. 
So the current situation with the code is
  • Timer will not be fired if the GC runs.
  • Timer created will never be stopped by Dispose if the GC does not collect it.
The solution is to convert the output of the Select iterator using toList()or an equivalent method . This method not only forces eager evaluation but also creates a proper enumerable list of Timer objects - System.Collections.Generic.List`1[System.Threading.Timer]
This is the Gist of the the complete program to observe the issue/code behavior.


Sunday, December 11, 2011

Prolog: List Difference

Today I was implementing a Prolog program to find the difference between two lists.

Here is the problem statement and the expected output.

minus([1,2,3,4],[1,3],W).
W=[2,4]
yes

As a beginner in Prolog, I tend use the accumulator based approach to find the solution. Here is what I came up with spending some significant time at the Prolog interpreter and debugger.


minusAcc(L,[],_,L) :- !.
minusAcc([],_,A,A) :- !.
minusAcc([H|T],SL,A,W) :- \+memberchk(H,SL),!,
                            append([H],A,AL),
                            minusAcc(T,SL,AL,W).
minusAcc([_|T],SL,A,W) :- minusAcc(T,SL,A,W).
minus(L,SL,W) :- minusAcc(L,SL,[],W).

This program gives the expected output but does not preserve the order. It actually gives the output in the reverse order because of the inherent recursion in the program.  Following is output generated by this program.

minus([1,2,3,4],[1,3],W).
W=[4,2]
yes

Since, I wrote this routine for use in list sorting, the order did not matter and I decided to live with this program.

Meanwhile, I had a look at the implementation available on the web for the same purpose and I found the following program on stackoverflow very interesting.

minus(L,[],L) :- !.
minus([],_,[]) :- !.
minus([H|T],SL,W) :- memberchk(H,SL),!,minus(T,SL,W).
minus([H|T1],SL,[H|T2]) :- minus(T1,SL,T2).

This program gives the expected output and preserves the order too. This program also uses the classic accumulator (unlike mine) based approach, but here the result itself is the accumulator. I had to trace it on debugger for about 10 mins (this is my 2nd day on prolog) to understand the control flow. It was totally worth it!

Gem of a program! Made my weekend!

BTW, here is the gist of my Day 2 of Prolog using Seven Languages in Seven Weeks!