    // @(#)root/base:$Name:  $:$Id: TTimer.cxx,v 1.11 2004/05/26 10:38:55 rdm Exp $
    // Author: Fons Rademakers   28/11/96

    /*************************************************************************
     * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers.               *
     * All rights reserved.                                                  *
     *                                                                       *
     * For the licensing terms see $ROOTSYS/LICENSE.                         *
     * For the list of contributors see $ROOTSYS/README/CREDITS.             *
     *************************************************************************/

    //////////////////////////////////////////////////////////////////////////
    //                                                                      //
    // TTimer                                                               //
    //                                                                      //
    // Handles synchronous and a-synchronous timer events. You can use      //
    // this class in one of the following ways:                             //
    //    - Sub-class TTimer and override the Notify() method.              //
    //    - Re-implement the TObject::HandleTimer() method in your class    //
    //      and pass a pointer to this object to timer, see the SetObject() //
    //      method.                                                         //
    //    - Pass an interpreter command to timer, see SetCommand() method.  //
    //    - Create a TTimer, connect its Timeout() signal to the            //
    //      appropriate methods. Then when the time is up it will emit a    //
    //      Timeout() signal and call connected slots.                      //
    //                                                                      //
    //  Minimum timeout interval is defined in TSystem::ESysConstants as    //
    //  kItimerResolution (currently 10 ms).                                //
    //                                                                      //
    //  Signal/slots example:                                               //
    //       TTimer *timer = new TTimer();                                  //
    //       timer->Connect("Timeout()", "myObjectClassName",               //
    //                      myObject, "TimerDone()");                       //
    //       timer->Start(2000, kTRUE);   // 2 seconds single-shot          //
    //                                                                      //
    //    // Timeout signal is emitted repeadetly with minimum timeout      //
    //    // timer->Start(0, kFALSE);                                       //
    //                                                                      //
    //////////////////////////////////////////////////////////////////////////

    #include "TTimer.h"
    #include "TSystem.h"
    #include "TROOT.h"

    ClassImp(TTimer)

    class TSingleShotCleaner : public TTimer {
    private:
       TList  *fGarbage;
    public:
       TSingleShotCleaner() : TTimer(10, kTRUE) { fGarbage = new TList(); }
       virtual ~TSingleShotCleaner() { fGarbage->Delete(); delete fGarbage; }
       void TurnOn() {
          TObject *obj = (TObject*) gTQSender;
          fGarbage->Add(obj);
          Reset();
          if (gSystem)
             gSystem->AddTimer(this);
       }
       Bool_t Notify() {
          fGarbage->Delete();
          Reset();
          if (gSystem)
             gSystem->RemoveTimer(this);
          return kTRUE;
       }
    };

    TSingleShotCleaner gSingleShotCleaner;  // single shot timer cleaner

    //______________________________________________________________________________
     TTimer::TTimer(Long_t ms, Bool_t mode) : fTime(ms)
    {
       // Create timer that times out in ms milliseconds. If milliSec is 0
       // then the timeout will be the minimum timeout (see TSystem::ESysConstants,
       // i.e. 10 ms). If mode == kTRUE then the timer is synchronous else
       // a-synchronous. The default is synchronous. Add a timer to the system
       // eventloop by calling TurnOn(). Set command to be executed from Notify()
       // or set the object whose HandleTimer() method will be called via Notify(),
       // derive from TTimer and override Notify() or connect slots to the
       // signals Timeout(), TurnOn() and TurnOff().

       fObject      = 0;
       fCommand     = "";
       fSync        = mode;
       fIntSyscalls = kFALSE;
       Reset();
    }

    //______________________________________________________________________________
     TTimer::TTimer(TObject *obj, Long_t ms, Bool_t mode) : fTime(ms)
    {
       // Create timer that times out in ms milliseconds. If mode == kTRUE then
       // the timer is synchronous else a-synchronous. The default is synchronous.
       // Add a timer to the system eventloop by calling TurnOn().
       // The object's HandleTimer() will be called by Notify().

       fObject      = obj;
       fCommand     = "";
       fSync        = mode;
       fIntSyscalls = kFALSE;
       Reset();
    }

    //______________________________________________________________________________
     TTimer::TTimer(const char *command, Long_t ms, Bool_t mode) : fTime(ms)
    {
       // Create timer that times out in ms milliseconds. If mode == kTRUE then
       // the timer is synchronous else a-synchronous. The default is synchronous.
       // Add a timer to the system eventloop by calling TurnOn().
       // The interpreter will execute command from Notify().

       fObject      = 0;
       fCommand     = command;
       fSync        = mode;
       fIntSyscalls = kFALSE;
       Reset();
    }

    //______________________________________________________________________________
     Bool_t TTimer::CheckTimer(const TTime &now)
    {
       // Check if timer timed out.

       if (fAbsTime <= now) {
          fTimeout = kTRUE;
          Notify();
          return kTRUE;
       }
       return kFALSE;
    }

    //______________________________________________________________________________
     Bool_t TTimer::Notify()
    {
       // Notify when timer times out. The timer is always reset. To stop
       // the timer call TurnOff().

       Timeout();       // emit Timeout() signal
       if (fObject) fObject->HandleTimer(this);
       if (fCommand && fCommand.Length() > 0)
          gROOT->ProcessLine(fCommand);

       Reset();
       return kTRUE;
    }

    //______________________________________________________________________________
     void TTimer::Reset()
    {
       // Reset the timer.

       // make sure gSystem exists
       ROOT::GetROOT();

       fTimeout = kFALSE;
       fAbsTime = fTime;
       if (gSystem) {
          fAbsTime += gSystem->Now();
          if (!fSync) gSystem->ResetTimer(this);
       }
    }

    //______________________________________________________________________________
     void TTimer::SetCommand(const char *command)
    {
       // Set the interpreter command to be executed at time out. Removes the
       // object to be notified (if it was set).

       fObject  = 0;
       fCommand = command;
    }

    //______________________________________________________________________________
     void TTimer::SetObject(TObject *object)
    {
       // Set the object to be notified  at time out. Removes the command to
       // be executed (if it was set).

       fObject  = object;
       fCommand = "";
    }

    //______________________________________________________________________________
     void TTimer::SetInterruptSyscalls(Bool_t set)
    {
       // When the argument is true the a-synchronous timer (SIGALRM) signal
       // handler is set so that interrupted syscalls will not be restarted
       // by the kernel. This is typically used in case one wants to put a
       // timeout on an I/O operation. By default interrupted syscalls will
       // be restarted.

       fIntSyscalls = set;
    }

    //___________________________________________________________________
     void TTimer::Start(Int_t milliSec, Bool_t singleShot)
    {
       // Starts the timer with a milliSec timeout. If milliSec is 0
       // then the timeout will be the minimum timeout (see TSystem::ESysConstants,
       // i.e. 10 ms), if milliSec is -1 then the time interval as previously
       // specified (in ctor or SetTime()) will be used.
       // If singleShot is kTRUE, the timer will be activated only once,
       // otherwise it will continue until it is stopped.
       // See also TurnOn(), Stop(), TurnOff().

       if (milliSec >= 0)
          SetTime(milliSec);
       Reset();
       TurnOn();
       if (singleShot)
          Connect(this, "Timeout()", "TTimer", this, "TurnOff()");
       else
          Disconnect(this, "Timeout()", this, "TurnOff()");
    }

    //______________________________________________________________________________
     void TTimer::TurnOff()
    {
       // Remove timer from system timer list. This requires that a timer
       // has been placed in the system timer list (using TurnOn()).
       // If a TTimer subclass is placed on another list, override TurnOff() to
       // remove the timer from the correct list.

       if (gSystem)
          if (gSystem->RemoveTimer(this))
             Emit("TurnOff()");
    }

    //______________________________________________________________________________
     void TTimer::TurnOn()
    {
       // Add the timer to the system timer list. If a TTimer subclass has to be
       // placed on another list, override TurnOn() to add the timer to the correct
       // list.

       // might have been set in a previous Start()
       Disconnect(this, "Timeout()", this, "TurnOff()");

       if (gSystem) {
          gSystem->AddTimer(this);
          Emit("TurnOn()");
       }
    }

    //______________________________________________________________________________
     void TTimer::SingleShot(Int_t milliSec, const char *receiver_class,
                            void *receiver, const char *method)
    {
       // This static function calls a slot after a given time interval.
       // Created internal timer will be deleted after that.

       TTimer *singleShotTimer = new TTimer(milliSec);
       TQObject::Connect(singleShotTimer, "Timeout()",
                         receiver_class, receiver, method);

       // gSingleShotCleaner will delete singleShotTimer a
       // short period after Timeout() signal is emitted
       TQObject::Connect(singleShotTimer, "Timeout()",
                         "TTimer", &gSingleShotCleaner, "TurnOn()");

       singleShotTimer->Start(milliSec, kTRUE);
    }