// @(#)root/thread:$Name:  $:$Id: TThread.cxx,v 1.21 2004/07/08 11:52:32 rdm Exp $
// Author: Fons Rademakers   02/07/97

/*************************************************************************
 * 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.             *
 *************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TThread                                                              //
//                                                                      //
// This class implements threads. A thread is an execution environment  //
// much lighter than a process. A single process can have multiple      //
// threads. The actual work is done via the TThreadImp class (either    //
// TPosixThread, TThreadSolaris or TThreadNT).                          //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#ifdef HAVE_CONFIG
#include "config.h"
#endif

#include "TThread.h"
#include "TThreadImp.h"
#include "TThreadFactory.h"
#include "TROOT.h"
#include "TApplication.h"
#include "TVirtualPad.h"
#include "TError.h"
#include "CallFunc.h"
#include "TMethodCall.h"


TThreadImp     *TThread::fgThreadImp = 0;
TThread        *TThread::fgMain = 0;
TMutex         *TThread::fgMainMutex;
char  *volatile TThread::fgXAct = 0;
TMutex         *TThread::fgXActMutex;
TCondition     *TThread::fgXActCondi;
void **volatile TThread::fgXArr = 0;
volatile Int_t  TThread::fgXAnb = 0;
volatile Int_t  TThread::fgXArt = 0;


ClassImp(TThread)

//______________________________________________________________________________
TThread::TThread(VoidRtnFunc_t fn, void *arg, EPriority pri)
{
   // Create a thread. Specify the function or static class method
   // to be executed by the thread and a pointer to the argument structure.
   // The user function should return a void*. To start the thread call Run().

   //if G__p2f2funcname-method recognizes true name of fn (call by interpreter)

   fDetached  = kFALSE;
   fFcnVoid   = 0;
   fFcnRetn   = fn;
   fPriority  = pri;
   fThreadArg = arg;
   Constructor();
   fNamed     = kFALSE;
}

//______________________________________________________________________________
TThread::TThread(VoidFunc_t fn, void *arg, EPriority pri)
{
   // Create a detached thread. Specify the function or class method
   // to be executed by the thread and a pointer to the argument structure.
   // To start the thread call Run().

   //if G__p2f2funcname-method recognizes true name of fn (call by interpreter)

   fDetached  = kTRUE;
   fFcnRetn   = 0;
   fFcnVoid   = fn;
   fPriority  = pri;
   fThreadArg = arg;
   Constructor();
   fNamed     = kFALSE;
}

//______________________________________________________________________________
TThread::TThread(const char *thname, VoidRtnFunc_t fn, void *arg,
                 EPriority pri) : TNamed(thname, "")
{
   // Create thread with a name. Specify the function or class method
   // to be executed by the thread and a pointer to the argument structure.
   // The user function should return a void*. To start the thread call Run().

   //if G__p2f2funcname-method recognizes true name of fn (call by interpreter)

   fDetached  = kFALSE;
   fFcnVoid   = 0;
   fFcnRetn   = fn;
   fPriority  = pri;
   fThreadArg = arg;
   Constructor();
   fNamed     = kTRUE;
}

//______________________________________________________________________________
TThread::TThread(const char *thname, VoidFunc_t fn, void *arg,
                 EPriority pri) : TNamed(thname, "")
{
   // Create a detached thread with a name. Specify the function or class
   // method to be executed by the thread and a pointer to the argument
   // structure. To start the thread call Run().

   //if G__p2f2funcname-method recognizes true name of fn (call by interpreter)

   fDetached  = kTRUE;
   fFcnRetn   = 0;
   fFcnVoid   = fn;
   fPriority  = pri;
   fThreadArg = arg;
   Constructor();
   fNamed     = kTRUE;
}

//______________________________________________________________________________
TThread::TThread(Int_t id)
{
   // Create a TThread for a already running thread.

   fDetached  = kTRUE;
   fFcnRetn   = 0;
   fFcnVoid   = 0;
   fPriority  = kNormalPriority;
   fThreadArg = NULL;
   Constructor();
   fNamed     = kFALSE;
   fId = (id ? id : SelfId());
   fState = kRunningState;

   if (gDebug) Info("Thread", "%s.%ld is running", GetName(), fId);
}

//______________________________________________________________________________
void TThread::Constructor()
{
   // Common thread constructor.

   fHolder = 0;
   fClean  = 0;
   fState  = kNewState;

   fId = 0;
   if (!fgThreadImp) { // *** Only once ***
      fgThreadImp = gThreadFactory->CreateThreadImp();
      fgMainMutex = new TMutex(kTRUE);
      fgXActMutex = new TMutex(kTRUE);
      new TThreadTimer;
      fgXActCondi = new TCondition;
      gThreadTsd  = &(TThread::Tsd);
      gThreadXAR  = &(TThread::XARequest);
   }

   PutComm("Constructor: MainMutex Locking");
   Lock();
   PutComm("Constructor: MainMutex Locked");
   fTsd[0] = gPad;

   if (fgMain) fgMain->fPrev = this;
   fNext = fgMain; fPrev=0; fgMain = this;

   // Create the global mutexes
   if (!gContainerMutex)
     gContainerMutex = new TMutex(kTRUE);
   if (!gCINTMutex)
     gCINTMutex = new TMutex(kTRUE);


   UnLock();
   PutComm();

   // thread is set up in initialisation routine or Run().
}

//______________________________________________________________________________
TThread::~TThread()
{
   // Cleanup the thread.

   char cbuf[100];
   if (gDebug) {
      sprintf(cbuf, "Info in <TThread::~TThread>: %s.%ld DELETED\n",
              GetName(), fId);
      TThread::Printf("%s", cbuf);
   }

   // Disconnect thread instance

   PutComm("Destructor: MainMutex Locking");
   Lock();
   PutComm("Destructor: MainMutex Locked");
   if (fPrev) fPrev->fNext = fNext;
   if (fNext) fNext->fPrev = fPrev;
   if (fgMain == this) fgMain = fNext;
   UnLock();
   PutComm();
   if (fHolder) *(fHolder) = 0;
}

//______________________________________________________________________________
Int_t TThread::Delete(TThread *&th)
{
   char cbuf[100];

   if (!th) return 0;
   th->fHolder = &th;

   if (th->fState == kRunningState) {     // Cancel if running
      th->fState = kDeletingState;
      if (gDebug) {
         sprintf(cbuf,"Info in <TThread::Delete>: %s.%ld deleting\n",
                 th->GetName(), th->fId);
         TThread::Printf("%s", cbuf);
      }
      th->Kill();
      return -1;
   }

   th->CleanUp();
   return 0;
}

//______________________________________________________________________________
void TThread::Debug(const char *txt)
{
   ::Info("TThread::Debug", "%s %ld", txt, fgMain->fId);
}

//______________________________________________________________________________
Int_t TThread::Exists()
{
   // Check existing threads
   // return number of running Threads

   TThread *l;

   if (!fgMain) { //no threads
      return 0;
   }

   Int_t num = 0;
   for (l = fgMain; l; l = l->fNext)
      num++; //count threads
   return num;
}

//______________________________________________________________________________
void TThread::SetPriority(EPriority pri)
{
   // Set thread priority.

   fPriority = pri;
}

//______________________________________________________________________________
void TThread::SetJoinId(Long_t jid)
{
   // Set id of thread to join.

   fJoinId = jid;
}

//______________________________________________________________________________
void TThread::SetJoinId(TThread *tj)
{
   // Set thread to join.

   SetJoinId(tj->fId);
}

//______________________________________________________________________________
TThread *TThread::GetThread(Long_t id)
{
   // Static method to find a thread by id.

   TThread *myth;

   for (myth = fgMain; myth && (myth->fId != id); myth=myth->fNext) { }
   return myth;
}

//______________________________________________________________________________
TThread *TThread::GetThread(const char *name)
{
   // Static method to find a thread by name.

   TThread *myth;

   for (myth = fgMain; myth && (strcmp(name,myth->GetName())); myth=myth->fNext) { }
   return myth;
}

//______________________________________________________________________________
TThread *TThread::Self()
{
   // Static method returning pointer to current thread.

   return GetThread(SelfId());
}

//______________________________________________________________________________
Long_t TThread::Join(void **ret)
{
   // Static method to join thread. (rdm?)

   return TThread::Join(0, ret);
}

//______________________________________________________________________________
Long_t TThread::Join(Long_t jid, void **ret)
{
   Long_t jd = jid;
   if (!jd) {  // jd is not known
      TThread *myth = TThread::Self();
      if (!myth) return -1L;
      jd = myth->fJoinId;
      if (!jd) return -1L;
   }
   return fgThreadImp->Join(jd,ret);
}

//______________________________________________________________________________
Long_t TThread::SelfId()
{
   if (!fgThreadImp) return -1L;
   Long_t id = fgThreadImp->SelfId();
   if (!id) id = -1L;  // in some implementations 0 is main thread
   return id;
}

//______________________________________________________________________________
Int_t TThread::Run(void *arg)
{
   char *fname = 0;

   if (arg) fThreadArg = arg;
   PutComm("Run: MainMutex locking");
   Lock();
   PutComm("Run: MainMutex locked");
   if (fFcnVoid) fname=G__p2f2funcname((void*)fFcnVoid);
   if (fFcnRetn) fname=G__p2f2funcname((void*)fFcnRetn);
   if (!fNamed)
      if (fname) SetName(fname);

   int iret = fgThreadImp->Run(this);

   fState = iret ? kInvalidState : kRunningState;
   if (gDebug) {
      Info("Run", "thread %s id=%ld requested", GetName(), fId);
   }
   UnLock();
   PutComm();
   return iret;
}

//______________________________________________________________________________
Int_t TThread::Kill()
{
   if (fState != kRunningState && fState != kDeletingState) {
      if (gDebug) {
         Info("Kill", "thread %ld is not running", fId);
      }
      return 13;
   } else {
      if (fState == kRunningState ) fState = kCancelingState;
      return fgThreadImp->Kill(this);
   }
}

//______________________________________________________________________________
Int_t TThread::Kill(Long_t id)
{
   TThread *th = GetThread(id);
   if (th) {
      return fgThreadImp->Kill(th);
   } else  {
      if (gDebug) {
         ::Info("TThread::Kill(Long_t)", "thread %ld not found ***", id);
      }
      return 13;
   }
}

//______________________________________________________________________________
Int_t TThread::Kill(const char *name)
{
   TThread *th = GetThread(name);
   if (th) {
      return fgThreadImp->Kill(th);
   } else  {
      if (gDebug) {
         ::Info("TThread::Kill(const char*)", "thread %s not found ***", name);
      }
      return 13;
   }
}

Int_t TThread::SetCancelOff() { return fgThreadImp->SetCancelOff(); }
Int_t TThread::SetCancelOn() { return fgThreadImp->SetCancelOn(); }
Int_t TThread::SetCancelAsynchronous() { return fgThreadImp->SetCancelAsynchronous(); }
Int_t TThread::SetCancelDeferred() { return fgThreadImp->SetCancelDeferred(); }
Int_t TThread::CancelPoint() { return fgThreadImp->CancelPoint(); }

//______________________________________________________________________________
Int_t TThread::CleanUpPush(void *free, void *arg)
{
   return fgThreadImp->CleanUpPush(&(Self()->fClean), free, arg);
}

//______________________________________________________________________________
Int_t TThread::CleanUpPop(Int_t exe)
{
   return fgThreadImp->CleanUpPop(&(Self()->fClean), exe);
}

//______________________________________________________________________________
Int_t TThread::CleanUp()
{
   TThread *th = Self();
   if (!th) return 13;

   fgThreadImp->CleanUp(&(Self()->fClean));
   th->fgMainMutex->CleanUp();
   th->fgXActMutex->CleanUp();
   if (th->fHolder) { // It was kill from delete
      delete th;
   }
   return 0;
}

//______________________________________________________________________________
void TThread::AfterCancel(TThread *th)
{
   th->fState = kCanceledState;
   if (gDebug) {
      char cbuf[100];
      sprintf(cbuf,"Info in <TThread::AfterCancel>: %s.%ld is killed\n",
              th->GetName(), th->fId);
      TThread::Printf("%s", cbuf);
   }
}

//______________________________________________________________________________
Int_t TThread::Exit(void *ret)
{
   return fgThreadImp->Exit(ret);
}

//______________________________________________________________________________
Int_t TThread::Sleep(ULong_t secs, ULong_t nanos)
{
   return fgThreadImp->Sleep(secs, nanos);
}

//______________________________________________________________________________
Int_t TThread::GetTime(ULong_t *absSec, ULong_t *absNanoSec)
{
   return fgThreadImp->GetTime(absSec, absNanoSec);
}

Int_t TThread::Lock()    { return (fgMainMutex ? fgMainMutex->Lock() : 0);}    // lock main mutex
Int_t TThread::TryLock() { return (fgMainMutex ? fgMainMutex->TryLock(): 0); } // lock main mutex
Int_t TThread::UnLock()  { return (fgMainMutex ? fgMainMutex->UnLock() :0); }  // unlock main mutex

//______________________________________________________________________________
ULong_t TThread::Call(void *p2f, void *arg)
{
   char *fname;
   void *iPointer2Function;
   int iPointerType = G__UNKNOWNFUNC;
   G__CallFunc func;
   {
      R__LOCKGUARD(gCINTMutex);
      // reconstruct function name
      fname = G__p2f2funcname(p2f);
      iPointer2Function = p2f;
      if (fname) {
         G__ClassInfo globalscope;
         G__MethodInfo method;
         Long_t dummy;

         // resolve function overloading
         method=globalscope.GetMethod(fname, "void*", &dummy);
         if (method.IsValid()) {
            // get pointer to function again after overloading resolution
            iPointer2Function = method.PointerToFunc();
            // check what kind of pointer is it
            iPointerType = G__isinterpretedp2f(iPointer2Function);

            switch(iPointerType) {

               case G__COMPILEDINTERFACEMETHOD: // using interface method
                  func.SetFunc((G__InterfaceMethod)iPointer2Function);
                  break;

               case G__BYTECODEFUNC: // bytecode version of interpreted func
                  func.SetBytecode((struct G__bytecodefunc*)iPointer2Function);
                  break;
            }
         } else {
            ::Error("TThread:Call", "no overloading parameter matches");
         }
      }
   }

   // check what kind of pointer is it
   switch(iPointerType) {

      case G__INTERPRETEDFUNC: // reconstruct function call as string
         char temp[20],para[200];
         strcpy(para, (char*)iPointer2Function);
         strcat(para,"(");
         sprintf(temp,"(void*)%#lx", (ULong_t)arg);
         strcat(para, temp);
         strcat(para, ")");
         return G__int(G__calc(para));

      case G__COMPILEDINTERFACEMETHOD: // using interface method
      case G__BYTECODEFUNC:            // bytecode version of interpreted func
         func.SetArg((long)arg);
         return func.ExecInt((void*)0);

      case G__COMPILEDTRUEFUNC: // using true pointer to function
      case G__UNKNOWNFUNC: // this case will never happen
         return (*(int (*)(void*))iPointer2Function)(arg);
   }

   ::Error("TThread::Call", "*** Something very WRONG ***");

   return (ULong_t) -1L;
}

//______________________________________________________________________________
void *TThread::Fun(void *ptr)
{
   TThread *th;
   void *ret,*arg;
   char cbuf[100];

   TThreadCleaner dummy;

   th = (TThread *)ptr;
   th->fId = SelfId();
   th->SetUniqueID(th->fId);

   // Default cancel state is OFF
   // Default cancel type  is DEFERRED
   // User can change it by call SetCancelOn() and SetCancelAsynchronous()
   SetCancelOff();
   SetCancelDeferred();
   CleanUpPush((void *)&AfterCancel,th);  // Enable standard cancelling function

   if (gDebug) {
      sprintf(cbuf, "Info in <TThread::Fun>: %s.%ld is running\n",
              th->GetName(), th->fId);
      TThread::Printf("%s", cbuf);
   }

   arg = th->fThreadArg;
   th->fState = kRunningState;

   if (th->fDetached) {
      //Detached, non joinable thread
      TThread::Call((void*)th->fFcnVoid, arg);
      ret = 0;
      th->fState = kFinishedState;
   } else {
      //UnDetached, joinable thread
      ret = (void*)TThread::Call((void*)th->fFcnRetn, arg);
      th->fState = kTerminatedState;
   }

   CleanUpPop(1);     // Disable standard cancelling function

   if (gDebug) {
      sprintf(cbuf,"Info in <TThread::Fun>: %s.%ld has finished\n",
              th->GetName(), th->fId);
      TThread::Printf("%s", cbuf);
   }

   TThread::Exit(ret);

   return ret;
}

//______________________________________________________________________________
void TThread::Ps()
{
   // List existing threads.

   TThread *l;
   int i;

   if (!fgMain) { //no threads
      Info("Ps", "*** No threads, ***");
      return;
   }

   Lock();

   int num = 0;
   for (l = fgMain; l; l = l->fNext)
      num++;

   char cbuf[100];
   printf("     Thread         State\n");
   for (l = fgMain; l; l = l->fNext) { // loop over threads
      memset(cbuf,' ',100);
      sprintf(cbuf, "%3d  %s.%ld",num--,l->GetName(),l->fId);
      i = strlen(cbuf);
      if (i<20)
         cbuf[i]=' ';
      cbuf[20]=0;
      printf("%20s",cbuf);

      switch (l->fState) {   // print states

         case kNewState:        printf("Idle       "); break;
         case kRunningState:    printf("Running    "); break;
         case kTerminatedState: printf("Terminated "); break;
         case kFinishedState:   printf("Finished   "); break;
         case kCancelingState:  printf("Canceling  "); break;
         case kCanceledState:   printf("Canceled   "); break;
         case kDeletingState:   printf("Deleting   "); break;
         default:               printf("Invalid    ");
      }
      if (l->fComm[0]) printf("  // %s\n",l->fComm);
      printf("\n");
   }  // end of loop

   UnLock();
}

//______________________________________________________________________________
void **TThread::Tsd(void *dflt,Int_t k)
{
   TThread *th = TThread::Self();

   if (!th) {   //Main thread
      return (void**)dflt;
   } else {
      return &(th->fTsd[k]);
   }
}

//______________________________________________________________________________
void TThread::Printf(const char *txt)
{
   Printf(txt,0L);
}

//______________________________________________________________________________
void TThread::Printf(const char *txt, Long_t in)
{
   void *arr[3];

   arr[1] = (void*)txt; arr[2] = &in;
   if (XARequest("PRTF", 3, arr, 0)) return;

   fprintf(stderr, txt, in);
}

//______________________________________________________________________________
Int_t TThread::XARequest(const char *xact, Int_t nb, void **ar, Int_t *iret)
{
   if (!gApplication || !gApplication->IsRunning()) return 0;

   TThread *th;
   if ((th = Self())) { // we are in the thread

      TConditionImp *condimp = fgXActCondi->fConditionImp;
      TMutexImp *condmutex = fgXActCondi->GetMutex()->fMutexImp;

      th->PutComm("XARequest: XActMutex Locking");
      fgXActMutex->Lock();
      th->PutComm("XARequest: XActMutex Locked");

      // Mathieu de Naurois
      // Lock now, so the XAction signal will wait
      // and never come before the wait
      condmutex->Lock();

      fgXAnb = nb;
      fgXArr = ar;
      fgXArt = 0;
      fgXAct = (char*)xact;
      th->PutComm((char *)fgXAct);

      if (condimp) condimp->Wait();
      condmutex->UnLock();

      th->PutComm();
      if (iret) *iret = fgXArt;
      fgXActMutex->UnLock();
      th->PutComm();
      return 1997;
   } else            //we are in the main thread
      return 0;
}

//______________________________________________________________________________
void TThread::XAction()
{
   TConditionImp *condimp = fgXActCondi->fConditionImp;
   TMutexImp *condmutex = fgXActCondi->GetMutex()->fMutexImp;
   condmutex->Lock();

   char const acts[] = "PRTF CUPD CANV CDEL PDCD METH";
   enum {kPRTF=0,kCUPD=5,kCANV=10,kCDEL=15,kPDCD=20,kMETH=25};
   int iact = strstr(acts,fgXAct) - acts;
   char *cmd = 0;

   switch (iact) {

      case kPRTF:
         fprintf(stderr,(char*)fgXArr[1],*((Int_t*)(fgXArr[2])));
         break;

      case kCUPD:
         //((TCanvas *)fgXArr[1])->Update();
         cmd = Form("((TCanvas *)0x%lx)->Update();",(Long_t)fgXArr[1]);
         gROOT->ProcessLine(cmd);
         break;

      case kCANV:

         switch(fgXAnb) { // Over TCanvas constructors

            case 2:
               //((TCanvas*)fgXArr[1])->Constructor();
               cmd = Form("((TCanvas *)0x%lx)->Constructor();",(Long_t)fgXArr[1]);
               gROOT->ProcessLine(cmd);
               break;

            case 5:
               //((TCanvas*)fgXArr[1])->Constructor(
               //                 (Text_t*)fgXArr[2],
               //                 (Text_t*)fgXArr[3],
               //                *((Int_t*)(fgXArr[4])));
               cmd = Form("((TCanvas *)0x%lx)->Constructor((Text_t*)0x%lx,(Text_t*)0x%lx,*((Int_t*)(0x%lx)));",(Long_t)fgXArr[1],(Long_t)fgXArr[2],(Long_t)fgXArr[3],(Long_t)fgXArr[4]);
               gROOT->ProcessLine(cmd);
               break;
            case 6:
               //((TCanvas*)fgXArr[1])->Constructor(
               //                 (char*)fgXArr[2],
               //                 (char*)fgXArr[3],
               //                *((Int_t*)(fgXArr[4])),
               //                *((Int_t*)(fgXArr[5])));
               cmd = Form("((TCanvas *)0x%lx)->Constructor((Text_t*)0x%lx,(Text_t*)0x%lx,*((Int_t*)(0x%lx)),*((Int_t*)(0x%lx)));",(Long_t)fgXArr[1],(Long_t)fgXArr[2],(Long_t)fgXArr[3],(Long_t)fgXArr[4],(Long_t)fgXArr[5]);
               gROOT->ProcessLine(cmd);
               break;

            case 8:
               //((TCanvas*)fgXArr[1])->Constructor(
               //                 (char*)fgXArr[2],
               //                 (char*)fgXArr[3],
               //               *((Int_t*)(fgXArr[4])),
               //               *((Int_t*)(fgXArr[5])),
               //               *((Int_t*)(fgXArr[6])),
               //               *((Int_t*)(fgXArr[7])));
               cmd = Form("((TCanvas *)0x%lx)->Constructor((Text_t*)0x%lx,(Text_t*)0x%lx,*((Int_t*)(0x%lx)),*((Int_t*)(0x%lx)),*((Int_t*)(0x%lx)),*((Int_t*)(0x%lx)));",(Long_t)fgXArr[1],(Long_t)fgXArr[2],(Long_t)fgXArr[3],(Long_t)fgXArr[4],(Long_t)fgXArr[5],(Long_t)fgXArr[6],(Long_t)fgXArr[7]);
               gROOT->ProcessLine(cmd);
               break;

         }
         break;

      case kCDEL:
         //((TCanvas*)fgXArr[1])->Destructor();
         cmd = Form("((TCanvas *)0x%lx)->Destructor();",(Long_t)fgXArr[1]);
         gROOT->ProcessLine(cmd);
         break;

      case kPDCD:
         ((TVirtualPad*) fgXArr[1])->Divide( *((Int_t*)(fgXArr[2])),
                                  *((Int_t*)(fgXArr[3])),
                                  *((Float_t*)(fgXArr[4])),
                                  *((Float_t*)(fgXArr[5])),
                                  *((Int_t*)(fgXArr[6])));
         break;
      case kMETH:
         ((TMethodCall *) fgXArr[1])->Execute((void*)(fgXArr[2]),(const char*)(fgXArr[3]));
         break;

      default:
         ::Error("TThread::XAction", "wrong case");
   }

   fgXAct = 0;
   if (condimp) condimp->Signal();
   condmutex->UnLock();
}

//______________________________________________________________________________
Int_t TThread::MakeFun(char *funname)
{
   int iret;
   char cbuf[100];
#ifndef ROOTDATADIR
   sprintf(cbuf,"make -f $ROOTSYS/thread/MakeFun.mk fun=%s",funname);
#else
   sprintf(cbuf,"make -f " ROOTDATADIR "/thread/MakeFun.mk fun=%s",funname);
#endif
   if ((iret = gSystem->Exec(cbuf))) return iret;
   sprintf(cbuf,"%s.so",funname);
   return gSystem->Load(cbuf);
}




//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TThreadTimer                                                         //
//                                                                      //
//////////////////////////////////////////////////////////////////////////


//______________________________________________________________________________
TThreadTimer::TThreadTimer(Long_t ms) : TTimer(ms, kTRUE)
{
   gSystem->AddTimer(this);
}

//______________________________________________________________________________
Bool_t TThreadTimer::Notify()
{

   if (TThread::fgXAct) { TThread::XAction(); }
   Reset();

   return kFALSE;
}


//////////////////////////////////////////////////////////////////////////
//                                                                      //
//  TThreadCleaner                                                      //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

//______________________________________________________________________________
TThreadCleaner::~TThreadCleaner()
{
   TThread::CleanUp(); // Call user clean up routines
}
