[M3devel] Latest pthreads attempt

mika at async.caltech.edu mika at async.caltech.edu
Tue Aug 12 21:52:02 CEST 2014


Hi Tony (and others),

I instrumented my ThreadPThread.m3 as follows:

added an argument "line" to each of mutex_lock, mutex_unlock, and cond_wait

called these with Compiler.ThisLine()

results are attached... (I only learned yesterday how to do attachments in my Unix mailer!)

Is there a document somewhere describing the invariants of ThreadPThread.m3?  I can guess at many of
them but am not entirely sure of some of the implementation details.

In any case it looks to me:

l.   194 lock   0x10250a8 self 0x1009400  <----
l.   198 lock   0x1025140 self 0x1009400
l.   202 unlock 0x1025140 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400  <----
ERROR: pthread_mutex_lock:11

like we have a problem here somewhere:

 188 PROCEDURE XWait (self: Activation; m: Mutex; c: Condition; alertable: BOOLEAN)
 189   RAISES {Alerted} =
 190   (* LL = m *)
 191   VAR next, prev: Activation;
 192   BEGIN
 193     IF c.mutex = NIL THEN InitMutex(c.mutex, c, CleanCondition) END;
 194     WITH r = pthread_mutex_lock(self.mutex,ThisLine()) DO <*ASSERT r=0*> END;      <=======
 195     <*ASSERT self.waitingOn = NIL*>
 196     <*ASSERT self.nextWaiter = NIL*>
 197 
 198     WITH r = pthread_mutex_lock(c.mutex,ThisLine()) DO <*ASSERT r=0*> END;
 199     self.waitingOn := c.mutex;
 200     self.nextWaiter := c.waiters;
 201     c.waiters := self;
 202     WITH r = pthread_mutex_unlock(c.mutex,ThisLine()) DO <*ASSERT r=0*> END;
 203 
 204     m.release();
 205     IF perfOn THEN PerfChanged(State.waiting) END;
 206     LOOP
 207       IF alertable AND self.alerted THEN
 208         self.alerted := FALSE;
 209         <*ASSERT self.waitingOn = c.mutex*>
 210         WITH r = pthread_mutex_lock(c.mutex,ThisLine()) DO <*ASSERT r=0*> END;
 211         next := c.waiters; prev := NIL;
 212         WHILE next # self DO
 213           <*ASSERT next # NIL*>
 214           prev := next; next := next.nextWaiter;
 215         END;
 216         IF prev = NIL
 217           THEN c.waiters := self.nextWaiter;
 218           ELSE prev.nextWaiter := self.nextWaiter;
 219         END;
 220         WITH r = pthread_mutex_unlock(c.mutex,ThisLine()) DO <*ASSERT r=0*> END;
 221         self.nextWaiter := NIL;
 222         self.waitingOn := NIL;
 223         WITH r = pthread_mutex_unlock(self.mutex,ThisLine()) DO <*ASSERT r=0*> END;
 224         m.acquire();
 225         RAISE Alerted;
 226       END;
 227       WITH r = pthread_cond_wait(self.cond, self.mutex, ThisLine()) DO <*ASSERT r=0*> END;
 228       IF self.waitingOn = NIL THEN
 229         <*ASSERT self.nextWaiter = NIL*>
 230         WITH r = pthread_mutex_unlock(self.mutex,ThisLine()) DO <*ASSERT r=0*> END;
 231         m.acquire();
 232         RETURN;
 233       END;
 234     END;
 235   END XWait;

The self.mutex is locked at 194, m.release is called with the self.mutex locked in other words.

But that winds up here:

 144 PROCEDURE UnlockMutex (m: Mutex) =
 145   (* LL = m *)
 146   VAR
 147     self := GetActivation();
 148     t, prev: Activation;
 149   BEGIN
 150     IF m.mutex = NIL THEN InitMutex(m.mutex, m, CleanMutex) END;
 151     WITH r = pthread_mutex_lock(self.mutex,ThisLine()) DO <*ASSERT r=0*> END;   <========

And the traceback agrees....

ERROR: pthread_mutex_lock:11
[New Thread 801009400 (LWP 101206/cm3)]

Program received signal SIGABRT, Aborted.
[Switching to Thread 801009400 (LWP 101206/cm3)]
thr_kill () at thr_kill.S:3
3       RSYSCALL(thr_kill)
Current language:  auto; currently asm
(gdb) where
#0  thr_kill () at thr_kill.S:3
#1  0x0000000000790ea9 in abort () at /usr/src/lib/libc/stdlib/abort.c:65
#2  0x000000000071ad1d in ThreadPThread__pthread_mutex_lock (mutex=Error accessing memory address 0x8000ffffc4f8: Bad address.
) at ../src/thread/PTHREAD/ThreadPThreadC.c:526
#3  0x0000000000714dad in ThreadPThread__UnlockMutex (M3_AYIbX3_m=Error accessing memory address 0x8000ffffc528: Bad address.
) at ../src/thread/PTHREAD/ThreadPThread.m3:151
#4  0x0000000000715298 in ThreadPThread__XWait (M3_DMxDjQ_self=Error accessing memory address 0x8000ffffc5a8: Bad address.
) at ../src/thread/PTHREAD/ThreadPThread.m3:204
#5  0x0000000000717633 in ThreadPThread__XJoin (M3_DMxDjQ_self=Error accessing memory address 0x8000ffffc608: Bad address.
) at ../src/thread/PTHREAD/ThreadPThread.m3:557
#6  0x0000000000717782 in Thread__Join (M3_BXP32l_t=Error accessing memory address 0x8000ffffc6d8: Bad address.
) at ../src/thread/PTHREAD/ThreadPThread.m3:569
#7  0x0000000000407d8d in Builder__ForceAllPromisesInParallel (M3_C58HwX_promises=Error accessing memory address 0x8000ffffc758: Bad address.
) at ../src/Builder.m3:1002

    Mika
-------------- next part --------------
/* Copyright (C) 2005, Purdue Research Foundation                  */
/* All rights reserved.                                            */
/* See the file COPYRIGHT-PURDUE for a full description.           */

#include "m3core.h"

#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__)
/* See ThreadApple.c, ThreadFreeBSD.c, ThreadOpenBSD.c. */
#define M3_DIRECT_SUSPEND
#endif

#define M3MODULE ThreadPThread

#if defined(__sparc) || defined(__ia64__)
#define M3_REGISTER_WINDOWS
#endif

#ifdef M3_DIRECT_SUSPEND
#define M3_DIRECT_SUSPEND_ASSERT_FALSE do {                     \
    assert(0 && "MacOS X, FreeBSD, OpenBSD should not get here."); \
    fprintf(stderr, "MacOS X, FreeBSD, OpenBSD should not get here.\n"); \
    abort();                                                    \
  } while(0);
#endif

M3_EXTERNC_BEGIN

#define InitC                   ThreadPThread__InitC
#define SignalHandler           ThreadPThread__SignalHandler
#define sizeof_pthread_mutex_t  ThreadPThread__sizeof_pthread_mutex_t
#define sizeof_pthread_cond_t   ThreadPThread__sizeof_pthread_cond_t
#define SIG_SUSPEND             ThreadPThread__SIG_SUSPEND

void __cdecl SignalHandler(int signo, siginfo_t *info, void *context);

#if M3_HAS_VISIBILITY
#pragma GCC visibility push(hidden)
#endif

/* expected values for compat, if compat matters:
    Solaris: 17 (at least 32bit SPARC?)
    Cygwin: 19 -- er, but maybe that's wrong
    Linux: 64
    FreeBSD: 31 (not used)
    OpenBSD: 31 (not used)
    HPUX: 44
  Look at the history of Usignal and RTMachine to find more values.  There was
  RTMachine.SIG_SUSPEND and SIG was aliased to it.  Both SIG and SIG_SUSPEND
  were only defined for systems using pthreads. SIG was shorthand. */
#ifdef M3_DIRECT_SUSPEND
EXTERN_CONST int SIG_SUSPEND = 0;
#elif defined(__sun) || defined(__CYGWIN__)
EXTERN_CONST int SIG_SUSPEND = SIGUSR2;
#elif defined(__linux)
EXTERN_CONST int SIG_SUSPEND = NSIG - 1;
#elif defined(__hpux)
EXTERN_CONST int SIG_SUSPEND = _SIGRTMAX;
#elif defined(SIGRTMAX) && !defined(__osf__)
/* This might be a function call, in which case try _SIGRTMAX or initializing
   it somewhere. SIGRTMAX is sysconf(132) on OSF. We may be
   able to use direct suspend/resume on OSF. */
EXTERN_CONST int SIG_SUSPEND = SIGRTMAX;
#elif defined(SIGUSR2)
EXTERN_CONST int SIG_SUSPEND = SIGUSR2;
#else
#error Unable to determine SIG_SUSPEND.
#endif

static int stack_grows_down;

#ifndef M3_DIRECT_SUSPEND

static sigset_t mask;

/* Signal based suspend/resume */
static sem_t ackSem;

static void __cdecl SignalHandlerC(int signo, siginfo_t *info, void *context)
/* wrapper to workaround on ALPHA_LINUX:
   /usr/bin/ld: ThreadPThreadC.o: gp-relative relocation against dynamic symbol ThreadPThread__SignalHandler
   http://gcc.gnu.org/bugzilla/show_bug.cgi?id=46861 */
{
  SignalHandler(signo, info, context);
}

int __cdecl ThreadPThread__sem_wait(void)           { return sem_wait(&ackSem); }
int __cdecl ThreadPThread__sem_post(void)           { return sem_post(&ackSem); }
int __cdecl ThreadPThread__sem_getvalue(int *value) { return sem_getvalue(&ackSem, value); }

void
__cdecl
ThreadPThread__sigsuspend(void)
{
  struct {
    sigjmp_buf jb;
  } s;

  ZERO_MEMORY(s);

  if (sigsetjmp(s.jb, 0) == 0) /* save registers to stack */
#ifdef M3_REGISTER_WINDOWS
    siglongjmp(s.jb, 1); /* flush register windows */
  else
#endif
    sigsuspend(&mask);
}

void
__cdecl
ThreadPThread__SuspendThread (m3_pthread_t mt)
{
  abort();
}

void
__cdecl
ThreadPThread__RestartThread (m3_pthread_t mt)
{
  abort();
}

void
__cdecl
ThreadPThread__ProcessStopped (m3_pthread_t mt, char *bottom, char *context,
                               void (*p)(void *start, void *limit))
{
  /* process stack */
  if (!bottom) return;
  if (stack_grows_down)
  {
    assert(context < bottom);
    p(context, bottom);
  }
  else
  {
    assert(bottom < context);
    p(bottom, context);
  }
  /* process register context */
  p(context, context + sizeof(ucontext_t));
}

#else /* M3_DIRECT_SUSPEND */

void __cdecl ThreadPThread__sem_wait(void)      { M3_DIRECT_SUSPEND_ASSERT_FALSE }
void __cdecl ThreadPThread__sem_post(void)      { M3_DIRECT_SUSPEND_ASSERT_FALSE }
void __cdecl ThreadPThread__sem_getvalue(void)  { M3_DIRECT_SUSPEND_ASSERT_FALSE }
void __cdecl ThreadPThread__sigsuspend(void)    { M3_DIRECT_SUSPEND_ASSERT_FALSE }

#endif /* M3_DIRECT_SUSPEND */

void
__cdecl
ThreadPThread__ProcessLive(char *bottom, void (*p)(void *start, void *limit))
{
/*
cc: Warning: ThreadPThreadC.c, line 170: In this statement, & before array "jb" is ignored. (addrarray)
    p(&jb, ((char *)&jb) + sizeof(jb));
------^
cc: Warning: ThreadPThreadC.c, line 170: In this statement, & before array "jb" is ignored. (addrarray)
    p(&jb, ((char *)&jb) + sizeof(jb));
--------------------^

jb may or may not be an array, & is necessary, wrap it in struct.
*/
  struct {
    sigjmp_buf jb;
  } s;

  ZERO_MEMORY(s);

  if (sigsetjmp(s.jb, 0) == 0) /* save registers to stack */
#ifdef M3_REGISTER_WINDOWS
    siglongjmp(s.jb, 1); /* flush register windows */
  else
#endif
  {
    /* capture top after longjmp because longjmp can clobber non-volatile locals */
    char *top = (char*)⊤
    assert(bottom);
    if (stack_grows_down)
    {
      assert(top < bottom);
      p(top, bottom);
    }
    else
    {
      assert(bottom < top);
      p(bottom, top);
    }
    p(&s, sizeof(s) + (char *)&s);
  }
}

#define M3_MAX(x, y) (((x) > (y)) ? (x) : (y))
typedef void *(*start_routine_t)(void *);

#define M3_RETRY(expr)                                  \
  r = (expr);                                           \
  if (r == EAGAIN || r == ENOMEM || r == ENOSPC)        \
  {                                                     \
    /* try again right away */                          \
    r = (expr);                                         \
    if (r == EAGAIN || r == ENOMEM || r == ENOSPC)      \
    {                                                   \
      /* try again after short delay */                 \
      sleep(1);                                         \
      r = (expr);                                       \
    }                                                   \
  }

int
__cdecl
ThreadPThread__thread_create(WORD_T stackSize,
                             start_routine_t start_routine,
                             void *arg)
{
  int r = { 0 };
  WORD_T bytes = { 0 };
  pthread_attr_t attr;
  pthread_t pthread;

  ZERO_MEMORY(pthread);
  ZERO_MEMORY(attr);
  
  M3_RETRY(pthread_attr_init(&attr));
#ifdef __hpux
  if (r == ENOSYS)
    {
      fprintf(stderr,
              "You got the nonfunctional pthread stubs on HP-UX. You need to"
              " adjust your build commands, such as to link to -lpthread or"
              " use -pthread, and not link explicitly to -lc.\n");
    }
#endif
  assert(r == 0);

  r = pthread_attr_getstacksize(&attr, &bytes); assert(r == 0);

  bytes = M3_MAX(bytes, stackSize);
  pthread_attr_setstacksize(&attr, bytes);

  M3_RETRY(pthread_create(&pthread, &attr, start_routine, arg));
#ifdef __sun
  if (r == ENOENT)
  {
    fprintf(stderr,
            "You got the nonfunctional pthread stubs on Solaris earlier than 5.10. "
            "You need to adjust your build commands, such as to link to -lpthread "
            " ahead of -lc.\n");
  }
#endif  
  if (r != 0)
  {
    fprintf(stderr,
            "pthread_create(stack_size:0x%X):0x%X errno:0x%X\n",
            (unsigned)stackSize,
            (unsigned)r,
            (unsigned)errno);
  }

  pthread_attr_destroy(&attr);

  return r;
}


#define MUTEX(name) \
static pthread_mutex_t name##Mu = PTHREAD_MUTEX_INITIALIZER; \
extern pthread_mutex_t * const ThreadPThread__##name##Mu; \
pthread_mutex_t * const ThreadPThread__##name##Mu = &name##Mu; \

#define CONDITION_VARIABLE(name) \
static pthread_cond_t name##Cond = PTHREAD_COND_INITIALIZER; \
extern pthread_cond_t * const ThreadPThread__##name##Cond; \
pthread_cond_t * const ThreadPThread__##name##Cond = &name##Cond; \

/* activeMu slotMu initMu perfMu heapMu heapCond */

MUTEX(active)                   /* global lock for list of active threads */
MUTEX(slots)                    /* global lock for thread slots table */
MUTEX(init)                     /* global lock for initializers */
MUTEX(perf)                     /* global lock for thread state tracing */
MUTEX(heap)                     /* global lock for heap atomicity */
CONDITION_VARIABLE(heap)        /* CV for heap waiters */

/*
NetBSD 5.0.2 compiles __thread, but segfault at runtime.
OpenBSD 4.7 compiles __thread, but segfault at runtime.
Apple doesn't compile
FreeBSD not tested
AIX probably works, not tested
Solaris: failed to link on Solaris 2.9: http://hudson.modula3.com:8080/job/cm3-current-build-SOLsun-opencsw-current9s/166/console
HP-UX? AIX?
Linux/arm: /usr/bin/ld: /usr/local/cm3/pkg/m3core/ARMEL_LINUX/libm3core.a(ThreadPThreadC.o)(.stab+0x2e28): R_ARM_ABS32 used with TLS symbol activations
*/
#if 0 /* defined(__linux) && !defined(__arm__) */

#define M3_COMPILER_THREAD_LOCAL

static __thread void* activations;

void
__cdecl
ThreadPThread__SetActivation(void *value)
{
  activations = value;
}

void*
__cdecl
ThreadPThread__GetActivation(void)
{
  return activations;
}

#else

static pthread_key_t activations;

void
__cdecl
ThreadPThread__SetActivation(void *value)
{
  int r = { 0 };
  M3_RETRY(pthread_setspecific(activations, value));
  assert(r == 0);
}

void *
__cdecl
ThreadPThread__GetActivation(void)
{
  return pthread_getspecific(activations);
}

#endif

typedef int (*generic_init_t)(void *, const void *);

void *
__cdecl
ThreadPThread_pthread_generic_new(WORD_T size, generic_init_t init)
{
  int r = ENOMEM;
  void *p = calloc(1, size);
  if (p == NULL)
    goto Error;
  M3_RETRY(init(p, NULL));
  if (r == ENOMEM)
    goto Error;
  assert(r == 0);
  if (r != 0)
    goto Error;
  return p;
Error:
  if (r)
  {
    fprintf(stderr, "ERROR: pthread_generic_new:%d\n", r);
    abort();
  }
  if (p) free(p);
  return NULL;
}

#define THREADPTHREAD__PTHREAD_GENERIC_NEW(type) {                      \
    typedef pthread_##type##_t T;                                       \
    typedef pthread_##type##attr_t attr_t;                              \
    typedef int (*init_t)(T *, const attr_t *);                         \
    /* make sure the type matches */                                    \
    init_t init = pthread_##type##_init;                                \
    return ThreadPThread_pthread_generic_new(sizeof(T),                 \
                                             (generic_init_t)init);     \
  }

void *
__cdecl
ThreadPThread__pthread_mutex_new(void)
{
  THREADPTHREAD__PTHREAD_GENERIC_NEW(mutex);
}

void *
__cdecl
ThreadPThread__pthread_cond_new(void)
{
  THREADPTHREAD__PTHREAD_GENERIC_NEW(cond);
}

void
__cdecl
ThreadPThread__pthread_mutex_delete(pthread_mutex_t* p)
{
  int e = { 0 };
  if (p == NULL) return;
#if defined(__hpux) || defined(__osf)
  /* workaround Tru64 5.1 and HP-UX bug: pthread_mutex_destroy()
     intermittently returns EBUSY even when there are no threads accessing the
     mutex. */
  do { e = pthread_mutex_destroy(p); } while (e == EBUSY);
#else
  e = pthread_mutex_destroy(p);
#endif
  if (e)
  {
    if (e == EBUSY)
      fprintf(stderr, "pthread_mutex_destroy:EBUSY\n");
    else
      fprintf(stderr, "pthread_mutex_destroy:%d\n", e);
    abort();
  }
  free(p);
}

void
__cdecl
ThreadPThread__pthread_cond_delete(pthread_cond_t *p)
{
  int r = { 0 };
  if (p == NULL) return;
  r = pthread_cond_destroy(p);
  assert(r == 0);
  free(p);
}

#define BILLION (1000 * 1000 * 1000)

void
__cdecl
ThreadPThread__Nanosleep(INTEGER nanoseconds)
{
#ifdef __INTERIX
  assert(nanoseconds >= 0);
  assert(nanoseconds < BILLION);
  /* This is only an approximation. We don't try to complete the sleep
   * if interrupted, because we don't cheaply know how much time has elapsed.
   */
  usleep(nanoseconds / 1000);
#else
  struct timespec wait;
  struct timespec remaining;

  assert(nanoseconds >= 0);
  assert(nanoseconds < BILLION);
  ZERO_MEMORY(wait);
  ZERO_MEMORY(remaining);
  wait.tv_sec = 0;
  wait.tv_nsec = nanoseconds;
  while (nanosleep(&wait, &remaining) == -1 && errno == EINTR)
      wait = remaining;
#endif
}

/*M3WRAP2(int, pthread_cond_wait, pthread_cond_t*, pthread_mutex_t*)*/
M3WRAP1(int, pthread_cond_signal, pthread_cond_t*)
M3WRAP1(int, pthread_cond_broadcast, pthread_cond_t*)

int
__cdecl
ThreadPThread__pthread_cond_timedwait(pthread_cond_t* cond,
                                      pthread_mutex_t* mutex,
                                      LONGREAL m3timeout)
{
  struct timespec timeout;
  double n = { 0 };
  
  ZERO_MEMORY(timeout);
  timeout.tv_nsec = modf(m3timeout, &n) * BILLION;
  timeout.tv_sec = n;
  return pthread_cond_timedwait(cond, mutex, &timeout);
}

int
__cdecl
ThreadPThread__pthread_detach_self(void)
{
  return pthread_detach(pthread_self());
}

m3_pthread_t
__cdecl
ThreadPThread__pthread_self(void)
{
  pthread_t a = pthread_self();
  return PTHREAD_TO_M3(a);
}

int
__cdecl
ThreadPThread__pthread_equal(m3_pthread_t t1, m3_pthread_t t2)
{
  return pthread_equal(PTHREAD_FROM_M3(t1), PTHREAD_FROM_M3(t2));
}

int
__cdecl
ThreadPThread__pthread_kill(m3_pthread_t thread, int sig)
{
  return pthread_kill(PTHREAD_FROM_M3(thread), sig);
}

int
__cdecl
ThreadPThread__pthread_cond_wait(pthread_cond_t *c, pthread_mutex_t *m, int line)
{
  fprintf(stderr, "l. %5d wait   0x%x self 0x%x\n",line,m,pthread_self());

  return pthread_cond_wait(c,m);
}

int
__cdecl
ThreadPThread__pthread_mutex_lock(pthread_mutex_t* mutex, int line)
{
  int a;

  fprintf(stderr, "l. %5d lock   0x%x self 0x%x\n",line,mutex,pthread_self());

  a = pthread_mutex_lock(mutex);
  if (a)
  {
    if (a == EINVAL)
      fprintf(stderr, "ERROR: pthread_mutex_lock:EINVAL\n");
    else
      fprintf(stderr, "ERROR: pthread_mutex_lock:%d\n", a);
    abort();
  }
  return a;
}

int
__cdecl
ThreadPThread__pthread_mutex_unlock(pthread_mutex_t* mutex, int line)
{
  int a;

  fprintf(stderr, "l. %5d unlock 0x%x self 0x%x\n",line,mutex,pthread_self());

  a = pthread_mutex_unlock(mutex);
  if (a)
  {
    fprintf(stderr, "ERROR: pthread_mutex_unlock:%d\n", a);
    abort();
  }
  return a;
}

void
__cdecl
InitC(int *bottom)
{
  int r = { 0 };

#ifndef M3_DIRECT_SUSPEND
  struct sigaction act;
  ZERO_MEMORY(act);
#endif

  stack_grows_down = (bottom > &r);
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__INTERIX)
  assert(stack_grows_down); /* See ThreadApple.c, ThreadFreeBSD.c */
#endif
#ifndef M3_COMPILER_THREAD_LOCAL
  M3_RETRY(pthread_key_create(&activations, NULL)); assert(r == 0);
#endif

#ifndef M3_DIRECT_SUSPEND
  ZERO_MEMORY(act);

  M3_RETRY(sem_init(&ackSem, 0, 0)); assert(r == 0);

  r = sigfillset(&mask); assert(r == 0);
  r = sigdelset(&mask, SIG_SUSPEND); assert(r == 0);
  r = sigdelset(&mask, SIGINT); assert(r == 0);
  r = sigdelset(&mask, SIGQUIT); assert(r == 0);
  r = sigdelset(&mask, SIGABRT); assert(r == 0);
  r = sigdelset(&mask, SIGTERM); assert(r == 0);

  act.sa_flags = SA_RESTART | SA_SIGINFO;
  act.sa_sigaction = SignalHandlerC;
  r = sigfillset(&act.sa_mask); assert(r == 0);
  r = sigaction(SIG_SUSPEND, &act, NULL); assert(r == 0);
#endif
}

M3_EXTERNC_END
-------------- next part --------------
(* Copyright (C) 2005, Purdue Research Foundation                  *)
(* All rights reserved.                                            *)
(* See the file COPYRIGHT-PURDUE for a full description.           *)

UNSAFE MODULE ThreadPThread EXPORTS Thread, ThreadF, RTThread, Scheduler,
SchedulerPosix, RTOS, RTHooks, ThreadPThread;

IMPORT Cerrno, FloatMode, MutexRep, RTCollectorSRC, RTError, RTHeapRep, RTIO,
       RTParams, RTPerfTool, RTProcess, ThreadEvent, Time,
       Word, Usched, Uerror, Uexec;
FROM Compiler IMPORT ThisFile, ThisLine;
FROM Ctypes IMPORT int;
IMPORT RuntimeError AS RTE;
FROM ThreadInternal IMPORT Poll;

(*----------------------------------------------------- types and globals ---*)

CONST
  MILLION = 1000 * 1000;
  WAIT_UNIT = MILLION; (* one million nanoseconds, one thousandth of a second *)
  RETRY_INTERVAL = 10 * MILLION; (* 10 million nanoseconds, one hundredth of a second *)

REVEAL
  Mutex = MutexRep.Public BRANDED "Mutex Pthread-1.0" OBJECT
    mutex: pthread_mutex_t := NIL;
    holder: Activation := NIL;
    waiters: Activation := NIL;
  OVERRIDES
    acquire := LockMutex;
    release := UnlockMutex;
  END;

  Condition = BRANDED "Thread.Condition Pthread-1.0" OBJECT
    mutex: pthread_mutex_t := NIL;
    waiters: Activation := NIL;     (* LL = mutex *)
  END;

  T = BRANDED "Thread.T Pthread-1.6" OBJECT
    act: Activation := NIL;         (* live untraced thread data *)
    closure: Closure := NIL;        (* our work and its result *)
    result: REFANY := NIL;          (* our work and its result *)
    join: Condition;                (* wait here to join; NIL when done *)
    joined: BOOLEAN := FALSE;       (* Is anyone waiting yet? *)
  END;

TYPE
  ActState = { Starting, Started, Stopping, Stopped };
  REVEAL Activation = UNTRACED BRANDED REF RECORD
    frame: ADDRESS := NIL;              (* exception handling support *)
    mutex: pthread_mutex_t := NIL;      (* write-once in CreateT *)
    cond: pthread_cond_t := NIL;        (* write-once in CreateT; a place to park while waiting *)
    alerted : BOOLEAN := FALSE;         (* LL = mutex; the alert flag *)
    waitingOn: pthread_mutex_t := NIL;  (* LL = mutex; The CV's mutex *)
    nextWaiter: Activation := NIL;      (* LL = mutex; waiting thread queue *)
    next, prev: Activation := NIL;      (* LL = activeMu; global doubly-linked, circular list of all active threads *)
    handle: pthread_t := NIL;           (* LL = activeMu; thread handle *)
    stackbase: ADDRESS := NIL;          (* LL = activeMu; stack base for GC *)
    context: ADDRESS := NIL;            (* LL = activeMu *)
    state := ActState.Started;          (* LL = activeMu *)
    slot: CARDINAL := 0;                (* LL = slotMu; index in slots *)
    floatState : FloatMode.ThreadState; (* per-thread floating point state *)
    heapState : RTHeapRep.ThreadState;  (* per-thread heap state *)
  END;

PROCEDURE SetState (act: Activation;  state: ActState) =
  CONST text = ARRAY ActState OF TEXT
    { "Starting", "Started", "Stopping", "Stopped" };
  BEGIN
    act.state := state;
    IF DEBUG THEN
      RTIO.PutText(text[state]);
      RTIO.PutText(" act=");
      RTIO.PutAddr(act);
      RTIO.PutText("\n");
      RTIO.Flush();
    END;
  END SetState;

(*----------------------------------------------------------------- Mutex ---*)

PROCEDURE Acquire (m: Mutex) =
  BEGIN
    m.acquire ();
  END Acquire;

PROCEDURE Release (m: Mutex) =
  BEGIN
    m.release ();
  END Release;

PROCEDURE CleanMutex (r: REFANY) =
  VAR m := NARROW(r, Mutex);
  BEGIN
    pthread_mutex_delete(m.mutex);
    m.mutex := NIL;
  END CleanMutex;

PROCEDURE InitMutex (VAR m: pthread_mutex_t; root: REFANY;
                     Clean: PROCEDURE(root: REFANY)) =
  VAR mutex := pthread_mutex_new();
  BEGIN
    TRY
      WITH r = pthread_mutex_lock(initMu,ThisLine()) DO <*ASSERT r=0*> END;
      (* Did someone else win the race? *)
      IF m # NIL THEN RETURN END;
      (* We won the race, but we might have failed to allocate. *)
      IF mutex = NIL THEN RTE.Raise (RTE.T.OutOfMemory) END;
      RTHeapRep.RegisterFinalCleanup (root, Clean);
      m := mutex;
      mutex := NIL;
    FINALLY
      WITH r = pthread_mutex_unlock(initMu,ThisLine()) DO <*ASSERT r=0*> END;
      pthread_mutex_delete(mutex);
    END;
  END InitMutex;

PROCEDURE LockMutex (m: Mutex) =
  VAR self := GetActivation();
  BEGIN
    IF m.mutex = NIL THEN InitMutex(m.mutex, m, CleanMutex) END;
    WITH r = pthread_mutex_lock(self.mutex,ThisLine()) DO <*ASSERT r=0*> END;
    WITH r = pthread_mutex_lock(m.mutex,ThisLine()) DO <*ASSERT r=0*> END;
    IF m.holder = NIL THEN
      m.holder := self;
      WITH r = pthread_mutex_unlock(m.mutex,ThisLine()) DO <*ASSERT r=0*> END;
      WITH r = pthread_mutex_unlock(self.mutex,ThisLine()) DO <*ASSERT r=0*> END;
      IF perfOn THEN PerfRunning() END;
      RETURN;
    END;
    <*ASSERT self.waitingOn = NIL*>
    <*ASSERT self.nextWaiter = NIL*>
    IF perfOn THEN PerfChanged(State.locking) END;
    self.waitingOn := m.mutex;
    self.nextWaiter := m.waiters;
    m.waiters := self;
    IF m.holder = self THEN Die(ThisLine(), "impossible acquire") END;
    WITH r = pthread_mutex_unlock(m.mutex,ThisLine()) DO <*ASSERT r=0*> END;
    REPEAT
      WITH r = pthread_cond_wait(self.cond, self.mutex, ThisLine()) DO <*ASSERT r=0*> END;
    UNTIL self.waitingOn = NIL; (* m.holder = self *)
    WITH r = pthread_mutex_unlock(self.mutex,ThisLine()) DO <*ASSERT r=0*> END;
  END LockMutex;

PROCEDURE UnlockMutex (m: Mutex) =
  (* LL = m *)
  VAR
    self := GetActivation();
    t, prev: Activation;
  BEGIN
    IF m.mutex = NIL THEN InitMutex(m.mutex, m, CleanMutex) END;
    WITH r = pthread_mutex_lock(self.mutex,ThisLine()) DO <*ASSERT r=0*> END;
    WITH r = pthread_mutex_lock(m.mutex,ThisLine()) DO <*ASSERT r=0*> END;
    IF m.holder # self THEN Die(ThisLine(), "illegal release") END;
    t := m.waiters;
    IF t = NIL THEN
      m.holder := NIL;
      WITH r = pthread_mutex_unlock(m.mutex,ThisLine()) DO <*ASSERT r=0*> END;
      WITH r = pthread_mutex_unlock(self.mutex,ThisLine()) DO <*ASSERT r=0*> END;
      RETURN;
    END;
    prev := NIL;
    WHILE t.nextWaiter # NIL DO
      prev := t;
      t := t.nextWaiter;
    END;
    IF prev # NIL
      THEN prev.nextWaiter := NIL;
      ELSE m.waiters := NIL;
    END;
    m.holder := t;
    WITH r = pthread_mutex_unlock(m.mutex,ThisLine()) DO <*ASSERT r=0*> END;
    WITH r = pthread_mutex_lock(t.mutex,ThisLine()) DO <*ASSERT r=0*> END;
    t.waitingOn := NIL;
    WITH r = pthread_cond_signal(t.cond) DO <*ASSERT r=0*> END;
    WITH r = pthread_mutex_unlock(t.mutex,ThisLine()) DO <*ASSERT r=0*> END;
    WITH r = pthread_mutex_unlock(self.mutex,ThisLine()) DO <*ASSERT r=0*> END;
  END UnlockMutex;

(*---------------------------------------- Condition variables and Alerts ---*)

PROCEDURE CleanCondition (r: REFANY) =
  VAR c := NARROW(r, Condition);
  BEGIN
    pthread_mutex_delete(c.mutex);
    c.mutex := NIL;
  END CleanCondition;

PROCEDURE XWait (self: Activation; m: Mutex; c: Condition; alertable: BOOLEAN)
  RAISES {Alerted} =
  (* LL = m *)
  VAR next, prev: Activation;
  BEGIN
    IF c.mutex = NIL THEN InitMutex(c.mutex, c, CleanCondition) END;
    WITH r = pthread_mutex_lock(self.mutex,ThisLine()) DO <*ASSERT r=0*> END;
    <*ASSERT self.waitingOn = NIL*>
    <*ASSERT self.nextWaiter = NIL*>

    WITH r = pthread_mutex_lock(c.mutex,ThisLine()) DO <*ASSERT r=0*> END;
    self.waitingOn := c.mutex;
    self.nextWaiter := c.waiters;
    c.waiters := self;
    WITH r = pthread_mutex_unlock(c.mutex,ThisLine()) DO <*ASSERT r=0*> END;

    m.release();
    IF perfOn THEN PerfChanged(State.waiting) END;
    LOOP
      IF alertable AND self.alerted THEN
        self.alerted := FALSE;
        <*ASSERT self.waitingOn = c.mutex*>
        WITH r = pthread_mutex_lock(c.mutex,ThisLine()) DO <*ASSERT r=0*> END;
        next := c.waiters; prev := NIL;
        WHILE next # self DO
          <*ASSERT next # NIL*>
          prev := next; next := next.nextWaiter;
        END;
        IF prev = NIL
          THEN c.waiters := self.nextWaiter;
          ELSE prev.nextWaiter := self.nextWaiter;
        END;
        WITH r = pthread_mutex_unlock(c.mutex,ThisLine()) DO <*ASSERT r=0*> END;
        self.nextWaiter := NIL;
        self.waitingOn := NIL;
        WITH r = pthread_mutex_unlock(self.mutex,ThisLine()) DO <*ASSERT r=0*> END;
        m.acquire();
        RAISE Alerted;
      END;
      WITH r = pthread_cond_wait(self.cond, self.mutex, ThisLine()) DO <*ASSERT r=0*> END;
      IF self.waitingOn = NIL THEN
        <*ASSERT self.nextWaiter = NIL*>
        WITH r = pthread_mutex_unlock(self.mutex,ThisLine()) DO <*ASSERT r=0*> END;
        m.acquire();
        RETURN;
      END;
    END;
  END XWait;

PROCEDURE AlertWait (m: Mutex; c: Condition) RAISES {Alerted} =
  (* LL = m *)
  VAR self := GetActivation();
  BEGIN
    XWait(self, m, c, alertable := TRUE);
  END AlertWait;

PROCEDURE Wait (m: Mutex; c: Condition) =
  <*FATAL Alerted*>
  (* LL = m *)
  VAR self := GetActivation();
  BEGIN
    XWait(self, m, c, alertable := FALSE);
  END Wait;

PROCEDURE DequeueHead(c: Condition) =
  (* LL = c *)
  VAR t := c.waiters;
  BEGIN
    WITH r = pthread_mutex_lock(t.mutex,ThisLine()) DO <*ASSERT r=0*> END;
    c.waiters := t.nextWaiter;
    t.nextWaiter := NIL;
    t.waitingOn := NIL;
    WITH r = pthread_cond_signal(t.cond) DO <*ASSERT r=0*> END;
    WITH r = pthread_mutex_unlock(t.mutex,ThisLine()) DO <*ASSERT r=0*> END;
  END DequeueHead;

PROCEDURE Signal (c: Condition) =
  BEGIN
    IF c.mutex = NIL THEN InitMutex(c.mutex, c, CleanCondition) END;
    WITH r = pthread_mutex_lock(c.mutex,ThisLine()) DO <*ASSERT r=0*> END;
    IF c.waiters # NIL THEN DequeueHead(c) END;
    WITH r = pthread_mutex_unlock(c.mutex,ThisLine()) DO <*ASSERT r=0*> END;
  END Signal;

PROCEDURE Broadcast (c: Condition) =
  BEGIN
    IF c.mutex = NIL THEN InitMutex(c.mutex, c, CleanCondition) END;
    WITH r = pthread_mutex_lock(c.mutex,ThisLine()) DO <*ASSERT r=0*> END;
    WHILE c.waiters # NIL DO DequeueHead(c) END;
    WITH r = pthread_mutex_unlock(c.mutex,ThisLine()) DO <*ASSERT r=0*> END;
  END Broadcast;

PROCEDURE Alert (thread: T) =
  VAR t := thread.act;
  BEGIN
    WITH r = pthread_mutex_lock(t.mutex,ThisLine()) DO <*ASSERT r=0*> END;
    t.alerted := TRUE;
    WITH r = pthread_cond_signal(t.cond) DO <*ASSERT r=0*> END;
    WITH r = pthread_mutex_unlock(t.mutex,ThisLine()) DO <*ASSERT r=0*> END;
  END Alert;

PROCEDURE XTestAlert (self: Activation): BOOLEAN =
  VAR result: BOOLEAN;
  BEGIN
    WITH r = pthread_mutex_lock(self.mutex,ThisLine()) DO <*ASSERT r=0*> END;
    result := self.alerted;
    self.alerted := FALSE;
    WITH r = pthread_mutex_unlock(self.mutex,ThisLine()) DO <*ASSERT r=0*> END;
    RETURN result;
  END XTestAlert;

PROCEDURE TestAlert (): BOOLEAN =
  VAR self := GetActivation();
  BEGIN
    RETURN XTestAlert(self);
  END TestAlert;

(*------------------------------------------------------------------ Self ---*)

VAR (* LL = slotMu *)
  n_slotted: CARDINAL;
  next_slot: CARDINAL;      (* NOTE: we don't use slots[0] *)
  slots: REF ARRAY OF T;    (* NOTE: we don't use slots[0] *)

PROCEDURE InitActivations (me: Activation) =
  BEGIN
    me.handle := pthread_self();
    me.next := me;
    me.prev := me;
    SetActivation(me);
    (* Explicitly (re)initialize to handle fork(). *)
    next_slot := 1;     (* no threads created yet *)
    slots := NIL;       (* no threads created yet *)
    n_slotted := 0;     (* no threads created yet *)
    allThreads := me;
    FloatMode.InitThread(me.floatState);
  END InitActivations;

PROCEDURE Self (): T =
  (* If not the initial thread and not created by Fork, returns NIL *)
  VAR
    me := GetActivation();
    t: T;
  BEGIN
    IF me = NIL THEN Die(ThisLine(), "Thread primitive called from non-Modula-3 thread") END;
    WITH r = pthread_mutex_lock(slotsMu,ThisLine()) DO <*ASSERT r=0*> END;
      t := slots[me.slot];
    WITH r = pthread_mutex_unlock(slotsMu,ThisLine()) DO <*ASSERT r=0*> END;
    IF (t.act # me) THEN Die(ThisLine(), "thread with bad slot!") END;
    RETURN t;
  END Self;

PROCEDURE AssignSlot (t: T): INTEGER =
  (* LL = 0, cause we allocate stuff with NEW! *)
  VAR n: CARDINAL;  new_slots: REF ARRAY OF T;  slot: CARDINAL;
  BEGIN
    WITH r = pthread_mutex_lock(slotsMu,ThisLine()) DO <*ASSERT r=0*> END;

      (* make sure we have room to register this guy *)
      IF (slots = NIL) THEN
        WITH r = pthread_mutex_unlock(slotsMu,ThisLine()) DO <*ASSERT r=0*> END;
          slots := NEW (REF ARRAY OF T, 20);
        WITH r = pthread_mutex_lock(slotsMu,ThisLine()) DO <*ASSERT r=0*> END;
      END;
      IF (n_slotted >= LAST (slots^)) THEN
        n := NUMBER (slots^);
        WITH r = pthread_mutex_unlock(slotsMu,ThisLine()) DO <*ASSERT r=0*> END;
          new_slots := NEW (REF ARRAY OF T, n+n);
        WITH r = pthread_mutex_lock(slotsMu,ThisLine()) DO <*ASSERT r=0*> END;
        IF (n = NUMBER (slots^)) THEN
          (* we won any races that may have occurred. *)
          SUBARRAY (new_slots^, 0, n) := slots^;
          slots := new_slots;
        ELSIF (n_slotted < LAST (slots^)) THEN
          (* we lost a race while allocating a new slot table,
             and the new table has room for us. *)
        ELSE
          (* ouch, the new table is full too!   Bail out and retry *)
          WITH r = pthread_mutex_unlock(slotsMu,ThisLine()) DO <*ASSERT r=0*> END;
          RETURN AssignSlot (t);
        END;
      END;

      (* look for an empty slot *)
      WHILE (slots [next_slot] # NIL) DO
        INC (next_slot);
        IF (next_slot >= NUMBER (slots^)) THEN next_slot := 1; END;
      END;

      INC (n_slotted);
      slot := next_slot;
      slots [slot] := t;

    WITH r = pthread_mutex_unlock(slotsMu,ThisLine()) DO <*ASSERT r=0*> END;
    RETURN slot;
  END AssignSlot;

PROCEDURE FreeSlot (self: T) =
  (* LL = 0 *)
  BEGIN
    WITH r = pthread_mutex_lock(slotsMu,ThisLine()) DO <*ASSERT r=0*> END;

      DEC (n_slotted);
      WITH z = slots [self.act.slot] DO
        IF z # self THEN Die (ThisLine(), "unslotted thread!"); END;
        z := NIL;
      END;
      self.act.slot := 0;
    WITH r = pthread_mutex_unlock(slotsMu,ThisLine()) DO <*ASSERT r=0*> END;
  END FreeSlot;

PROCEDURE DumpThread (t: Activation) =
  BEGIN
    RTIO.PutText("Activation:   "); RTIO.PutAddr(t);             RTIO.PutChar('\n');
    RTIO.PutText("  slot:       "); RTIO.PutInt(t.slot);         RTIO.PutChar('\n');
    RTIO.PutText("  mutex:      "); RTIO.PutAddr(t.mutex);       RTIO.PutChar('\n');
    RTIO.PutText("  cond:       "); RTIO.PutAddr(t.cond);        RTIO.PutChar('\n');
    RTIO.PutText("  alerted:    "); RTIO.PutInt(ORD(t.alerted)); RTIO.PutChar('\n');
    RTIO.PutText("  waitingOn:  "); RTIO.PutAddr(t.waitingOn);   RTIO.PutChar('\n');
    RTIO.PutText("  nextWaiter: "); RTIO.PutAddr(t.nextWaiter);  RTIO.PutChar('\n');
    RTIO.PutText("  frame:      "); RTIO.PutAddr(t.frame);       RTIO.PutChar('\n');
    RTIO.PutText("  next:       "); RTIO.PutAddr(t.next);        RTIO.PutChar('\n');
    RTIO.PutText("  prev:       "); RTIO.PutAddr(t.prev);        RTIO.PutChar('\n');
    RTIO.PutText("  handle:     "); RTIO.PutAddr(t.handle);      RTIO.PutChar('\n');
    RTIO.PutText("  stackbase:  "); RTIO.PutAddr(t.stackbase);   RTIO.PutChar('\n');
    RTIO.PutText("  context:    "); RTIO.PutAddr(t.context);     RTIO.PutChar('\n');
    RTIO.PutText("  state:      ");
    CASE t.state OF
    | ActState.Started => RTIO.PutText("Started\n");
    | ActState.Stopped => RTIO.PutText("Stopped\n");
    | ActState.Starting => RTIO.PutText("Starting\n");
    | ActState.Stopping => RTIO.PutText("Stopping\n");
    END;
    RTIO.Flush();
  END DumpThread;

PROCEDURE DumpThreads () =
  VAR t := allThreads;
  BEGIN
    REPEAT
      DumpThread(t);
      t := t.next
    UNTIL t = allThreads;
  END DumpThreads;

(*------------------------------------------------------------ Fork, Join ---*)

VAR (* LL=activeMu *)
  allThreads: Activation := NIL;            (* global list of active threads *)

PROCEDURE CleanThread (r: REFANY) =
  VAR t := NARROW(r, T);
  BEGIN
    pthread_mutex_delete(t.act.mutex);
    pthread_cond_delete(t.act.cond);
    DISPOSE(t.act);
  END CleanThread;

(* ThreadBase calls RunThread after finding (approximately) where
   its stack begins.  This dance ensures that all of ThreadMain's
   traced references are within the stack scanned by the collector. *)

PROCEDURE ThreadBase (param: ADDRESS): ADDRESS =
  VAR
    me: Activation := param;
  BEGIN
    SetActivation(me);
    me.stackbase := ADR(me); (* enable GC scanning of this stack *)
    me.handle := pthread_self();

    (* add to the list of active threads *)
    WITH r = pthread_mutex_lock(activeMu,ThisLine()) DO <*ASSERT r=0*> END;
      me.next := allThreads;
      me.prev := allThreads.prev;
      allThreads.prev.next := me;
      allThreads.prev := me;
    WITH r = pthread_mutex_unlock(activeMu,ThisLine()) DO <*ASSERT r=0*> END;
    FloatMode.InitThread (me.floatState);

    RunThread(me);

    (* remove from the list of active threads *)
    WITH r = pthread_mutex_lock(activeMu,ThisLine()) DO <*ASSERT r=0*> END;
      <*ASSERT allThreads # me*>
      me.stackbase := NIL; (* disable GC scanning of my stack *)
      me.next.prev := me.prev;
      me.prev.next := me.next;
      WITH r = pthread_detach_self() DO <*ASSERT r=0*> END;
    WITH r = pthread_mutex_unlock(activeMu,ThisLine()) DO <*ASSERT r=0*> END;
    me.next := NIL;
    me.prev := NIL;
    RETURN NIL;
  END ThreadBase;

PROCEDURE RunThread (me: Activation) =
  VAR self: T;
  BEGIN
    IF perfOn THEN PerfChanged(State.alive) END;

    WITH r = pthread_mutex_lock(slotsMu,ThisLine()) DO <*ASSERT r=0*> END;
      self := slots [me.slot];
    WITH r = pthread_mutex_unlock(slotsMu,ThisLine()) DO <*ASSERT r=0*> END;

    IF perfOn THEN PerfRunning() END;

    (*** Run the user-level code. ***)
    self.result := self.closure.apply();

    IF perfOn THEN PerfChanged(State.dying) END;

    (* Join *)
    LOCK joinMu DO
      Broadcast(self.join);
      self.join := NIL;     (* mark me done *)
    END;

    IF perfOn THEN PerfChanged(State.dead) END;

    (* we're dying *)
    RTHeapRep.FlushThreadState(me.heapState);

    IF perfOn THEN PerfDeleted() END;
    FreeSlot(self);  (* note: needs self.act ! *)
    (* Since we're no longer slotted, we cannot touch traced refs. *)
  END RunThread;

VAR joinMu: MUTEX;

PROCEDURE Fork (closure: Closure): T =
  VAR
    act := NEW(Activation,
               mutex := pthread_mutex_new(),
               cond := pthread_cond_new());
    size := defaultStackSize;
    t: T := NIL;
  BEGIN
    TRY
      IF act.mutex = NIL OR act.cond = NIL THEN
        RTE.Raise(RTE.T.OutOfMemory);
      END;
      t := NEW(T, act := act, closure := closure, join := NEW(Condition));
      RTHeapRep.RegisterFinalCleanup(t, CleanThread);
      act.slot := AssignSlot(t);
    FINALLY
      IF act.slot = 0 THEN
        (* we failed, cleanup *)
        pthread_mutex_delete(act.mutex);
        pthread_cond_delete(act.cond);
        DISPOSE(act);
      END;
    END;
    (* determine the initial size of the stack for this thread *)
    TYPECASE closure OF
    | SizedClosure (scl) => size := scl.stackSize;
    ELSE (*skip*)
    END;
    WITH r = thread_create(size * ADRSIZE(Word.T), ThreadBase, act) DO
      IF r # 0 THEN DieI(ThisLine(), r) END;
    END;
    RETURN t;
  END Fork;

PROCEDURE XJoin (self: Activation; t: T; alertable: BOOLEAN):
  REFANY RAISES {Alerted} =
  BEGIN
    LOCK joinMu DO
      IF t.joined THEN Die(ThisLine(), "attempt to join with thread twice") END;
      TRY
        t.joined := TRUE;
        WHILE t.join # NIL DO XWait(self, joinMu, t.join, alertable) END;
      FINALLY
        IF t.join # NIL THEN t.joined := FALSE END;
      END;
    END;
    RETURN t.result;
  END XJoin;

PROCEDURE Join (t: T): REFANY =
  <*FATAL Alerted*>
  VAR self := GetActivation();
  BEGIN
    RETURN XJoin(self, t, alertable := FALSE);
  END Join;

PROCEDURE AlertJoin (t: T): REFANY RAISES {Alerted} =
  VAR self := GetActivation();
  BEGIN
    RETURN XJoin(self, t, alertable := TRUE);
  END AlertJoin;

(*---------------------------------------------------- Scheduling support ---*)

PROCEDURE XPause (self: Activation; n: LONGREAL; alertable: BOOLEAN)
  RAISES {Alerted} =
  VAR until := Time.Now() + n;
  BEGIN
    IF perfOn THEN PerfChanged(State.pausing) END;
    WITH r = pthread_mutex_lock(self.mutex,ThisLine()) DO <*ASSERT r=0*> END;
    <*ASSERT self.waitingOn = NIL*>
    <*ASSERT self.nextWaiter = NIL*>

    LOOP
      IF alertable AND self.alerted THEN
        self.alerted := FALSE;
        WITH r = pthread_mutex_unlock(self.mutex,ThisLine()) DO <*ASSERT r=0*> END;
        IF perfOn THEN PerfRunning() END;
        RAISE Alerted;
      END;
      WITH r = pthread_cond_timedwait(self.cond, self.mutex, until) DO
        IF r = Uerror.ETIMEDOUT THEN
          WITH r = pthread_mutex_unlock(self.mutex,ThisLine()) DO <*ASSERT r=0*> END;
          IF perfOn THEN PerfRunning() END;
          RETURN;
        END;
        <*ASSERT r=0*>
      END;
    END;
  END XPause;

PROCEDURE Pause (n: LONGREAL) =
  <*FATAL Alerted*>
  VAR self := GetActivation();
  BEGIN
    XPause(self, n, alertable := FALSE);
  END Pause;

PROCEDURE AlertPause (n: LONGREAL) RAISES {Alerted} =
  VAR self := GetActivation();
  BEGIN
    XPause(self, n, alertable := TRUE);
  END AlertPause;

PROCEDURE Yield () =
  BEGIN
    WITH r = Usched.yield() DO
      IF r # 0 THEN DieI(ThisLine(), Cerrno.GetErrno()) END;
    END;
  END Yield;

PROCEDURE IOWait (fd: CARDINAL; read: BOOLEAN;
                  timeoutInterval: LONGREAL := -1.0D0): WaitResult =
  <*FATAL Alerted*>
  VAR self := GetActivation();
  BEGIN
    TRY
      IF perfOn THEN PerfChanged(State.blocking) END;
      RETURN XIOWait(self, fd, read, timeoutInterval, alertable := FALSE);
    FINALLY
      IF perfOn THEN PerfRunning() END;
    END;
  END IOWait;

PROCEDURE IOAlertWait (fd: CARDINAL; read: BOOLEAN;
                       timeoutInterval: LONGREAL := -1.0D0): WaitResult
  RAISES {Alerted} =
  VAR self := GetActivation();
  BEGIN
    TRY
      IF perfOn THEN PerfChanged(State.blocking) END;
      RETURN XIOWait(self, fd, read, timeoutInterval, alertable := TRUE);
    FINALLY
      IF perfOn THEN PerfRunning() END;
    END;
  END IOAlertWait;

PROCEDURE XIOWait (self: Activation; fd: CARDINAL; read: BOOLEAN;
                   interval: LONGREAL; alertable: BOOLEAN): WaitResult
  RAISES {Alerted} =
  VAR res: WaitResult;
      subInterval: LONGREAL := 1.0d0;
      err: int := 0;
      again := FALSE;
  BEGIN
    IF NOT alertable THEN
      subInterval := interval;
    ELSIF interval < 0.0d0 THEN
      interval := LAST(LONGREAL);
    ELSIF interval < subInterval THEN
      subInterval := interval;
    END;

    IF alertable AND XTestAlert(self) THEN RAISE Alerted END;
    LOOP
      res := VAL(Poll(fd, ORD(read), subInterval), WaitResult);

      IF alertable AND XTestAlert(self) THEN RAISE Alerted END;

      CASE res OF
        | WaitResult.FDError, WaitResult.Ready =>
          RETURN res;
        | WaitResult.Error =>
          err := Cerrno.GetErrno();
          IF err = Uerror.EINTR THEN
            (* spurious wakeups are OK *)
          ELSIF err = Uerror.EAGAIN AND NOT again THEN
            again := TRUE; (* try just once more *)
          ELSE
            RETURN WaitResult.Error;
          END;
        | WaitResult.Timeout =>
          interval := interval - subInterval;
          IF interval <= 0.0d0 THEN RETURN WaitResult.Timeout END;
          IF interval < subInterval THEN
            subInterval := interval;
          END;
      END;
    END;
  END XIOWait;

PROCEDURE WaitProcess (pid: int; VAR status: int): int =
  (* ThreadPThread.m3 and ThreadPosix.m3 are very similar. *)
  BEGIN
    LOOP
      WITH r = Uexec.waitpid(pid, ADR(status), 0) DO
        <*ASSERT r # 0*>
        IF r > 0 THEN RETURN r END;
        IF Cerrno.GetErrno() # Uerror.EINTR THEN RETURN r END;
      END;
    END;
  END WaitProcess;

(*--------------------------------------------------- Stack size controls ---*)

VAR defaultStackSize := 4096;

PROCEDURE GetDefaultStackSize (): CARDINAL =
  BEGIN
    RETURN defaultStackSize;
  END GetDefaultStackSize;

PROCEDURE MinDefaultStackSize (size: CARDINAL) =
  BEGIN
    defaultStackSize := MAX(defaultStackSize, size);
  END MinDefaultStackSize;

PROCEDURE IncDefaultStackSize (inc: CARDINAL) =
  BEGIN
    INC(defaultStackSize, inc);
  END IncDefaultStackSize;

(*--------------------------------------------- Garbage collector support ---*)
(* NOTE: These routines are called indirectly by the low-level page fault
   handler of the garbage collector.  So, if they touched traced references,
   they could trigger indefinite invocations of the fault handler. *)

(* In versions of SuspendOthers prior to the addition of the incremental
   collector, it acquired 'cm' to guarantee that no suspended thread held it.
   That way when the collector tried to acquire a mutex or signal a
   condition, it wouldn't deadlock with the suspended thread that held cm.

   With the VM-synchronized, incremental collector this design is inadequate.
   Here's a deadlock that occurred:
      Thread.Broadcast held cm,
      then it touched its condition argument,
      the page containing the condition was protected by the collector,
      another thread started running the page fault handler,
      the handler called SuspendOthers,
      SuspendOthers tried to acquire cm.

   So, SuspendOthers does not grab "cm" before shutting down the other
   threads.  If the collector tries to use any of the thread functions
   that acquire "cm", it'll be deadlocked.
*)

VAR suspended: BOOLEAN := FALSE;        (* LL=activeMu *)

PROCEDURE SuspendOthers () =
  (* LL=0. Always bracketed with ResumeOthers which releases "activeMu" *)
  BEGIN
    WITH r = pthread_mutex_lock(activeMu,ThisLine()) DO <*ASSERT r=0*> END;
    StopWorld();
    <*ASSERT NOT suspended*>
    suspended := TRUE;
  END SuspendOthers;

PROCEDURE ResumeOthers () =
  (* LL=activeMu.  Always preceded by SuspendOthers. *)
  BEGIN
    <*ASSERT suspended*>
    suspended := FALSE;
    StartWorld();
    WITH r = pthread_mutex_unlock(activeMu,ThisLine()) DO <*ASSERT r=0*> END;
  END ResumeOthers;

PROCEDURE ProcessStacks (p: PROCEDURE (start, limit: ADDRESS)) =
  (* LL=activeMu.  Only called within {SuspendOthers, ResumeOthers} *)
  VAR
    me := GetActivation();
    act: Activation;
  BEGIN
    ProcessMe(me, p);
    act := me.next;
    WHILE act # me DO
      ProcessOther(act, p);
      act := act.next;
    END;
  END ProcessStacks;

PROCEDURE ProcessEachStack (p: PROCEDURE (start, limit: ADDRESS)) =
  (* LL=0 *)
  VAR
    me := GetActivation();
    act: Activation;
    acks: int;
    nLive, nDead, newlySent: INTEGER := 0;
    wait_nsecs := RETRY_INTERVAL;
  BEGIN
    WITH r = pthread_mutex_lock(activeMu,ThisLine()) DO <*ASSERT r=0*> END;

    ProcessMe(me, p);

    act := me.next;
    WHILE act # me DO
      (* stop *)
      LOOP
        <*ASSERT act.state = ActState.Started*>
        SetState(act, ActState.Stopping);
        IF SIG_SUSPEND = 0 THEN
          IF StopThread(act) THEN
            SetState(act, ActState.Stopped);
            EXIT;
          ELSE
            SetState(act, ActState.Started);
          END;
        ELSE
          SignalThread(act);
          INC(nLive);
          EXIT;
        END;
        Nanosleep(WAIT_UNIT);
      END;
      WHILE nLive > 0 DO
        <*ASSERT SIG_SUSPEND # 0*>
        WITH r = sem_getvalue(acks) DO <*ASSERT r=0*> END;
        IF acks = nLive THEN EXIT END;
        <*ASSERT acks < nLive*>
        IF wait_nsecs <= 0 THEN
          newlySent := 0;
          <*ASSERT act.state # ActState.Starting*>
          IF act.state # ActState.Stopped THEN
            SetState(act, ActState.Stopping);
            SignalThread(act);
            INC(newlySent);
          END;
          WITH r = sem_getvalue(acks) DO <*ASSERT r=0*> END;
          IF newlySent < nLive - acks THEN
            (* how did we manage to lose some? *)
            nLive := acks + newlySent;
          END;
          wait_nsecs := RETRY_INTERVAL;
        ELSE
          Nanosleep(WAIT_UNIT);
          DEC(wait_nsecs, WAIT_UNIT);
        END;
      END;
      FOR i := 0 TO nLive - 1 DO
        WHILE sem_wait() # 0 DO
          WITH r = Cerrno.GetErrno() DO
            IF r # Uerror.EINTR THEN DieI(ThisLine(), r) END;
          END;
          (*retry*)
        END;
      END;

      (* process *)
      ProcessOther(act, p);

      (* start *)
      nDead := 0;
      LOOP
        <*ASSERT act.state = ActState.Stopped*>
        SetState(act, ActState.Starting);
        IF SIG_SUSPEND = 0 THEN
          IF StartThread(act) THEN
            SetState(act, ActState.Started);
            EXIT;
          ELSE
            SetState(act, ActState.Stopped);
          END;
        ELSE
          SignalThread(act);
          INC(nDead);
          EXIT;
        END;
        Nanosleep(WAIT_UNIT);
      END;
      WHILE nDead > 0 DO
        <*ASSERT SIG_SUSPEND # 0*>
        WITH r = sem_getvalue(acks) DO <*ASSERT r=0*> END;
        IF acks = nDead THEN EXIT END;
        <*ASSERT acks < nDead*>
        IF wait_nsecs <= 0 THEN
          newlySent := 0;
          <*ASSERT act.state # ActState.Stopping*>
          IF act.state # ActState.Started THEN
            SignalThread(act);
            INC(newlySent);
          END;
          WITH r = sem_getvalue(acks) DO <*ASSERT r=0*> END;
          IF newlySent < nDead - acks THEN
            (* how did we manage to lose some? *)
            nDead := acks + newlySent;
          END;
          wait_nsecs := RETRY_INTERVAL;
        ELSE
          Nanosleep(WAIT_UNIT);
          DEC(wait_nsecs, WAIT_UNIT);
        END;
      END;
      FOR i := 0 TO nDead - 1 DO
        WHILE sem_wait() # 0 DO
          WITH r = Cerrno.GetErrno() DO
            IF r # Uerror.EINTR THEN DieI(ThisLine(), r) END;
          END;
          (*retry*)
        END;
      END;
    END;

    WITH r = pthread_mutex_unlock(activeMu,ThisLine()) DO <*ASSERT r=0*> END;
  END ProcessEachStack;

PROCEDURE ProcessMe (me: Activation;  p: PROCEDURE (start, limit: ADDRESS)) =
  (* LL=activeMu *)
  BEGIN
    <*ASSERT me.state # ActState.Stopped*>
    IF DEBUG THEN
      RTIO.PutText("Processing act="); RTIO.PutAddr(me); RTIO.PutText("\n"); RTIO.Flush();
    END;
    RTHeapRep.FlushThreadState(me.heapState);
    ProcessLive(me.stackbase, p);
  END ProcessMe;

PROCEDURE ProcessOther (act: Activation;  p: PROCEDURE (start, stop: ADDRESS)) =
  (* LL=activeMu *)
  BEGIN
    <*ASSERT act.state = ActState.Stopped*>
    IF DEBUG THEN
      RTIO.PutText("Processing act="); RTIO.PutAddr(act); RTIO.PutText("\n"); RTIO.Flush();
    END;
    RTHeapRep.FlushThreadState(act.heapState);
    IF act.stackbase # NIL THEN
      ProcessStopped(act.handle, act.stackbase, act.context, p);
    END;
  END ProcessOther;

(* Signal based suspend/resume *)

PROCEDURE SignalThread(act: Activation) =
  (* LL=activeMu *)
  BEGIN
    <*ASSERT SIG_SUSPEND # 0*>
    LOOP
      WITH z = pthread_kill(act.handle, SIG_SUSPEND) DO
        IF z = 0 THEN EXIT END;
        IF z # Uerror.EAGAIN THEN DieI(ThisLine(), z) END;
        (* try it again... *)
      END;
    END;
  END SignalThread;

PROCEDURE StopThread (act: Activation): BOOLEAN =
  (* LL=activeMu *)
  BEGIN
    <*ASSERT act.state = ActState.Stopping*>
    <*ASSERT SIG_SUSPEND = 0*>
    IF NOT SuspendThread(act.handle) THEN RETURN FALSE END;
    IF act.heapState.inCritical # 0 THEN
      IF NOT RestartThread(act.handle) THEN <*ASSERT FALSE*> END;
      RETURN FALSE;
    END;
    RETURN TRUE;
  END StopThread;

PROCEDURE StartThread (act: Activation): BOOLEAN =
  (* LL=activeMu *)
  BEGIN
    <*ASSERT act.state = ActState.Starting*>
    <*ASSERT SIG_SUSPEND = 0*>
    RETURN RestartThread(act.handle);
  END StartThread;

PROCEDURE StopWorld () =
  (* LL=activeMu *)
  VAR
    me := GetActivation();
    act: Activation;
    acks: int;
    nLive, newlySent: INTEGER;
    retry: BOOLEAN;
    wait_nsecs := RETRY_INTERVAL;
  BEGIN
    IF DEBUG THEN
      RTIO.PutText("Stopping from act="); RTIO.PutAddr(me); RTIO.PutText("\n"); RTIO.Flush();
    END;

    nLive := 0;
    LOOP
      retry := FALSE;
      act := me.next;
      WHILE act # me DO
        <*ASSERT act.state # ActState.Starting*>
        IF act.state = ActState.Started THEN
          SetState(act, ActState.Stopping);
          IF SIG_SUSPEND = 0 THEN
            IF StopThread(act) THEN
              SetState(act, ActState.Stopped);
            ELSE
              SetState(act, ActState.Started);
              retry := TRUE;
            END;
          ELSE
            SignalThread(act);
            INC(nLive);
          END;
        END;
        act := act.next;
      END;
      IF NOT retry THEN EXIT END;
      Nanosleep(WAIT_UNIT);
    END;
    WHILE nLive > 0 DO
      <*ASSERT SIG_SUSPEND # 0*>
      WITH r = sem_getvalue(acks) DO <*ASSERT r=0*> END;
      IF acks = nLive THEN EXIT END;
      <*ASSERT acks < nLive*>
      IF wait_nsecs <= 0 THEN
        newlySent := 0;
        act := me.next;
        WHILE act # me DO
          <*ASSERT act.state # ActState.Starting*>
          IF act.state # ActState.Stopped THEN
            SetState(act, ActState.Stopping);
            SignalThread(act);
            INC(newlySent);
          END;
          act := act.next;
        END;
        WITH r = sem_getvalue(acks) DO <*ASSERT r=0*> END;
        IF newlySent < nLive - acks THEN
          (* how did we manage to lose some? *)
          nLive := acks + newlySent;
        END;
        wait_nsecs := RETRY_INTERVAL;
      ELSE
        Nanosleep(WAIT_UNIT);
        DEC(wait_nsecs, WAIT_UNIT);
      END;
    END;
    (* drain semaphore *)
    FOR i := 0 TO nLive-1 DO
      WHILE sem_wait() # 0 DO
        WITH r = Cerrno.GetErrno() DO
          IF r # Uerror.EINTR THEN DieI(ThisLine(), r) END;
        END;
        (*retry*)
      END;
    END;

    IF DEBUG THEN
      RTIO.PutText("Stopped from act="); RTIO.PutAddr(me); RTIO.PutText("\n"); RTIO.Flush();
      DumpThreads();
    END;
  END StopWorld;

PROCEDURE StartWorld () =
  (* LL=activeMu *)
  VAR
    me := GetActivation();
    act: Activation;
    acks: int;
    nDead, newlySent: INTEGER;
    retry: BOOLEAN;
    wait_nsecs := RETRY_INTERVAL;
  BEGIN
    IF DEBUG THEN
      RTIO.PutText("Starting from act="); RTIO.PutAddr(me); RTIO.PutText("\n"); RTIO.Flush();
    END;

    nDead := 0;
    LOOP
      retry := FALSE;
      act := me.next;
      WHILE act # me DO
        <*ASSERT act.state # ActState.Stopping*>
        IF act.state # ActState.Started THEN
          SetState(act, ActState.Starting);
          IF SIG_SUSPEND = 0 THEN
            IF StartThread(act) THEN
              SetState(act, ActState.Started);
            ELSE
              SetState(act, ActState.Stopped);
              retry := TRUE;
            END;
          ELSE
            SignalThread(act);
            INC(nDead);
          END;
        END;
        act := act.next;
      END;
      IF NOT retry THEN EXIT END;
      Nanosleep(WAIT_UNIT);
    END;
    WHILE nDead > 0 DO
      <*ASSERT SIG_SUSPEND # 0*>
      WITH r = sem_getvalue(acks) DO <*ASSERT r=0*> END;
      IF acks = nDead THEN EXIT END;
      <*ASSERT acks < nDead*>
      IF wait_nsecs <= 0 THEN
        newlySent := 0;
        act := me.next;
        WHILE act # me DO
          <*ASSERT act.state # ActState.Stopping*>
          IF act.state # ActState.Started THEN
            SignalThread(act);
            INC(newlySent);
          END;
          act := act.next;
        END;
        WITH r = sem_getvalue(acks) DO <*ASSERT r=0*> END;
        IF newlySent < nDead - acks THEN
          (* how did we manage to lose some? *)
          nDead := acks + newlySent;
        END;
        wait_nsecs := RETRY_INTERVAL;
      ELSE
        Nanosleep(WAIT_UNIT);
        DEC(wait_nsecs, WAIT_UNIT);
      END;
    END;
    (* drain semaphore *)
    FOR i := 0 TO nDead-1 DO
      WHILE sem_wait() # 0 DO
        WITH r = Cerrno.GetErrno() DO
          IF r # Uerror.EINTR THEN DieI(ThisLine(), r) END;
        END;
        (*retry*)
      END;
    END;

    IF DEBUG THEN
      RTIO.PutText("Started from act="); RTIO.PutAddr(me); RTIO.PutText("\n"); RTIO.Flush();
      DumpThreads();
    END;
  END StartWorld;

PROCEDURE SignalHandler (sig: int; <*UNUSED*>info: ADDRESS; context: ADDRESS) =
  VAR
    errno := Cerrno.GetErrno();
    me := GetActivation();
  BEGIN
    <*ASSERT sig = SIG_SUSPEND*>
    IF me.state = ActState.Stopping THEN
      IF me.heapState.inCritical # 0 THEN
        me.state := ActState.Started;
        RETURN;
      END;
      me.state := ActState.Stopped;
      <*ASSERT me.context = NIL*>
      me.context := context;
      WITH r = sem_post() DO <*ASSERT r=0*> END;
      REPEAT sigsuspend() UNTIL me.state = ActState.Starting;
      me.context := NIL;
      me.state := ActState.Started;
      WITH r = sem_post() DO <*ASSERT r=0*> END;
    END;
    Cerrno.SetErrno(errno);
  END SignalHandler;

(*----------------------------------------------------------- misc. stuff ---*)

PROCEDURE MyId (): Id RAISES {} =
  VAR me := GetActivation();
  BEGIN
    IF me = NIL
      THEN RETURN 0
      ELSE RETURN me.slot;
    END;
  END MyId;

PROCEDURE MyFPState (): UNTRACED REF FloatMode.ThreadState =
  VAR me := GetActivation();
  BEGIN
    RETURN ADR(me.floatState);
  END MyFPState;

PROCEDURE MyHeapState (): UNTRACED REF RTHeapRep.ThreadState =
  VAR me := GetActivation();
  BEGIN
    RETURN ADR(me.heapState);
  END MyHeapState;

PROCEDURE DisableSwitching () =
  BEGIN
    (* no user-level thread switching *)
  END DisableSwitching;

PROCEDURE EnableSwitching () =
  BEGIN
    (* no user-level thread switching *)
  END EnableSwitching;

(*---------------------------------------------------------------- errors ---*)

PROCEDURE Die (lineno: INTEGER; msg: TEXT) =
  BEGIN
    RTError.Msg (ThisFile(), lineno, "Thread client error: ", msg);
  END Die;

PROCEDURE DieI (lineno: INTEGER; i: INTEGER) =
  BEGIN
    RTError.MsgI (ThisFile(), lineno, "Thread client error: ", i);
  END DieI;

(*------------------------------------------------------ ShowThread hooks ---*)

VAR
  perfW : RTPerfTool.Handle;
  perfOn: BOOLEAN := FALSE;     (* LL = perfMu *)

PROCEDURE PerfStart () =
  BEGIN
    IF RTPerfTool.Start ("showthread", perfW) THEN
      perfOn := TRUE;
      RTProcess.RegisterExitor (PerfStop);
    END;
  END PerfStart;

PROCEDURE PerfStop () =
  BEGIN
    (* UNSAFE, but needed to prevent deadlock if we're crashing! *)
    RTPerfTool.Close (perfW);
  END PerfStop;

CONST
  EventSize = (BITSIZE(ThreadEvent.T) + BITSIZE(CHAR) - 1) DIV BITSIZE(CHAR);

TYPE
  TE = ThreadEvent.Kind;

PROCEDURE PerfChanged (s: State) =
  VAR
    e := ThreadEvent.T {kind := TE.Changed, id := MyId(), state := s};
  BEGIN
    WITH r = pthread_mutex_lock(perfMu,ThisLine()) DO <*ASSERT r=0*> END;
      perfOn := RTPerfTool.Send (perfW, ADR (e), EventSize);
    WITH r = pthread_mutex_unlock(perfMu,ThisLine()) DO <*ASSERT r=0*> END;
  END PerfChanged;

PROCEDURE PerfDeleted () =
  VAR
    e := ThreadEvent.T {kind := TE.Deleted, id := MyId()};
  BEGIN
    WITH r = pthread_mutex_lock(perfMu,ThisLine()) DO <*ASSERT r=0*> END;
      perfOn := RTPerfTool.Send (perfW, ADR (e), EventSize);
    WITH r = pthread_mutex_unlock(perfMu,ThisLine()) DO <*ASSERT r=0*> END;
  END PerfDeleted;

PROCEDURE PerfRunning () =
  VAR
    e := ThreadEvent.T {kind := TE.Running, id := MyId()};
  BEGIN
    WITH r = pthread_mutex_lock(perfMu,ThisLine()) DO <*ASSERT r=0*> END;
      perfOn := RTPerfTool.Send (perfW, ADR (e), EventSize);
    WITH r = pthread_mutex_unlock(perfMu,ThisLine()) DO <*ASSERT r=0*> END;
  END PerfRunning;

(*-------------------------------------------------------- Initialization ---*)

PROCEDURE InitWithStackBase (stackbase: ADDRESS) =
  VAR
    self: T;
    me: Activation;
  BEGIN
    InitC(stackbase);

    me := NEW(Activation,
              mutex := pthread_mutex_new(),
              cond := pthread_cond_new());
    InitActivations(me);
    me.stackbase := stackbase;
    IF me.mutex = NIL OR me.cond = NIL THEN
      Die(ThisLine(), "Thread initialization failed.");
    END;
    self := NEW(T, act := me, closure := NIL, join := NIL);
    me.slot := AssignSlot(self);

    joinMu := NEW(MUTEX);

    PerfStart();
    IF perfOn THEN PerfRunning() END;
    IF RTParams.IsPresent("backgroundgc") THEN
      RTCollectorSRC.StartBackgroundCollection();
    END;
    IF RTParams.IsPresent("foregroundgc") THEN
      RTCollectorSRC.StartForegroundCollection();
    END;
  END InitWithStackBase;

PROCEDURE Init ()=
  VAR r: INTEGER;
  BEGIN
    r := RTProcess.RegisterForkHandlers(AtForkPrepare,
                                        AtForkParent,
                                        AtForkChild);
    IF r # 0 THEN DieI(ThisLine(), r) END;
    InitWithStackBase(ADR(r)); (* not quite accurate but hopefully ok *)
  END Init;

PROCEDURE PThreadLockMutex(mutex: pthread_mutex_t; line: INTEGER) =
  BEGIN
    IF mutex # NIL THEN
      WITH r = pthread_mutex_lock(mutex,ThisLine()) DO
        IF r # 0 THEN DieI(line, r) END;
      END;
    END;
  END PThreadLockMutex;

PROCEDURE PThreadUnlockMutex(mutex: pthread_mutex_t;  line: INTEGER) =
  BEGIN
    IF mutex # NIL THEN
      WITH r = pthread_mutex_unlock(mutex,ThisLine()) DO
        IF r # 0 THEN DieI(line, r) END;
      END;
    END;
  END PThreadUnlockMutex;

PROCEDURE AtForkPrepare() =
  VAR me := GetActivation();
      act: Activation;
  BEGIN
    PThreadLockMutex(slotsMu, ThisLine());
    PThreadLockMutex(perfMu, ThisLine());
    PThreadLockMutex(initMu, ThisLine()); (* InitMutex => RegisterFinalCleanup => LockHeap *)
    PThreadLockMutex(heapMu, ThisLine());
    PThreadLockMutex(activeMu, ThisLine()); (* LockHeap => SuspendOthers => activeMu *)
    (* Walk activations and lock all threads.
     * NOTE: We have initMu, activeMu, so slots won't change, conditions and
     * mutexes won't be initialized on-demand.
     *)
    act := me;
    REPEAT
      PThreadLockMutex(act.mutex, ThisLine());
      act := act.next;
    UNTIL act = me;
  END AtForkPrepare;

PROCEDURE AtForkParent() =
  VAR me := GetActivation();
      act: Activation;
      cond: Condition;
  BEGIN
    (* Walk activations and unlock all threads, conditions. *)
    act := me;
    REPEAT
      cond := slots[act.slot].join;
      IF cond # NIL THEN PThreadUnlockMutex(cond.mutex, ThisLine()) END;
      PThreadUnlockMutex(act.mutex, ThisLine());
      act := act.next;
    UNTIL act = me;
    PThreadUnlockMutex(activeMu, ThisLine());
    PThreadUnlockMutex(heapMu, ThisLine());
    PThreadUnlockMutex(initMu, ThisLine());
    PThreadUnlockMutex(perfMu, ThisLine());
    PThreadUnlockMutex(slotsMu, ThisLine());
  END AtForkParent;

PROCEDURE AtForkChild() =
  BEGIN
    AtForkParent();
    InitWithStackBase(GetActivation().stackbase);
  END AtForkChild;

(*------------------------------------------------------------- collector ---*)
(* These procedures provide synchronization primitives for the allocator
   and collector. *)

VAR
  holder: pthread_t;
  inCritical := 0;

PROCEDURE LockHeap () =
  VAR self := pthread_self();
  BEGIN
    WITH r = pthread_mutex_lock(heapMu,ThisLine()) DO <*ASSERT r=0*> END;
    IF inCritical = 0 THEN
      holder := self;
    ELSIF pthread_equal(holder, self) = 0 THEN
      WITH r = pthread_cond_wait(heapCond, heapMu, ThisLine()) DO <*ASSERT r=0*> END;
    END;
    INC(inCritical);
    WITH r = pthread_mutex_unlock(heapMu,ThisLine()) DO <*ASSERT r=0*> END;
  END LockHeap;

PROCEDURE UnlockHeap () =
  VAR self := pthread_self();
  BEGIN
    WITH r = pthread_mutex_lock(heapMu,ThisLine()) DO <*ASSERT r=0*> END;
    <*ASSERT pthread_equal(holder, self) # 0*>
    DEC(inCritical);
    IF inCritical = 0 THEN
      holder := NIL;
    END;
    WITH r = pthread_mutex_unlock(heapMu,ThisLine()) DO <*ASSERT r=0*> END;
  END UnlockHeap;

PROCEDURE WaitHeap () =
  VAR self := pthread_self();
  BEGIN
    WITH r = pthread_mutex_lock(heapMu,ThisLine()) DO <*ASSERT r=0*> END;
    <*ASSERT pthread_equal(holder, self) # 0*>
    DEC(inCritical);
    <*ASSERT inCritical = 0*>
    WITH r = pthread_cond_wait(heapCond, heapMu, ThisLine()) DO <*ASSERT r=0*> END;
    holder := self;
    <*ASSERT inCritical = 0*>
    INC(inCritical);
    WITH r = pthread_mutex_unlock(heapMu,ThisLine()) DO <*ASSERT r=0*> END;
  END WaitHeap;

PROCEDURE BroadcastHeap () =
  BEGIN
    WITH r = pthread_cond_broadcast(heapCond) DO <*ASSERT r=0*> END;
  END BroadcastHeap;

(*--------------------------------------------- exception handling support --*)

PROCEDURE GetCurrentHandlers (): ADDRESS =
  VAR me := GetActivation();
  BEGIN
    RETURN me.frame;
  END GetCurrentHandlers;

PROCEDURE SetCurrentHandlers (h: ADDRESS) =
  VAR me := GetActivation();
  BEGIN
    me.frame := h;
  END SetCurrentHandlers;

(*RTHooks.PushEFrame*)
PROCEDURE PushEFrame (frame: ADDRESS) =
  TYPE Frame = UNTRACED REF RECORD next: ADDRESS END;
  VAR
    me := GetActivation();
    f: Frame := frame;
  BEGIN
    f.next := me.frame;
    me.frame := f;
  END PushEFrame;

(*RTHooks.PopEFrame*)
PROCEDURE PopEFrame (frame: ADDRESS) =
  VAR me := GetActivation();
  BEGIN
    me.frame := frame;
  END PopEFrame;

VAR DEBUG := RTParams.IsPresent("debugthreads");

BEGIN
END ThreadPThread.
-------------- next part --------------
(* Copyright (C) 2005, Purdue Research Foundation                  *)
(* All rights reserved.                                            *)
(* See the file COPYRIGHT-PURDUE for a full description.           *)

(*---------------------------------------------------------------------------*)

UNSAFE INTERFACE ThreadPThread;

FROM Ctypes IMPORT int;
FROM Cstddef IMPORT size_t;

TYPE
  (* These are opaque C references (not necessarily UNTRACED REF ADDRESS) *)
  pthread_t = UNTRACED BRANDED REF ADDRESS;
  pthread_mutex_t = UNTRACED BRANDED REF ADDRESS;
  pthread_cond_t = UNTRACED BRANDED REF ADDRESS;
  Activation <: ADDRESS; (* untraced thread stated stored in thread local *)

(*---------------------------------------------------------------------------*)

PROCEDURE SignalHandler(sig: int; info, uap: ADDRESS);

(*---------------------------------------------------------------------------*)

<*EXTERNAL "ThreadPThread__SIG_SUSPEND"*>
(*CONST*) VAR SIG_SUSPEND: int;

(*---------------------------------------------------------------------------*)

<*EXTERNAL "ThreadPThread__InitC"*>
PROCEDURE InitC(bottom: ADDRESS);

(*---------------------------------------------------------------------------*)

(* the semaphore is implied *)

<*EXTERNAL "ThreadPThread__sem_wait"*>
PROCEDURE sem_wait (): int;
<*EXTERNAL "ThreadPThread__sem_post"*>
PROCEDURE sem_post (): int;

<*EXTERNAL "ThreadPThread__sem_getvalue"*>
PROCEDURE sem_getvalue (VAR value: int): int;

(*---------------------------------------------------------------------------*)

(* the signal set is implied *)

<*EXTERNAL "ThreadPThread__sigsuspend"*>
PROCEDURE sigsuspend ();

(*---------------------------------------------------------------------------*)

(* pthread_create but replace attr with stackSize so that attr need not be known to Modula-3 *)

<*EXTERNAL "ThreadPThread__thread_create"*>
PROCEDURE thread_create(stackSize: size_t;
                        start_routine: PROCEDURE(arg: ADDRESS): ADDRESS; arg: ADDRESS): int;

<*EXTERNAL ThreadPThread__pthread_detach_self*>
PROCEDURE pthread_detach_self(): int;

<*EXTERNAL ThreadPThread__pthread_self*>
PROCEDURE pthread_self(): pthread_t;

<*EXTERNAL "ThreadPThread__pthread_equal"*>
PROCEDURE pthread_equal(t1, t2: pthread_t): int;

<*EXTERNAL "ThreadPThread__pthread_kill"*>
PROCEDURE pthread_kill(t: pthread_t; sig: int): int;

(*---------------------------------------------------------------------------*)

(* static mutexes and conditions *)

<*EXTERNAL "ThreadPThread__activeMu"*> VAR activeMu: pthread_mutex_t;
<*EXTERNAL "ThreadPThread__slotsMu"*>  VAR slotsMu: pthread_mutex_t;
<*EXTERNAL "ThreadPThread__initMu"*>   VAR initMu: pthread_mutex_t;
<*EXTERNAL "ThreadPThread__perfMu"*>   VAR perfMu: pthread_mutex_t;
<*EXTERNAL "ThreadPThread__heapMu"*>   VAR heapMu: pthread_mutex_t;
<*EXTERNAL "ThreadPThread__heapCond"*> VAR heapCond: pthread_cond_t;

(* thread local "activation" *)

<*EXTERNAL ThreadPThread__SetActivation*>
PROCEDURE SetActivation(value: Activation);

<*EXTERNAL ThreadPThread__GetActivation*>
PROCEDURE GetActivation(): Activation;

(*---------------------------------------------------------------------------*)

(* support for dynamically allocated mutexes and condition variables *)

<*EXTERNAL "ThreadPThread__pthread_mutex_new"*>
PROCEDURE pthread_mutex_new():pthread_mutex_t;

<*EXTERNAL "ThreadPThread__pthread_mutex_delete"*>
PROCEDURE pthread_mutex_delete(a:pthread_mutex_t);

<*EXTERNAL ThreadPThread__pthread_mutex_lock*>
PROCEDURE pthread_mutex_lock(mutex: pthread_mutex_t;line :int):int;

<*EXTERNAL ThreadPThread__pthread_mutex_unlock*>
PROCEDURE pthread_mutex_unlock(mutex: pthread_mutex_t;line :int):int;

<*EXTERNAL "ThreadPThread__pthread_cond_new"*>
PROCEDURE pthread_cond_new(): pthread_cond_t;

<*EXTERNAL "ThreadPThread__pthread_cond_delete"*>
PROCEDURE pthread_cond_delete(cond: pthread_cond_t);

<*EXTERNAL ThreadPThread__pthread_cond_wait*>
PROCEDURE pthread_cond_wait(cond: pthread_cond_t; mutex: pthread_mutex_t; line:int):int;

<*EXTERNAL ThreadPThread__pthread_cond_timedwait*>
PROCEDURE pthread_cond_timedwait(cond: pthread_cond_t;
                                 mutex: pthread_mutex_t;
                                 abs: LONGREAL(*Time.T*)):int;

<*EXTERNAL ThreadPThread__pthread_cond_signal*>
PROCEDURE pthread_cond_signal(cond: pthread_cond_t):int;

<*EXTERNAL ThreadPThread__pthread_cond_broadcast*>
PROCEDURE pthread_cond_broadcast(cond: pthread_cond_t):int;

(*---------------------------------------------------------------------------*)

<*EXTERNAL "ThreadPThread__Nanosleep"*>
PROCEDURE Nanosleep(nanoseconds: INTEGER);

(*---------------------------------------------------------------------------*)

<*EXTERNAL "ThreadPThread__SuspendThread"*>
PROCEDURE SuspendThread (t: pthread_t): BOOLEAN;

<*EXTERNAL "ThreadPThread__RestartThread"*>
PROCEDURE RestartThread (t: pthread_t): BOOLEAN;

<*EXTERNAL "ThreadPThread__ProcessLive"*>
PROCEDURE ProcessLive
  (bottom: ADDRESS; p: PROCEDURE(start, limit: ADDRESS));

<*EXTERNAL "ThreadPThread__ProcessStopped"*>
PROCEDURE ProcessStopped
  (t: pthread_t; bottom, context: ADDRESS; p: PROCEDURE(start, limit: ADDRESS));
(*---------------------------------------------------------------------------*)

END ThreadPThread.
-------------- next part --------------
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.   344 lock   0x1383d70 self 0x1009400
l.   348 unlock 0x1383d70 self 0x1009400
l.   350 lock   0x1383d70 self 0x1009400
l.   381 unlock 0x1383d70 self 0x1009400
l.   103 lock   0x1383d78 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.   112 unlock 0x1383d78 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x10250e0 self 0x1009400
l.   125 unlock 0x10250e0 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x10250e0 self 0x1009400
l.   157 unlock 0x10250e0 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x10250e0 self 0x1009400
l.   125 unlock 0x10250e0 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x10250e0 self 0x1009400
l.   157 unlock 0x10250e0 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x10250e0 self 0x1009400
l.   125 unlock 0x10250e0 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x10250e0 self 0x1009400
l.   157 unlock 0x10250e0 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x10250e0 self 0x1009400
l.   125 unlock 0x10250e0 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x10250e0 self 0x1009400
l.   157 unlock 0x10250e0 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x10250e0 self 0x1009400
l.   125 unlock 0x10250e0 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x10250e0 self 0x1009400
l.   157 unlock 0x10250e0 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   103 lock   0x1383d78 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.   112 unlock 0x1383d78 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x10250e8 self 0x1009400
l.   125 unlock 0x10250e8 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x10250e8 self 0x1009400
l.   157 unlock 0x10250e8 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   103 lock   0x1383d78 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.   112 unlock 0x1383d78 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x10250f0 self 0x1009400
l.   125 unlock 0x10250f0 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x10250f0 self 0x1009400
l.   157 unlock 0x10250f0 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.   757 lock   0x1383d68 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.   769 unlock 0x1383d68 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.   757 lock   0x1383d68 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.   769 unlock 0x1383d68 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.   103 lock   0x1383d78 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.   112 unlock 0x1383d78 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x10250f8 self 0x1009400
l.   125 unlock 0x10250f8 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x10250f8 self 0x1009400
l.   157 unlock 0x10250f8 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x10250f8 self 0x1009400
l.   125 unlock 0x10250f8 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x10250f8 self 0x1009400
l.   157 unlock 0x10250f8 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x10250f8 self 0x1009400
l.   125 unlock 0x10250f8 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x10250f8 self 0x1009400
l.   157 unlock 0x10250f8 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.   103 lock   0x1383d78 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.   112 unlock 0x1383d78 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025100 self 0x1009400
l.   125 unlock 0x1025100 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x10250e0 self 0x1009400
l.   125 unlock 0x10250e0 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x10250e0 self 0x1009400
l.   157 unlock 0x10250e0 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025100 self 0x1009400
l.   157 unlock 0x1025100 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.   757 lock   0x1383d68 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.   769 unlock 0x1383d68 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.   757 lock   0x1383d68 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.   769 unlock 0x1383d68 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x10250f0 self 0x1009400
l.   125 unlock 0x10250f0 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x10250f0 self 0x1009400
l.   157 unlock 0x10250f0 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   103 lock   0x1383d78 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.   112 unlock 0x1383d78 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025108 self 0x1009400
l.   125 unlock 0x1025108 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025108 self 0x1009400
l.   157 unlock 0x1025108 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025108 self 0x1009400
l.   125 unlock 0x1025108 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025108 self 0x1009400
l.   157 unlock 0x1025108 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025108 self 0x1009400
l.   125 unlock 0x1025108 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025108 self 0x1009400
l.   157 unlock 0x1025108 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025108 self 0x1009400
l.   125 unlock 0x1025108 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025108 self 0x1009400
l.   157 unlock 0x1025108 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025108 self 0x1009400
l.   125 unlock 0x1025108 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025108 self 0x1009400
l.   157 unlock 0x1025108 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025108 self 0x1009400
l.   125 unlock 0x1025108 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025108 self 0x1009400
l.   157 unlock 0x1025108 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025108 self 0x1009400
l.   125 unlock 0x1025108 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025108 self 0x1009400
l.   157 unlock 0x1025108 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025108 self 0x1009400
l.   125 unlock 0x1025108 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025108 self 0x1009400
l.   157 unlock 0x1025108 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025108 self 0x1009400
l.   125 unlock 0x1025108 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025108 self 0x1009400
l.   157 unlock 0x1025108 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025108 self 0x1009400
l.   125 unlock 0x1025108 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025108 self 0x1009400
l.   157 unlock 0x1025108 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025108 self 0x1009400
l.   125 unlock 0x1025108 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025108 self 0x1009400
l.   157 unlock 0x1025108 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025108 self 0x1009400
l.   125 unlock 0x1025108 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025108 self 0x1009400
l.   157 unlock 0x1025108 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x10250f0 self 0x1009400
l.   125 unlock 0x10250f0 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x10250f0 self 0x1009400
l.   157 unlock 0x10250f0 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x10250f0 self 0x1009400
l.   125 unlock 0x10250f0 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x10250f0 self 0x1009400
l.   157 unlock 0x10250f0 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025108 self 0x1009400
l.   125 unlock 0x1025108 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025108 self 0x1009400
l.   157 unlock 0x1025108 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025108 self 0x1009400
l.   125 unlock 0x1025108 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025108 self 0x1009400
l.   157 unlock 0x1025108 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025108 self 0x1009400
l.   125 unlock 0x1025108 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025108 self 0x1009400
l.   157 unlock 0x1025108 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025108 self 0x1009400
l.   125 unlock 0x1025108 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025108 self 0x1009400
l.   157 unlock 0x1025108 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025108 self 0x1009400
l.   125 unlock 0x1025108 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025108 self 0x1009400
l.   157 unlock 0x1025108 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025108 self 0x1009400
l.   125 unlock 0x1025108 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025108 self 0x1009400
l.   157 unlock 0x1025108 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025108 self 0x1009400
l.   125 unlock 0x1025108 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025108 self 0x1009400
l.   157 unlock 0x1025108 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025108 self 0x1009400
l.   125 unlock 0x1025108 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025108 self 0x1009400
l.   157 unlock 0x1025108 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.   103 lock   0x1383d78 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.   112 unlock 0x1383d78 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025110 self 0x1009400
l.   125 unlock 0x1025110 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025110 self 0x1009400
l.   157 unlock 0x1025110 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.   757 lock   0x1383d68 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.   769 unlock 0x1383d68 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.   103 lock   0x1383d78 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.   112 unlock 0x1383d78 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025118 self 0x1009400
l.   125 unlock 0x1025118 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025118 self 0x1009400
l.   157 unlock 0x1025118 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025118 self 0x1009400
l.   125 unlock 0x1025118 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025118 self 0x1009400
l.   157 unlock 0x1025118 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025118 self 0x1009400
l.   125 unlock 0x1025118 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025118 self 0x1009400
l.   157 unlock 0x1025118 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025118 self 0x1009400
l.   125 unlock 0x1025118 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025118 self 0x1009400
l.   157 unlock 0x1025118 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025118 self 0x1009400
l.   125 unlock 0x1025118 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025118 self 0x1009400
l.   157 unlock 0x1025118 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025118 self 0x1009400
l.   125 unlock 0x1025118 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025118 self 0x1009400
l.   157 unlock 0x1025118 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025118 self 0x1009400
l.   125 unlock 0x1025118 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025118 self 0x1009400
l.   157 unlock 0x1025118 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025118 self 0x1009400
l.   125 unlock 0x1025118 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025118 self 0x1009400
l.   157 unlock 0x1025118 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025118 self 0x1009400
l.   125 unlock 0x1025118 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025118 self 0x1009400
l.   157 unlock 0x1025118 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025118 self 0x1009400
l.   125 unlock 0x1025118 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025118 self 0x1009400
l.   157 unlock 0x1025118 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025118 self 0x1009400
l.   125 unlock 0x1025118 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025118 self 0x1009400
l.   157 unlock 0x1025118 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025118 self 0x1009400
l.   125 unlock 0x1025118 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025118 self 0x1009400
l.   157 unlock 0x1025118 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025118 self 0x1009400
l.   125 unlock 0x1025118 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025118 self 0x1009400
l.   157 unlock 0x1025118 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025118 self 0x1009400
l.   125 unlock 0x1025118 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025118 self 0x1009400
l.   157 unlock 0x1025118 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025118 self 0x1009400
l.   125 unlock 0x1025118 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025118 self 0x1009400
l.   157 unlock 0x1025118 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025118 self 0x1009400
l.   125 unlock 0x1025118 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025118 self 0x1009400
l.   157 unlock 0x1025118 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025118 self 0x1009400
l.   125 unlock 0x1025118 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025118 self 0x1009400
l.   157 unlock 0x1025118 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025118 self 0x1009400
l.   125 unlock 0x1025118 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025118 self 0x1009400
l.   157 unlock 0x1025118 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025118 self 0x1009400
l.   125 unlock 0x1025118 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025118 self 0x1009400
l.   157 unlock 0x1025118 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025118 self 0x1009400
l.   125 unlock 0x1025118 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025118 self 0x1009400
l.   157 unlock 0x1025118 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025118 self 0x1009400
l.   125 unlock 0x1025118 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025118 self 0x1009400
l.   157 unlock 0x1025118 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025118 self 0x1009400
l.   125 unlock 0x1025118 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025118 self 0x1009400
l.   157 unlock 0x1025118 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025118 self 0x1009400
l.   125 unlock 0x1025118 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025118 self 0x1009400
l.   157 unlock 0x1025118 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025118 self 0x1009400
l.   125 unlock 0x1025118 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025118 self 0x1009400
l.   157 unlock 0x1025118 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025118 self 0x1009400
l.   125 unlock 0x1025118 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025118 self 0x1009400
l.   157 unlock 0x1025118 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   103 lock   0x1383d78 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.   112 unlock 0x1383d78 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025120 self 0x1009400
l.   125 unlock 0x1025120 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025120 self 0x1009400
l.   157 unlock 0x1025120 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025120 self 0x1009400
l.   125 unlock 0x1025120 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025120 self 0x1009400
l.   157 unlock 0x1025120 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025120 self 0x1009400
l.   125 unlock 0x1025120 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025120 self 0x1009400
l.   157 unlock 0x1025120 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025120 self 0x1009400
l.   125 unlock 0x1025120 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025120 self 0x1009400
l.   157 unlock 0x1025120 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025120 self 0x1009400
l.   125 unlock 0x1025120 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025120 self 0x1009400
l.   157 unlock 0x1025120 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025120 self 0x1009400
l.   125 unlock 0x1025120 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025120 self 0x1009400
l.   157 unlock 0x1025120 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025120 self 0x1009400
l.   125 unlock 0x1025120 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025120 self 0x1009400
l.   157 unlock 0x1025120 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025120 self 0x1009400
l.   125 unlock 0x1025120 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025120 self 0x1009400
l.   157 unlock 0x1025120 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025120 self 0x1009400
l.   125 unlock 0x1025120 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025120 self 0x1009400
l.   157 unlock 0x1025120 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025120 self 0x1009400
l.   125 unlock 0x1025120 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025120 self 0x1009400
l.   157 unlock 0x1025120 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025120 self 0x1009400
l.   125 unlock 0x1025120 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025120 self 0x1009400
l.   157 unlock 0x1025120 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x10250f8 self 0x1009400
l.   125 unlock 0x10250f8 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x10250f8 self 0x1009400
l.   157 unlock 0x10250f8 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x10250f0 self 0x1009400
l.   125 unlock 0x10250f0 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x10250f0 self 0x1009400
l.   157 unlock 0x10250f0 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025100 self 0x1009400
l.   125 unlock 0x1025100 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.   757 lock   0x1383d68 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.   769 unlock 0x1383d68 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
l.   152 lock   0x1025100 self 0x1009400
l.   157 unlock 0x1025100 self 0x1009400
l.   158 unlock 0x10250a8 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.   757 lock   0x1383d68 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.   769 unlock 0x1383d68 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.   344 lock   0x1383d70 self 0x1009400
l.   381 unlock 0x1383d70 self 0x1009400
l.   103 lock   0x1383d78 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.   112 unlock 0x1383d78 self 0x1009400
l.   121 lock   0x10250a8 self 0x1009400
l.   122 lock   0x1025138 self 0x1009400
l.   125 unlock 0x1025138 self 0x1009400
l.   126 unlock 0x10250a8 self 0x1009400
l.   103 lock   0x1383d78 self 0x1009400
l.  1373 lock   0x1383d88 self 0x1009400
l.  1380 unlock 0x1383d88 self 0x1009400
l.  1386 lock   0x1383d88 self 0x1009400
l.  1392 unlock 0x1383d88 self 0x1009400
l.   112 unlock 0x1383d78 self 0x1009400
l.   194 lock   0x10250a8 self 0x1009400
l.   198 lock   0x1025140 self 0x1009400
l.   202 unlock 0x1025140 self 0x1009400
l.   151 lock   0x10250a8 self 0x1009400
ERROR: pthread_mutex_lock:11


More information about the M3devel mailing list