Logo Search packages:      
Sourcecode: galaxium version File versions  Download package

TimerUtility.cs

/*
 * Copyright (C) 2005-2008  Ben Motmans  <ben.motmans@gmail.com>
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

using System;
using System.Timers;
using System.Threading;
using System.Collections.Generic;

using Anculus.Core;
using Anculus.Gui;

namespace Galaxium.Core
{
      public static class TimerUtility
      {
            private static List<TimerRequest> _requests;
            private static TimerRequestComparer _comparer;
            
            private static bool _isRunning;
            private static int _timeout;
            private static System.Timers.Timer _timer;
            private static DateTime _timestamp;
            
            private static uint _uid;
            private static object _sync = new object ();
            
            private const double _timeDeviation = 0.1;
            
            static TimerUtility ()
            {
                  _requests = new List<TimerRequest> ();
                  _comparer = new TimerRequestComparer ();
                  
                  _timer = new System.Timers.Timer (1000);
                  _timer.Elapsed += TimerElapsed;
            }
            
            public static uint RequestInfiniteCallback (VoidDispatchHandler callback, int delay)
            {
                  ThrowUtility.ThrowIfNull ("callback", callback);
                  ThrowUtility.ThrowIfLessThenOne ("delay", delay);
                  
                  VoidDispatchContainer container = new VoidDispatchContainer (callback, false);
                  return AddCallback (container, delay, true);
            }
            
            public static uint RequestInfiniteCallback (ObjectDispatchHandler callback, object obj, int delay)
            {
                  ThrowUtility.ThrowIfNull ("callback", callback);
                  ThrowUtility.ThrowIfLessThenOne ("delay", delay);
                  
                  ObjectDispatchContainer container = new ObjectDispatchContainer (callback, obj, false);
                  return AddCallback (container, delay, true);
            }
            
            public static uint RequestCallback (VoidDispatchHandler callback, int delay)
            {
                  ThrowUtility.ThrowIfNull ("callback", callback);
                  ThrowUtility.ThrowIfLessThenOne ("delay", delay);
                  
                  VoidDispatchContainer container = new VoidDispatchContainer (callback, false);
                  return AddCallback (container, delay, false);
            }
            
            public static uint RequestCallback (ObjectDispatchHandler callback, object obj, int delay)
            {
                  ThrowUtility.ThrowIfNull ("callback", callback);
                  ThrowUtility.ThrowIfLessThenOne ("delay", delay);
                  
                  ObjectDispatchContainer container = new ObjectDispatchContainer (callback, obj, false);
                  return AddCallback (container, delay, false);
            }
            
            public static void ResetCallback (uint id)
            {
                  lock (_sync)
                  {
                        int index = Sort.BinarySearchIndex<TimerRequest,uint> (_requests, _comparer, id);
                        
                        if (index >= 0)
                              _requests[index].CurrentDelay = 0;
                  }
            }
            
            public static void RemoveCallback (uint id)
            {
                  lock (_sync)
                  {
                        int index = Sort.BinarySearchIndex<TimerRequest,uint> (_requests, _comparer, id);
                        
                        if (index >= 0)
                              _requests.RemoveAt (index);
                  }
            }
            
            private static uint AddCallback (IDispatchContainer container, int delay, bool inf)
            {
                  if (_uid == uint.MaxValue)
                        _uid = 0; //this will most likely never occur, since it will take about 250 days with 100 callbacks/second to reach uint.MaxValue
                  
                  TimerRequest req = new TimerRequest (++_uid, container, delay, inf);
                  
                  if (_isRunning)
                  {
                        if (delay < _timeout)
                        {
                              //add the current waiting time to all requests and reset the timer
                              int diff = (int)DateTime.Now.Subtract (_timestamp).TotalMilliseconds;
                              
                              lock (_sync)
                              {
                                    int len = _requests.Count;
                                    
                                    for (int i=0; i<len; i++)
                                          _requests[i].CurrentDelay += diff;
                                    
                                    _requests.Add (req);
                              }
                              
                              ChangeTimerInterval ();
                        }
                        else
                        {
                              //add the invers of the current waiting time
                              int diff = (int)DateTime.Now.Subtract (_timestamp).TotalMilliseconds;
                              req.CurrentDelay = -diff;
                              
                              lock (_sync)
                                    _requests.Add (req);
                        }
                  }
                  else
                  {
                        lock (_sync)
                              _requests.Add (req);
                        
                        _isRunning = true;
                        ChangeTimerInterval ();
                  }
                  
                  return _uid;
            }
            
            private static void ChangeTimerInterval ()
            {
                  if (_requests.Count == 0)
                  {
                        _isRunning = false;
                        _timer.Enabled = false;
                        return;
                  }
                  
                  int delay = _requests[0].RemainingDelay;
                  
                  for (int i=1; i<_requests.Count; i++)
                  {
                        if (_requests[i].RemainingDelay < delay)
                              delay = _requests[i].RemainingDelay;
                  }
                  
                  Thread.VolatileWrite (ref _timeout, delay);
                  
                  _timer.Enabled = false;
                  _timer.Interval = delay;
                  _timestamp = DateTime.Now;
                  _timer.Enabled = true;
            }
            
            private static void TimerElapsed (object sender, ElapsedEventArgs args)
            {
                  int len = 0;
                  List<IDispatchContainer> run = new List<IDispatchContainer> ();
                  
                  lock (_sync)
                  {
                        len = _requests.Count;
                        List<int> rem = new List<int> ();
                        
                        for (int i=0; i<len; i++)
                        {
                              TimerRequest req = _requests[i];
                              req.CurrentDelay += _timeout;
                              
                              if (req.CurrentDelay >= req.Delay)
                              {
                                    if (req.Infinite)
                                          req.CurrentDelay = 0;
                                    else
                                          rem.Add (i);
                                    
                                    run.Add (req.Callback);
                              }
                        }
                        
                        len = rem.Count;
                        
                        while (--len >= 0)
                              _requests.RemoveAt (rem[len]);
                        
                        ChangeTimerInterval ();
                  }
                  
                  // Ensure that we invoke the main thread outside the lock
                  // Otherwise, if the callback attempts to add/remove a request
                  // we'll deadlock
                  
                  foreach (IDispatchContainer cb in run)
                  {
                        ThreadUtility.SyncDispatch (new VoidDelegate (delegate
                        {
                              cb.Execute ();
                        }));
                  }
            }
            
            private class TimerRequestComparer  : IPropertyComparer<TimerRequest, uint>
            {
                  public int Compare (TimerRequest t, uint u)
                  {
                        return t.Identifier.CompareTo (u);
                  }
            }
            
            private class TimerRequest
            {
                  public readonly uint Identifier;
                  public readonly IDispatchContainer Callback;
                  
                  public readonly int Delay;
                  public int CurrentDelay;
                  
                  public readonly bool Infinite;
                  
                  public TimerRequest (uint id, IDispatchContainer callback, int delay, bool infinite)
                  {
                        this.Identifier = id;
                        this.Infinite = infinite;
                        this.Delay = delay;
                        this.CurrentDelay = 0;
                        this.Callback = callback;
                  }
                  
                  public int RemainingDelay
                  {
                        get { return Delay - CurrentDelay; }
                  }
            }
      }
}

Generated by  Doxygen 1.6.0   Back to index