ROOT logo
// $Id: GThread.cxx 2316 2010-01-01 11:23:28Z matevz $

// Copyright (C) 1999-2008, Matevz Tadel. All rights reserved.
// This file is part of GLED, released under GNU General Public License version 2.
// For the licensing terms see $GLEDSYS/LICENSE or http://www.gnu.org/.

#include "GThread.h"
#include <Glasses/ZMirEmittingEntity.h>

#include <pthread.h>
#include <signal.h>
#include <unistd.h>
#include <stdio.h>

//______________________________________________________________________________
//
// POSIX thread wrapper class - Gled specific as it provides
// data members required for authorization and MIR processing.
//

ClassImp(GThread);

GThread*                 GThread::sMainThread   = 0;
bool                     GThread::sMainInitDone = false;
int                      GThread::sThreadCount  = 0;
map<pthread_t, GThread*> GThread::sThreadMap;
list<GThread*>           GThread::sThreadList;
GMutex                   GThread::sContainerLock;
int                      GThread::sMinStackSize = 0;

pthread_key_t GThread::TSD_Self;

/**************************************************************************/
GThread::GThread(const Text_t* name) :
  mRunningState (RS_Running),
  mIndex        (-1),
  mThreadListIt (),
  mId           (0),

  mName     (name),
  mStartFoo (0), mStartArg (0),
  mEndFoo   (0), mEndArg   (0),
  bDetached (false),
  mNice     (0),
  mStackSize(0),

  mOwner(0), mMIR(0)
{
  // Private constructor for wrapping of existing threads.
  // Thread is put into 'Running' state, registered into the thread list
  // and assigned an internal thread-index.
  // The owner of the thread is set to 0.

  sContainerLock.Lock();
  mThreadListIt = sThreadList.insert(sThreadList.end(), this);
  mIndex        = ++sThreadCount;
  sContainerLock.Unlock();
}

GThread::GThread(const Text_t* name, GThread_foo foo, void* arg, bool detached) :
  mRunningState (RS_Incubating),
  mIndex        (-1),
  mThreadListIt (),
  mId           (0),

  mName     (name),
  mStartFoo (foo), mStartArg (arg),
  mEndFoo   (0),   mEndArg   (0),
  bDetached (detached),
  mNice     (0),
  mStackSize(sMinStackSize),

  mOwner(Owner()), mMIR(0)
{
  // Normal constructor.
  // Thread is put into 'Incubating' state, registered into the thread list
  // and assigned an internal thread-index.
  // The owner of the thread is set be the same as the owner of the calling thread.

  static const Exc_t _eh("GThread::GThread ");

  if (!sMainThread)
    throw(_eh + "InitMain() not called.");

  sContainerLock.Lock();
  mThreadListIt = sThreadList.insert(sThreadList.end(), this);
  mIndex        = ++sThreadCount;
  sContainerLock.Unlock();
}

GThread::~GThread()
{
  // Destructor.
  // Thread is unregistered from the thread list.

  sContainerLock.Lock();
  sThreadList.erase(mThreadListIt);
  sContainerLock.Unlock();
}

/**************************************************************************/

void* GThread::thread_spawner(void* arg)
{
  GThread* self = (GThread*) arg;

  pthread_setspecific(TSD_Self, self);

  if (self->mNice)
  {
    nice(self->mNice);
  }

  self->mId = pthread_self();

  sContainerLock.Lock();
  sThreadMap[self->mId] = self;
  self->mRunningState   = RS_Running;
  sContainerLock.Unlock();

  void* ret = 0;

  pthread_cleanup_push(thread_reaper, self);

  ret = (self->mStartFoo)(self->mStartArg);

  pthread_cleanup_pop(1);

  return ret;
}

void GThread::thread_reaper(void* arg)
{
  GThread* self = (GThread*) arg;

  sContainerLock.Lock();
  self->mRunningState = RS_Terminating;
  sContainerLock.Unlock();

  if (self->mEndFoo != 0)
  {
    (self->mEndFoo)(self->mEndArg);
  }

  sContainerLock.Lock();
  sThreadMap.erase(self->mId);
  self->mRunningState = RS_Finished;
  sContainerLock.Unlock();

  if (self->bDetached)
    delete self;
}

int GThread::Spawn()
{
  static const Exc_t _eh("GThread::Spawn ");

  pthread_attr_t attr;
  pthread_attr_init(&attr);
  if (bDetached)
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

  size_t stacksize;
  pthread_attr_getstacksize(&attr, &stacksize);
  if (mStackSize && (Int_t) stacksize < mStackSize)
  {
    ISmess(_eh + "increasing stack size for thread '" + mName +"' to " +
	   GForm("%dkB (was %dkB).", mStackSize/1024, (Int_t)stacksize/1024));
    pthread_attr_setstacksize(&attr, mStackSize);
  }

  sContainerLock.Lock();
  mRunningState = RS_Spawning;
  sContainerLock.Unlock();

  int ret = pthread_create(&mId, &attr, thread_spawner, this);
  if (ret)
  {
    sContainerLock.Lock();
    mRunningState = RS_ErrorSpawning;
    sContainerLock.Unlock();
    perror("GThread::Spawn");
  }

  return ret;
}

int GThread::Join(void** tret)
{
  int ret = pthread_join(mId, tret);
  if (ret) perror("GThread::Join");
  return ret;
}

int GThread::Kill(Signal signal)
{
  int ret = pthread_kill(mId, (Int_t)signal);
  if(ret) perror("GThread::Kill");
  return ret;
}

int GThread::Cancel()
{
  int ret = pthread_cancel(mId);
  if(ret) perror("GThread::Cancel");
  return ret;
}

int GThread::Detach()
{
  int ret = pthread_detach(mId);
  if(ret) perror("GThread::Detach");
  else    bDetached = true;
  return ret;
}

/**************************************************************************/
// Static functions; to be called from within thread.
/**************************************************************************/

GThread::CState GThread::SetCancelState(CState s)
{
  int ex_val;
  if(s == CS_Enable)
    pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,  &ex_val);
  else
    pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &ex_val);
  return ex_val == PTHREAD_CANCEL_ENABLE ? CS_Enable : CS_Disable;
}

/**************************************************************************/

GThread::CType GThread::SetCancelType(CType t)
{
  int ex_val;
  if(t == CT_Async)
    pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &ex_val);
  else
    pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,     &ex_val);
  return ex_val == PTHREAD_CANCEL_DEFERRED ? CT_Deferred : CT_Async;
}

/**************************************************************************/

void GThread::TestCancel()
{
  pthread_testcancel();
}

void GThread::Exit(void* ret)
{
  pthread_exit(ret);
}

/**************************************************************************/

GThread* GThread::Self()
{
  // Static - returns current thread.

  return (GThread*) pthread_getspecific(TSD_Self);
}

ZMirEmittingEntity* GThread::Owner()
{
  // Static - returns owner of current thread.

  return Self()->mOwner;
}

ZMIR* GThread::MIR()
{
  // Static - returns MIR processed in current thread.

  return Self()->mMIR;
}

/**************************************************************************/

const char* GThread::RunningStateName(RState state)
{
  switch (state)
  {
    case RS_Incubating:    return "Incubating";
    case RS_Spawning:      return "Spawning";
    case RS_Running:       return "Running";
    case RS_Terminating:   return "Terminating";
    case RS_Finished:      return "Finished";
    case RS_ErrorSpawning: return "ErrorSpawning";
    default:               return "<unknown>";
  }
}

void GThread::ListThreads()
{
  if (sMainThread == 0)
  {
    printf("Threads not initialized.");
    return;
  }

  printf("+------------------------------------------------------------------------+\n");
  sContainerLock.Lock();
  Int_t count = 0;
  for (lpGThread_i i = sThreadList.begin(); i != sThreadList.end(); ++i)
  {
    const GThread& t = **i;
    printf("| %2d | %4d | %-24s | %-14s | %-14s |\n", ++count,
           t.mIndex, t.mName.Data(), RunningStateName(t.mRunningState),
           t.get_owner() ? t.get_owner()->GetName() : "<null>");
  }
  sContainerLock.Unlock();
  printf("+------------------------------------------------------------------------+\n");

}

//==============================================================================

GThread* GThread::InitMain()
{
  // This will create a GThread wrapper around the calling thread.
  // To be called from ::main thread, somewhere early during the
  // system initialization.

  static const Exc_t _eh("GThread::InitMain ");

  if (sMainThread)
  {
    throw _eh + " already called.";
  }

  pthread_key_create(&TSD_Self, 0);

  sMainThread = new GThread("main");

  pthread_setspecific(TSD_Self, sMainThread);

  sMainThread->mId = pthread_self();

  sContainerLock.Lock();
  sThreadMap[sMainThread->mId]  = sMainThread;
  sContainerLock.Unlock();

  return sMainThread;
}

void GThread::FiniMain()
{
  static const Exc_t _eh("GThread::FiniMain ");

  if (! sMainThread)
  {
    throw _eh + "InitMain() not called.";
  }
  if (Self() != sMainThread)
  {
    throw _eh + "not called from main thread.";
  }

  sContainerLock.Lock();

  sThreadMap.erase(sMainThread->mId);

  sMainThread->mId = 0;
  sMainThread->mRunningState = RS_Finished;
  pthread_setspecific(TSD_Self, 0);

  sContainerLock.Unlock();

  delete sMainThread;
  sMainThread = 0;
}

//==============================================================================

int GThread::GetMinStackSize()
{
  return sMinStackSize;
}

void GThread::SetMinStackSize(int ss)
{
  sMinStackSize = ss;
}

 GThread.cxx:1
 GThread.cxx:2
 GThread.cxx:3
 GThread.cxx:4
 GThread.cxx:5
 GThread.cxx:6
 GThread.cxx:7
 GThread.cxx:8
 GThread.cxx:9
 GThread.cxx:10
 GThread.cxx:11
 GThread.cxx:12
 GThread.cxx:13
 GThread.cxx:14
 GThread.cxx:15
 GThread.cxx:16
 GThread.cxx:17
 GThread.cxx:18
 GThread.cxx:19
 GThread.cxx:20
 GThread.cxx:21
 GThread.cxx:22
 GThread.cxx:23
 GThread.cxx:24
 GThread.cxx:25
 GThread.cxx:26
 GThread.cxx:27
 GThread.cxx:28
 GThread.cxx:29
 GThread.cxx:30
 GThread.cxx:31
 GThread.cxx:32
 GThread.cxx:33
 GThread.cxx:34
 GThread.cxx:35
 GThread.cxx:36
 GThread.cxx:37
 GThread.cxx:38
 GThread.cxx:39
 GThread.cxx:40
 GThread.cxx:41
 GThread.cxx:42
 GThread.cxx:43
 GThread.cxx:44
 GThread.cxx:45
 GThread.cxx:46
 GThread.cxx:47
 GThread.cxx:48
 GThread.cxx:49
 GThread.cxx:50
 GThread.cxx:51
 GThread.cxx:52
 GThread.cxx:53
 GThread.cxx:54
 GThread.cxx:55
 GThread.cxx:56
 GThread.cxx:57
 GThread.cxx:58
 GThread.cxx:59
 GThread.cxx:60
 GThread.cxx:61
 GThread.cxx:62
 GThread.cxx:63
 GThread.cxx:64
 GThread.cxx:65
 GThread.cxx:66
 GThread.cxx:67
 GThread.cxx:68
 GThread.cxx:69
 GThread.cxx:70
 GThread.cxx:71
 GThread.cxx:72
 GThread.cxx:73
 GThread.cxx:74
 GThread.cxx:75
 GThread.cxx:76
 GThread.cxx:77
 GThread.cxx:78
 GThread.cxx:79
 GThread.cxx:80
 GThread.cxx:81
 GThread.cxx:82
 GThread.cxx:83
 GThread.cxx:84
 GThread.cxx:85
 GThread.cxx:86
 GThread.cxx:87
 GThread.cxx:88
 GThread.cxx:89
 GThread.cxx:90
 GThread.cxx:91
 GThread.cxx:92
 GThread.cxx:93
 GThread.cxx:94
 GThread.cxx:95
 GThread.cxx:96
 GThread.cxx:97
 GThread.cxx:98
 GThread.cxx:99
 GThread.cxx:100
 GThread.cxx:101
 GThread.cxx:102
 GThread.cxx:103
 GThread.cxx:104
 GThread.cxx:105
 GThread.cxx:106
 GThread.cxx:107
 GThread.cxx:108
 GThread.cxx:109
 GThread.cxx:110
 GThread.cxx:111
 GThread.cxx:112
 GThread.cxx:113
 GThread.cxx:114
 GThread.cxx:115
 GThread.cxx:116
 GThread.cxx:117
 GThread.cxx:118
 GThread.cxx:119
 GThread.cxx:120
 GThread.cxx:121
 GThread.cxx:122
 GThread.cxx:123
 GThread.cxx:124
 GThread.cxx:125
 GThread.cxx:126
 GThread.cxx:127
 GThread.cxx:128
 GThread.cxx:129
 GThread.cxx:130
 GThread.cxx:131
 GThread.cxx:132
 GThread.cxx:133
 GThread.cxx:134
 GThread.cxx:135
 GThread.cxx:136
 GThread.cxx:137
 GThread.cxx:138
 GThread.cxx:139
 GThread.cxx:140
 GThread.cxx:141
 GThread.cxx:142
 GThread.cxx:143
 GThread.cxx:144
 GThread.cxx:145
 GThread.cxx:146
 GThread.cxx:147
 GThread.cxx:148
 GThread.cxx:149
 GThread.cxx:150
 GThread.cxx:151
 GThread.cxx:152
 GThread.cxx:153
 GThread.cxx:154
 GThread.cxx:155
 GThread.cxx:156
 GThread.cxx:157
 GThread.cxx:158
 GThread.cxx:159
 GThread.cxx:160
 GThread.cxx:161
 GThread.cxx:162
 GThread.cxx:163
 GThread.cxx:164
 GThread.cxx:165
 GThread.cxx:166
 GThread.cxx:167
 GThread.cxx:168
 GThread.cxx:169
 GThread.cxx:170
 GThread.cxx:171
 GThread.cxx:172
 GThread.cxx:173
 GThread.cxx:174
 GThread.cxx:175
 GThread.cxx:176
 GThread.cxx:177
 GThread.cxx:178
 GThread.cxx:179
 GThread.cxx:180
 GThread.cxx:181
 GThread.cxx:182
 GThread.cxx:183
 GThread.cxx:184
 GThread.cxx:185
 GThread.cxx:186
 GThread.cxx:187
 GThread.cxx:188
 GThread.cxx:189
 GThread.cxx:190
 GThread.cxx:191
 GThread.cxx:192
 GThread.cxx:193
 GThread.cxx:194
 GThread.cxx:195
 GThread.cxx:196
 GThread.cxx:197
 GThread.cxx:198
 GThread.cxx:199
 GThread.cxx:200
 GThread.cxx:201
 GThread.cxx:202
 GThread.cxx:203
 GThread.cxx:204
 GThread.cxx:205
 GThread.cxx:206
 GThread.cxx:207
 GThread.cxx:208
 GThread.cxx:209
 GThread.cxx:210
 GThread.cxx:211
 GThread.cxx:212
 GThread.cxx:213
 GThread.cxx:214
 GThread.cxx:215
 GThread.cxx:216
 GThread.cxx:217
 GThread.cxx:218
 GThread.cxx:219
 GThread.cxx:220
 GThread.cxx:221
 GThread.cxx:222
 GThread.cxx:223
 GThread.cxx:224
 GThread.cxx:225
 GThread.cxx:226
 GThread.cxx:227
 GThread.cxx:228
 GThread.cxx:229
 GThread.cxx:230
 GThread.cxx:231
 GThread.cxx:232
 GThread.cxx:233
 GThread.cxx:234
 GThread.cxx:235
 GThread.cxx:236
 GThread.cxx:237
 GThread.cxx:238
 GThread.cxx:239
 GThread.cxx:240
 GThread.cxx:241
 GThread.cxx:242
 GThread.cxx:243
 GThread.cxx:244
 GThread.cxx:245
 GThread.cxx:246
 GThread.cxx:247
 GThread.cxx:248
 GThread.cxx:249
 GThread.cxx:250
 GThread.cxx:251
 GThread.cxx:252
 GThread.cxx:253
 GThread.cxx:254
 GThread.cxx:255
 GThread.cxx:256
 GThread.cxx:257
 GThread.cxx:258
 GThread.cxx:259
 GThread.cxx:260
 GThread.cxx:261
 GThread.cxx:262
 GThread.cxx:263
 GThread.cxx:264
 GThread.cxx:265
 GThread.cxx:266
 GThread.cxx:267
 GThread.cxx:268
 GThread.cxx:269
 GThread.cxx:270
 GThread.cxx:271
 GThread.cxx:272
 GThread.cxx:273
 GThread.cxx:274
 GThread.cxx:275
 GThread.cxx:276
 GThread.cxx:277
 GThread.cxx:278
 GThread.cxx:279
 GThread.cxx:280
 GThread.cxx:281
 GThread.cxx:282
 GThread.cxx:283
 GThread.cxx:284
 GThread.cxx:285
 GThread.cxx:286
 GThread.cxx:287
 GThread.cxx:288
 GThread.cxx:289
 GThread.cxx:290
 GThread.cxx:291
 GThread.cxx:292
 GThread.cxx:293
 GThread.cxx:294
 GThread.cxx:295
 GThread.cxx:296
 GThread.cxx:297
 GThread.cxx:298
 GThread.cxx:299
 GThread.cxx:300
 GThread.cxx:301
 GThread.cxx:302
 GThread.cxx:303
 GThread.cxx:304
 GThread.cxx:305
 GThread.cxx:306
 GThread.cxx:307
 GThread.cxx:308
 GThread.cxx:309
 GThread.cxx:310
 GThread.cxx:311
 GThread.cxx:312
 GThread.cxx:313
 GThread.cxx:314
 GThread.cxx:315
 GThread.cxx:316
 GThread.cxx:317
 GThread.cxx:318
 GThread.cxx:319
 GThread.cxx:320
 GThread.cxx:321
 GThread.cxx:322
 GThread.cxx:323
 GThread.cxx:324
 GThread.cxx:325
 GThread.cxx:326
 GThread.cxx:327
 GThread.cxx:328
 GThread.cxx:329
 GThread.cxx:330
 GThread.cxx:331
 GThread.cxx:332
 GThread.cxx:333
 GThread.cxx:334
 GThread.cxx:335
 GThread.cxx:336
 GThread.cxx:337
 GThread.cxx:338
 GThread.cxx:339
 GThread.cxx:340
 GThread.cxx:341
 GThread.cxx:342
 GThread.cxx:343
 GThread.cxx:344
 GThread.cxx:345
 GThread.cxx:346
 GThread.cxx:347
 GThread.cxx:348
 GThread.cxx:349
 GThread.cxx:350
 GThread.cxx:351
 GThread.cxx:352
 GThread.cxx:353
 GThread.cxx:354
 GThread.cxx:355
 GThread.cxx:356
 GThread.cxx:357
 GThread.cxx:358
 GThread.cxx:359
 GThread.cxx:360
 GThread.cxx:361
 GThread.cxx:362
 GThread.cxx:363
 GThread.cxx:364
 GThread.cxx:365
 GThread.cxx:366
 GThread.cxx:367
 GThread.cxx:368
 GThread.cxx:369
 GThread.cxx:370
 GThread.cxx:371
 GThread.cxx:372
 GThread.cxx:373
 GThread.cxx:374
 GThread.cxx:375
 GThread.cxx:376
 GThread.cxx:377
 GThread.cxx:378
 GThread.cxx:379
 GThread.cxx:380
 GThread.cxx:381
 GThread.cxx:382
 GThread.cxx:383
 GThread.cxx:384
 GThread.cxx:385