Logo Search packages:      
Sourcecode: xulrunner-1.9 version File versions

irix.c

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is the Netscape Portable Runtime (NSPR).
 *
 * The Initial Developer of the Original Code is
 * Netscape Communications Corporation.
 * Portions created by the Initial Developer are Copyright (C) 1998-2000
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

#include "primpl.h"

#include <signal.h>

#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/syssgi.h>
#include <sys/time.h>
#include <sys/immu.h>
#include <sys/utsname.h>
#include <sys/sysmp.h>
#include <sys/pda.h>
#include <sys/prctl.h>
#include <sys/wait.h>
#include <sys/resource.h>
#include <sys/procfs.h>
#include <task.h>
#include <dlfcn.h>

static void _MD_IrixIntervalInit(void);

#if defined(_PR_PTHREADS)
/*
 * for compatibility with classic nspr
 */
void _PR_IRIX_CHILD_PROCESS()
{
}
#else  /* defined(_PR_PTHREADS) */

static void irix_detach_sproc(void);
char *_nspr_sproc_private;    /* ptr. to private region in every sproc */

extern PRUintn    _pr_numCPU;

typedef struct nspr_arena {
      PRCList links;
      usptr_t *usarena;
} nspr_arena;

#define ARENA_PTR(qp) \
      ((nspr_arena *) ((char*) (qp) - offsetof(nspr_arena , links)))

static usptr_t *alloc_new_arena(void);

PRCList arena_list = PR_INIT_STATIC_CLIST(&arena_list);
ulock_t arena_list_lock;
nspr_arena first_arena;
int   _nspr_irix_arena_cnt = 1;

PRCList sproc_list = PR_INIT_STATIC_CLIST(&sproc_list);
ulock_t sproc_list_lock;

typedef struct sproc_data {
      void (*entry) (void *, size_t);
      unsigned inh;
      void *arg;
      caddr_t sp;
      size_t len;
      int *pid;
      int creator_pid;
} sproc_data;

typedef struct sproc_params {
      PRCList links;
      sproc_data sd;
} sproc_params;

#define SPROC_PARAMS_PTR(qp) \
      ((sproc_params *) ((char*) (qp) - offsetof(sproc_params , links)))

long  _nspr_irix_lock_cnt = 0;
long  _nspr_irix_sem_cnt = 0;
long  _nspr_irix_pollsem_cnt = 0;

usptr_t *_pr_usArena;
ulock_t _pr_heapLock;

usema_t *_pr_irix_exit_sem;
PRInt32 _pr_irix_exit_now = 0;
PRInt32 _pr_irix_process_exit_code = 0;   /* exit code for PR_ProcessExit */
PRInt32 _pr_irix_process_exit = 0; /* process exiting due to call to
                                                               PR_ProcessExit */

int _pr_irix_primoridal_cpu_fd[2] = { -1, -1 };
static void (*libc_exit)(int) = NULL;
static void *libc_handle = NULL;

#define _NSPR_DEF_INITUSERS         100   /* default value of CONF_INITUSERS */
#define _NSPR_DEF_INITSIZE          (4 * 1024 * 1024) /* 4 MB */

int _irix_initusers = _NSPR_DEF_INITUSERS;
int _irix_initsize = _NSPR_DEF_INITSIZE;

PRIntn _pr_io_in_progress, _pr_clock_in_progress;

PRInt32 _pr_md_irix_sprocs_created, _pr_md_irix_sprocs_failed;
PRInt32 _pr_md_irix_sprocs = 1;
PRCList _pr_md_irix_sproc_list =
PR_INIT_STATIC_CLIST(&_pr_md_irix_sproc_list);

sigset_t ints_off;
extern sigset_t timer_set;

#if !defined(PR_SETABORTSIG)
#define PR_SETABORTSIG 18
#endif
/*
 * terminate the entire application if any sproc exits abnormally
 */
PRBool _nspr_terminate_on_error = PR_TRUE;

/*
 * exported interface to set the shared arena parameters
 */
void _PR_Irix_Set_Arena_Params(PRInt32 initusers, PRInt32 initsize)
{
    _irix_initusers = initusers;
    _irix_initsize = initsize;
}

static usptr_t *alloc_new_arena()
{
    return(usinit("/dev/zero"));
}

static PRStatus new_poll_sem(struct _MDThread *mdthr, int val)
{
PRIntn _is;
PRStatus rv = PR_SUCCESS;
usema_t *sem = NULL;
PRCList *qp;
nspr_arena *arena;
usptr_t *irix_arena;
PRThread *me = _MD_GET_ATTACHED_THREAD(); 

      if (me && !_PR_IS_NATIVE_THREAD(me))
            _PR_INTSOFF(_is); 
      _PR_LOCK(arena_list_lock);
      for (qp = arena_list.next; qp != &arena_list; qp = qp->next) {
            arena = ARENA_PTR(qp);
            sem = usnewpollsema(arena->usarena, val);
            if (sem != NULL) {
                  mdthr->cvar_pollsem = sem;
                  mdthr->pollsem_arena = arena->usarena;
                  break;
            }
      }
      if (sem == NULL) {
            /*
             * If no space left in the arena allocate a new one.
             */
            if (errno == ENOMEM) {
                  arena = PR_NEWZAP(nspr_arena);
                  if (arena != NULL) {
                        irix_arena = alloc_new_arena();
                        if (irix_arena) {
                              PR_APPEND_LINK(&arena->links, &arena_list);
                              _nspr_irix_arena_cnt++;
                              arena->usarena = irix_arena;
                              sem = usnewpollsema(arena->usarena, val);
                              if (sem != NULL) {
                                    mdthr->cvar_pollsem = sem;
                                    mdthr->pollsem_arena = arena->usarena;
                              } else
                                    rv = PR_FAILURE;
                        } else {
                              PR_DELETE(arena);
                              rv = PR_FAILURE;
                        }

                  } else
                        rv = PR_FAILURE;
            } else
                  rv = PR_FAILURE;
      }
      _PR_UNLOCK(arena_list_lock);
      if (me && !_PR_IS_NATIVE_THREAD(me))
            _PR_FAST_INTSON(_is);
      if (rv == PR_SUCCESS)
            _MD_ATOMIC_INCREMENT(&_nspr_irix_pollsem_cnt);
      return rv;
}

static void free_poll_sem(struct _MDThread *mdthr)
{
PRIntn _is;
PRThread *me = _MD_GET_ATTACHED_THREAD(); 

      if (me && !_PR_IS_NATIVE_THREAD(me))
            _PR_INTSOFF(_is); 
      usfreepollsema(mdthr->cvar_pollsem, mdthr->pollsem_arena);
      if (me && !_PR_IS_NATIVE_THREAD(me))
            _PR_FAST_INTSON(_is);
      _MD_ATOMIC_DECREMENT(&_nspr_irix_pollsem_cnt);
}

static PRStatus new_lock(struct _MDLock *lockp)
{
PRIntn _is;
PRStatus rv = PR_SUCCESS;
ulock_t lock = NULL;
PRCList *qp;
nspr_arena *arena;
usptr_t *irix_arena;
PRThread *me = _MD_GET_ATTACHED_THREAD(); 

      if (me && !_PR_IS_NATIVE_THREAD(me))
            _PR_INTSOFF(_is); 
      _PR_LOCK(arena_list_lock);
      for (qp = arena_list.next; qp != &arena_list; qp = qp->next) {
            arena = ARENA_PTR(qp);
            lock = usnewlock(arena->usarena);
            if (lock != NULL) {
                  lockp->lock = lock;
                  lockp->arena = arena->usarena;
                  break;
            }
      }
      if (lock == NULL) {
            /*
             * If no space left in the arena allocate a new one.
             */
            if (errno == ENOMEM) {
                  arena = PR_NEWZAP(nspr_arena);
                  if (arena != NULL) {
                        irix_arena = alloc_new_arena();
                        if (irix_arena) {
                              PR_APPEND_LINK(&arena->links, &arena_list);
                              _nspr_irix_arena_cnt++;
                              arena->usarena = irix_arena;
                              lock = usnewlock(irix_arena);
                              if (lock != NULL) {
                                    lockp->lock = lock;
                                    lockp->arena = arena->usarena;
                              } else
                                    rv = PR_FAILURE;
                        } else {
                              PR_DELETE(arena);
                              rv = PR_FAILURE;
                        }

                  } else
                        rv = PR_FAILURE;
            } else
                  rv = PR_FAILURE;
      }
      _PR_UNLOCK(arena_list_lock);
      if (me && !_PR_IS_NATIVE_THREAD(me))
            _PR_FAST_INTSON(_is);
      if (rv == PR_SUCCESS)
            _MD_ATOMIC_INCREMENT(&_nspr_irix_lock_cnt);
      return rv;
}

static void free_lock(struct _MDLock *lockp)
{
PRIntn _is;
PRThread *me = _MD_GET_ATTACHED_THREAD(); 

      if (me && !_PR_IS_NATIVE_THREAD(me))
            _PR_INTSOFF(_is); 
      usfreelock(lockp->lock, lockp->arena);
      if (me && !_PR_IS_NATIVE_THREAD(me))
            _PR_FAST_INTSON(_is);
      _MD_ATOMIC_DECREMENT(&_nspr_irix_lock_cnt);
}

void _MD_FREE_LOCK(struct _MDLock *lockp)
{
      PRIntn _is;
      PRThread *me = _MD_GET_ATTACHED_THREAD(); 

      if (me && !_PR_IS_NATIVE_THREAD(me))
            _PR_INTSOFF(_is); 
      free_lock(lockp);
      if (me && !_PR_IS_NATIVE_THREAD(me))
            _PR_FAST_INTSON(_is);
}

/*
 * _MD_get_attached_thread
 *          Return the thread pointer of the current thread if it is attached.
 *
 *          This function is needed for Irix because the thread-local-storage is
 *          implemented by mmapin'g a page with the MAP_LOCAL flag. This causes the
 *          sproc-private page to inherit contents of the page of the caller of sproc().
 */
PRThread *_MD_get_attached_thread(void)
{

      if (_MD_GET_SPROC_PID() == get_pid())
            return _MD_THIS_THREAD();
      else
            return 0;
}

/*
 * _MD_get_current_thread
 *          Return the thread pointer of the current thread (attaching it if
 *          necessary)
 */
PRThread *_MD_get_current_thread(void)
{
PRThread *me;

      me = _MD_GET_ATTACHED_THREAD();
    if (NULL == me) {
        me = _PRI_AttachThread(
            PR_USER_THREAD, PR_PRIORITY_NORMAL, NULL, 0);
    }
    PR_ASSERT(me != NULL);
      return(me);
}

/*
 * irix_detach_sproc
 *          auto-detach a sproc when it exits
 */
void irix_detach_sproc(void)
{
PRThread *me;

      me = _MD_GET_ATTACHED_THREAD();
      if ((me != NULL) && (me->flags & _PR_ATTACHED)) {
            _PRI_DetachThread();
      }
}


PRStatus _MD_NEW_LOCK(struct _MDLock *lockp)
{
    PRStatus rv;
    PRIntn is;
    PRThread *me = _MD_GET_ATTACHED_THREAD();   

      if (me && !_PR_IS_NATIVE_THREAD(me))
            _PR_INTSOFF(is);
      rv = new_lock(lockp);
      if (me && !_PR_IS_NATIVE_THREAD(me))
            _PR_FAST_INTSON(is);
      return rv;
}

static void
sigchld_handler(int sig)
{
    pid_t pid;
    int status;

    /*
     * If an sproc exited abnormally send a SIGKILL signal to all the
     * sprocs in the process to terminate the application
     */
    while ((pid = waitpid(0, &status, WNOHANG)) > 0) {
        if (WIFSIGNALED(status) && ((WTERMSIG(status) == SIGSEGV) ||
            (WTERMSIG(status) == SIGBUS) ||
            (WTERMSIG(status) == SIGABRT) ||
            (WTERMSIG(status) == SIGILL))) {

                        prctl(PR_SETEXITSIG, SIGKILL);
                        _exit(status);
                  }
    }
}

static void save_context_and_block(int sig)
{
PRThread *me = _PR_MD_CURRENT_THREAD();
_PRCPU *cpu = _PR_MD_CURRENT_CPU();

      /*
       * save context
       */
      (void) setjmp(me->md.jb);
      /*
       * unblock the suspending thread
       */
      if (me->cpu) {
            /*
             * I am a cpu thread, not a user-created GLOBAL thread
             */
            unblockproc(cpu->md.suspending_id); 
      } else {
            unblockproc(me->md.suspending_id);  
      }
      /*
       * now, block current thread
       */
      blockproc(getpid());
}

/*
** The irix kernel has a bug in it which causes async connect's which are
** interrupted by a signal to fail terribly (EADDRINUSE is returned). 
** We work around the bug by blocking signals during the async connect
** attempt.
*/
PRInt32 _MD_irix_connect(
    PRInt32 osfd, const PRNetAddr *addr, PRInt32 addrlen, PRIntervalTime timeout)
{
    PRInt32 rv;
    sigset_t oldset;

    sigprocmask(SIG_BLOCK, &ints_off, &oldset);
    rv = connect(osfd, addr, addrlen);
    sigprocmask(SIG_SETMASK, &oldset, 0);

    return(rv);
}

#include "prprf.h"

/********************************************************************/
/********************************************************************/
/*************** Various thread like things for IRIX ****************/
/********************************************************************/
/********************************************************************/

void *_MD_GetSP(PRThread *t)
{
    PRThread *me = _PR_MD_CURRENT_THREAD();
    void *sp;

    if (me == t)
        (void) setjmp(t->md.jb);

    sp = (void *)(t->md.jb[JB_SP]);
    PR_ASSERT((sp >= (void *) t->stack->stackBottom) &&
        (sp <= (void *) (t->stack->stackBottom + t->stack->stackSize)));
    return(sp);
}

void _MD_InitLocks()
{
    char buf[200];
    char *init_users, *init_size;

    PR_snprintf(buf, sizeof(buf), "/dev/zero");

    if (init_users = getenv("_NSPR_IRIX_INITUSERS"))
        _irix_initusers = atoi(init_users);

    if (init_size = getenv("_NSPR_IRIX_INITSIZE"))
        _irix_initsize = atoi(init_size);

    usconfig(CONF_INITUSERS, _irix_initusers);
    usconfig(CONF_INITSIZE, _irix_initsize);
    usconfig(CONF_AUTOGROW, 1);
    usconfig(CONF_AUTORESV, 1);
      if (usconfig(CONF_ARENATYPE, US_SHAREDONLY) < 0) {
            perror("PR_Init: unable to config mutex arena");
            exit(-1);
      }

    _pr_usArena = usinit(buf);
    if (!_pr_usArena) {
        fprintf(stderr,
            "PR_Init: Error - unable to create lock/monitor arena\n");
        exit(-1);
    }
    _pr_heapLock = usnewlock(_pr_usArena);
      _nspr_irix_lock_cnt++;

    arena_list_lock = usnewlock(_pr_usArena);
      _nspr_irix_lock_cnt++;

    sproc_list_lock = usnewlock(_pr_usArena);
      _nspr_irix_lock_cnt++;

      _pr_irix_exit_sem = usnewsema(_pr_usArena, 0);
      _nspr_irix_sem_cnt = 1;

      first_arena.usarena = _pr_usArena;
      PR_INIT_CLIST(&first_arena.links);
      PR_APPEND_LINK(&first_arena.links, &arena_list);
}

/* _PR_IRIX_CHILD_PROCESS is a private API for Server group */
void _PR_IRIX_CHILD_PROCESS()
{
extern PRUint32 _pr_global_threads;

    PR_ASSERT(_PR_MD_CURRENT_CPU() == _pr_primordialCPU);
    PR_ASSERT(_pr_numCPU == 1);
    PR_ASSERT(_pr_global_threads == 0);
    /*
     * save the new pid
     */
    _pr_primordialCPU->md.id = getpid();
      _MD_SET_SPROC_PID(getpid());  
}

static PRStatus pr_cvar_wait_sem(PRThread *thread, PRIntervalTime timeout)
{
    int rv;

#ifdef _PR_USE_POLL
      struct pollfd pfd;
      int msecs;

      if (timeout == PR_INTERVAL_NO_TIMEOUT)
            msecs = -1;
      else
            msecs  = PR_IntervalToMilliseconds(timeout);
#else
    struct timeval tv, *tvp;
    fd_set rd;

      if(timeout == PR_INTERVAL_NO_TIMEOUT)
            tvp = NULL;
      else {
            tv.tv_sec = PR_IntervalToSeconds(timeout);
            tv.tv_usec = PR_IntervalToMicroseconds(
            timeout - PR_SecondsToInterval(tv.tv_sec));
            tvp = &tv;
      }
      FD_ZERO(&rd);
      FD_SET(thread->md.cvar_pollsemfd, &rd);
#endif

    /*
     * call uspsema only if a previous select call on this semaphore
     * did not timeout
     */
    if (!thread->md.cvar_pollsem_select) {
        rv = _PR_WAIT_SEM(thread->md.cvar_pollsem);
            PR_ASSERT(rv >= 0);
      } else
        rv = 0;
again:
    if(!rv) {
#ifdef _PR_USE_POLL
            pfd.events = POLLIN;
            pfd.fd = thread->md.cvar_pollsemfd;
            rv = _MD_POLL(&pfd, 1, msecs);
#else
            rv = _MD_SELECT(thread->md.cvar_pollsemfd + 1, &rd, NULL,NULL,tvp);
#endif
        if ((rv == -1) && (errno == EINTR)) {
                  rv = 0;
                  goto again;
            }
            PR_ASSERT(rv >= 0);
      }

    if (rv > 0) {
        /*
         * acquired the semaphore, call uspsema next time
         */
        thread->md.cvar_pollsem_select = 0;
        return PR_SUCCESS;
    } else {
        /*
         * select timed out; must call select, not uspsema, when trying
         * to acquire the semaphore the next time
         */
        thread->md.cvar_pollsem_select = 1;
        return PR_FAILURE;
    }
}

PRStatus _MD_wait(PRThread *thread, PRIntervalTime ticks)
{
    if ( thread->flags & _PR_GLOBAL_SCOPE ) {
      _MD_CHECK_FOR_EXIT();
        if (pr_cvar_wait_sem(thread, ticks) == PR_FAILURE) {
          _MD_CHECK_FOR_EXIT();
            /*
             * wait timed out
             */
            _PR_THREAD_LOCK(thread);
            if (thread->wait.cvar) {
                /*
                 * The thread will remove itself from the waitQ
                 * of the cvar in _PR_WaitCondVar
                 */
                thread->wait.cvar = NULL;
                thread->state =  _PR_RUNNING;
                _PR_THREAD_UNLOCK(thread);
            }  else {
                _PR_THREAD_UNLOCK(thread);
                /*
             * This thread was woken up by a notifying thread
             * at the same time as a timeout; so, consume the
             * extra post operation on the semaphore
             */
              _MD_CHECK_FOR_EXIT();
            pr_cvar_wait_sem(thread, PR_INTERVAL_NO_TIMEOUT);
            }
          _MD_CHECK_FOR_EXIT();
        }
    } else {
        _PR_MD_SWITCH_CONTEXT(thread);
    }
    return PR_SUCCESS;
}

PRStatus _MD_WakeupWaiter(PRThread *thread)
{
    PRThread *me = _PR_MD_CURRENT_THREAD();
    PRIntn is;

      PR_ASSERT(_pr_md_idle_cpus >= 0);
    if (thread == NULL) {
            if (_pr_md_idle_cpus)
            _MD_Wakeup_CPUs();
    } else if (!_PR_IS_NATIVE_THREAD(thread)) {
            if (_pr_md_idle_cpus)
                  _MD_Wakeup_CPUs();
    } else {
            PR_ASSERT(_PR_IS_NATIVE_THREAD(thread));
            if (!_PR_IS_NATIVE_THREAD(me))
                  _PR_INTSOFF(is);
            _MD_CVAR_POST_SEM(thread);
            if (!_PR_IS_NATIVE_THREAD(me))
                  _PR_FAST_INTSON(is);
    } 
    return PR_SUCCESS;
}

void create_sproc (void (*entry) (void *, size_t), unsigned inh,
                              void *arg, caddr_t sp, size_t len, int *pid)
{
sproc_params sparams;
char data;
int rv;
PRThread *me = _PR_MD_CURRENT_THREAD();

      if (!_PR_IS_NATIVE_THREAD(me) && (_PR_MD_CURRENT_CPU()->id == 0)) {
            *pid = sprocsp(entry,         /* startup func         */
                                    inh,        /* attribute flags      */
                                    arg,        /* thread param         */
                                    sp,         /* stack address  */
                                    len);       /* stack size           */
      } else {
            sparams.sd.entry = entry;
            sparams.sd.inh = inh;
            sparams.sd.arg = arg;
            sparams.sd.sp = sp;
            sparams.sd.len = len;
            sparams.sd.pid = pid;
            sparams.sd.creator_pid = getpid();
            _PR_LOCK(sproc_list_lock);
            PR_APPEND_LINK(&sparams.links, &sproc_list);
            rv = write(_pr_irix_primoridal_cpu_fd[1], &data, 1);
            PR_ASSERT(rv == 1);
            _PR_UNLOCK(sproc_list_lock);
            blockproc(getpid());
      }
}

/*
 * _PR_MD_WAKEUP_PRIMORDIAL_CPU
 *
 *          wakeup cpu 0
 */

void _PR_MD_WAKEUP_PRIMORDIAL_CPU()
{
char data = '0';
int rv;

      rv = write(_pr_irix_primoridal_cpu_fd[1], &data, 1);
      PR_ASSERT(rv == 1);
}

/*
 * _PR_MD_primordial_cpu
 *
 *          process events that need to executed by the primordial cpu on each
 *          iteration through the idle loop
 */

void _PR_MD_primordial_cpu()
{
PRCList *qp;
sproc_params *sp;
int pid;

      _PR_LOCK(sproc_list_lock);
      while ((qp = sproc_list.next) != &sproc_list) {
            sp = SPROC_PARAMS_PTR(qp);
            PR_REMOVE_LINK(&sp->links);
            pid = sp->sd.creator_pid;
            (*(sp->sd.pid)) = sprocsp(sp->sd.entry,         /* startup func    */
                                          sp->sd.inh,             /* attribute flags     */
                                          sp->sd.arg,             /* thread param     */
                                          sp->sd.sp,              /* stack address    */
                                          sp->sd.len);            /* stack size     */
            unblockproc(pid);
      }
      _PR_UNLOCK(sproc_list_lock);
}

PRStatus _MD_CreateThread(PRThread *thread, 
void (*start)(void *), 
PRThreadPriority priority, 
PRThreadScope scope, 
PRThreadState state, 
PRUint32 stackSize)
{
    typedef void (*SprocEntry) (void *, size_t);
    SprocEntry spentry = (SprocEntry)start;
    PRIntn is;
      PRThread *me = _PR_MD_CURRENT_THREAD();   
      PRInt32 pid;
      PRStatus rv;

      if (!_PR_IS_NATIVE_THREAD(me))
            _PR_INTSOFF(is);
    thread->md.cvar_pollsem_select = 0;
    thread->flags |= _PR_GLOBAL_SCOPE;

      thread->md.cvar_pollsemfd = -1;
      if (new_poll_sem(&thread->md,0) == PR_FAILURE) {
            if (!_PR_IS_NATIVE_THREAD(me))
                  _PR_FAST_INTSON(is);
            return PR_FAILURE;
      }
      thread->md.cvar_pollsemfd =
            _PR_OPEN_POLL_SEM(thread->md.cvar_pollsem);
      if ((thread->md.cvar_pollsemfd < 0)) {
            free_poll_sem(&thread->md);
            if (!_PR_IS_NATIVE_THREAD(me))
                  _PR_FAST_INTSON(is);
            return PR_FAILURE;
      }

    create_sproc(spentry,            /* startup func    */
                  PR_SALL,            /* attribute flags     */
                  (void *)thread,     /* thread param     */
                  NULL,               /* stack address    */
                  stackSize, &pid);         /* stack size     */
    if (pid > 0) {
        _MD_ATOMIC_INCREMENT(&_pr_md_irix_sprocs_created);
        _MD_ATOMIC_INCREMENT(&_pr_md_irix_sprocs);
            rv = PR_SUCCESS;
            if (!_PR_IS_NATIVE_THREAD(me))
                  _PR_FAST_INTSON(is);
        return rv;
    } else {
        close(thread->md.cvar_pollsemfd);
        thread->md.cvar_pollsemfd = -1;
            free_poll_sem(&thread->md);
        thread->md.cvar_pollsem = NULL;
        _MD_ATOMIC_INCREMENT(&_pr_md_irix_sprocs_failed);
            if (!_PR_IS_NATIVE_THREAD(me))
                  _PR_FAST_INTSON(is);
        return PR_FAILURE;
    }
}

void _MD_CleanThread(PRThread *thread)
{
    if (thread->flags & _PR_GLOBAL_SCOPE) {
        close(thread->md.cvar_pollsemfd);
        thread->md.cvar_pollsemfd = -1;
            free_poll_sem(&thread->md);
        thread->md.cvar_pollsem = NULL;
    }
}

void _MD_SetPriority(_MDThread *thread, PRThreadPriority newPri)
{
    return;
}

extern void _MD_unix_terminate_waitpid_daemon(void);

void
_MD_CleanupBeforeExit(void)
{
    extern PRInt32    _pr_cpus_exit;

    _MD_unix_terminate_waitpid_daemon();

      _pr_irix_exit_now = 1;
    if (_pr_numCPU > 1) {
        /*
         * Set a global flag, and wakeup all cpus which will notice the flag
         * and exit.
         */
        _pr_cpus_exit = getpid();
        _MD_Wakeup_CPUs();
        while(_pr_numCPU > 1) {
            _PR_WAIT_SEM(_pr_irix_exit_sem);
            _pr_numCPU--;
        }
    }
    /*
     * cause global threads on the recycle list to exit
     */
     _PR_DEADQ_LOCK;
     if (_PR_NUM_DEADNATIVE != 0) {
      PRThread *thread;
      PRCList *ptr;

        ptr = _PR_DEADNATIVEQ.next;
        while( ptr != &_PR_DEADNATIVEQ ) {
            thread = _PR_THREAD_PTR(ptr);
            _MD_CVAR_POST_SEM(thread);
                ptr = ptr->next;
        } 
     }
     _PR_DEADQ_UNLOCK;
     while(_PR_NUM_DEADNATIVE > 1) {
      _PR_WAIT_SEM(_pr_irix_exit_sem);
      _PR_DEC_DEADNATIVE;
     }
}

#ifdef _PR_HAVE_SGI_PRDA_PROCMASK
extern void __sgi_prda_procmask(int);
#endif

PRStatus
_MD_InitAttachedThread(PRThread *thread, PRBool wakeup_parent)
{
      PRStatus rv = PR_SUCCESS;

    if (thread->flags & _PR_GLOBAL_SCOPE) {
            if (new_poll_sem(&thread->md,0) == PR_FAILURE) {
                  return PR_FAILURE;
            }
            thread->md.cvar_pollsemfd =
                  _PR_OPEN_POLL_SEM(thread->md.cvar_pollsem);
            if ((thread->md.cvar_pollsemfd < 0)) {
                  free_poll_sem(&thread->md);
                  return PR_FAILURE;
            }
            if (_MD_InitThread(thread, PR_FALSE) == PR_FAILURE) {
                  close(thread->md.cvar_pollsemfd);
                  thread->md.cvar_pollsemfd = -1;
                  free_poll_sem(&thread->md);
                  thread->md.cvar_pollsem = NULL;
                  return PR_FAILURE;
            }
    }
      return rv;
}

PRStatus
_MD_InitThread(PRThread *thread, PRBool wakeup_parent)
{
    struct sigaction sigact;
      PRStatus rv = PR_SUCCESS;

    if (thread->flags & _PR_GLOBAL_SCOPE) {
            thread->md.id = getpid();
        setblockproccnt(thread->md.id, 0);
            _MD_SET_SPROC_PID(getpid());  
#ifdef _PR_HAVE_SGI_PRDA_PROCMASK
            /*
             * enable user-level processing of sigprocmask(); this is an
             * undocumented feature available in Irix 6.2, 6.3, 6.4 and 6.5
             */
            __sgi_prda_procmask(USER_LEVEL);
#endif
            /*
             * set up SIGUSR1 handler; this is used to save state
             */
            sigact.sa_handler = save_context_and_block;
            sigact.sa_flags = SA_RESTART;
            /*
             * Must mask clock interrupts
             */
            sigact.sa_mask = timer_set;
            sigaction(SIGUSR1, &sigact, 0);


            /*
             * PR_SETABORTSIG is a new command implemented in a patch to
             * Irix 6.2, 6.3 and 6.4. This causes a signal to be sent to all
             * sprocs in the process when one of them terminates abnormally
             *
             */
            if (prctl(PR_SETABORTSIG, SIGKILL) < 0) {
                  /*
                   *  if (errno == EINVAL)
                   *
                   *    PR_SETABORTSIG not supported under this OS.
                   *    You may want to get a recent kernel rollup patch that
                   *    supports this feature.
                   */
            }
            /*
             * SIGCLD handler for detecting abormally-terminating
             * sprocs and for reaping sprocs
             */
            sigact.sa_handler = sigchld_handler;
            sigact.sa_flags = SA_RESTART;
            sigact.sa_mask = ints_off;
            sigaction(SIGCLD, &sigact, NULL);
    }
      return rv;
}

/*
 * PR_Cleanup should be executed on the primordial sproc; migrate the thread
 * to the primordial cpu
 */

void _PR_MD_PRE_CLEANUP(PRThread *me)
{
PRIntn is;
_PRCPU *cpu = _pr_primordialCPU;

      PR_ASSERT(cpu);

      me->flags |= _PR_BOUND_THREAD;      

      if (me->cpu->id != 0) {
            _PR_INTSOFF(is);
            _PR_RUNQ_LOCK(cpu);
            me->cpu = cpu;
            me->state = _PR_RUNNABLE;
            _PR_ADD_RUNQ(me, cpu, me->priority);
            _PR_RUNQ_UNLOCK(cpu);
            _MD_Wakeup_CPUs();

            _PR_MD_SWITCH_CONTEXT(me);

            _PR_FAST_INTSON(is);
            PR_ASSERT(me->cpu->id == 0);
      }
}

/*
 * process exiting
 */
PR_EXTERN(void ) _MD_exit(PRIntn status)
{
PRThread *me = _PR_MD_CURRENT_THREAD();

      /*
       * the exit code of the process is the exit code of the primordial
       * sproc
       */
      if (!_PR_IS_NATIVE_THREAD(me) && (_PR_MD_CURRENT_CPU()->id == 0)) {
            /*
             * primordial sproc case: call _exit directly
             * Cause SIGKILL to be sent to other sprocs
             */
            prctl(PR_SETEXITSIG, SIGKILL);
            _exit(status);
      } else {
            int rv;
            char data;
            sigset_t set;

            /*
             * non-primordial sproc case: cause the primordial sproc, cpu 0,
             * to wakeup and call _exit
             */
            _pr_irix_process_exit = 1;
            _pr_irix_process_exit_code = status;
            rv = write(_pr_irix_primoridal_cpu_fd[1], &data, 1);
            PR_ASSERT(rv == 1);
            /*
             * block all signals and wait for SIGKILL to terminate this sproc
             */
            sigfillset(&set);
            sigsuspend(&set);
            /*
             * this code doesn't (shouldn't) execute
             */
            prctl(PR_SETEXITSIG, SIGKILL);
            _exit(status);
      }
}

/*
 * Override the exit() function in libc to cause the process to exit
 * when the primodial/main nspr thread calls exit. Calls to exit by any
 * other thread simply result in a call to the exit function in libc.
 * The exit code of the process is the exit code of the primordial
 * sproc.
 */

void exit(int status)
{
PRThread *me, *thr;
PRCList *qp;

      if (!_pr_initialized)  {
            if (!libc_exit) {

                  if (!libc_handle)
                        libc_handle = dlopen("libc.so",RTLD_NOW);
                  if (libc_handle)
                        libc_exit = (void (*)(int)) dlsym(libc_handle, "exit");
            }
            if (libc_exit)
                  (*libc_exit)(status);
            else
                  _exit(status);
      }

      me = _PR_MD_CURRENT_THREAD();

      if (me == NULL)         /* detached thread */
            (*libc_exit)(status);

      PR_ASSERT(_PR_IS_NATIVE_THREAD(me) ||
                                    (_PR_MD_CURRENT_CPU())->id == me->cpu->id);

      if (me->flags & _PR_PRIMORDIAL) {

            me->flags |= _PR_BOUND_THREAD;      

            PR_ASSERT((_PR_MD_CURRENT_CPU())->id == me->cpu->id);
            if (me->cpu->id != 0) {
                  _PRCPU *cpu = _pr_primordialCPU;
                  PRIntn is;

                  _PR_INTSOFF(is);
                  _PR_RUNQ_LOCK(cpu);
                  me->cpu = cpu;
                  me->state = _PR_RUNNABLE;
                  _PR_ADD_RUNQ(me, cpu, me->priority);
                  _PR_RUNQ_UNLOCK(cpu);
                  _MD_Wakeup_CPUs();

                  _PR_MD_SWITCH_CONTEXT(me);

                  _PR_FAST_INTSON(is);
            }

            PR_ASSERT((_PR_MD_CURRENT_CPU())->id == 0);

            if (prctl(PR_GETNSHARE) > 1) {
#define SPROC_EXIT_WAIT_TIME 5
                  int sleep_cnt = SPROC_EXIT_WAIT_TIME;

                  /*
                   * sprocs still running; caue cpus and recycled global threads
                   * to exit
                   */
                  _pr_irix_exit_now = 1;
                  if (_pr_numCPU > 1) {
                        _MD_Wakeup_CPUs();
                  }
                   _PR_DEADQ_LOCK;
                   if (_PR_NUM_DEADNATIVE != 0) {
                        PRThread *thread;
                        PRCList *ptr;

                        ptr = _PR_DEADNATIVEQ.next;
                        while( ptr != &_PR_DEADNATIVEQ ) {
                              thread = _PR_THREAD_PTR(ptr);
                              _MD_CVAR_POST_SEM(thread);
                              ptr = ptr->next;
                        } 
                   }

                  while (sleep_cnt-- > 0) {
                        if (waitpid(0, NULL, WNOHANG) >= 0) 
                              sleep(1);
                        else
                              break;
                  }
                  prctl(PR_SETEXITSIG, SIGKILL);
            }
            (*libc_exit)(status);
      } else {
            /*
             * non-primordial thread; simply call exit in libc.
             */
            (*libc_exit)(status);
      }
}


void
_MD_InitRunningCPU(_PRCPU *cpu)
{
    extern int _pr_md_pipefd[2];

    _MD_unix_init_running_cpu(cpu);
    cpu->md.id = getpid();
      _MD_SET_SPROC_PID(getpid());  
      if (_pr_md_pipefd[0] >= 0) {
      _PR_IOQ_MAX_OSFD(cpu) = _pr_md_pipefd[0];
#ifndef _PR_USE_POLL
      FD_SET(_pr_md_pipefd[0], &_PR_FD_READ_SET(cpu));
#endif
      }
}

void
_MD_ExitThread(PRThread *thread)
{
    if (thread->flags & _PR_GLOBAL_SCOPE) {
        _MD_ATOMIC_DECREMENT(&_pr_md_irix_sprocs);
        _MD_CLEAN_THREAD(thread);
        _MD_SET_CURRENT_THREAD(NULL);
    }
}

void
_MD_SuspendCPU(_PRCPU *cpu)
{
    PRInt32 rv;

      cpu->md.suspending_id = getpid();
      rv = kill(cpu->md.id, SIGUSR1);
      PR_ASSERT(rv == 0);
      /*
       * now, block the current thread/cpu until woken up by the suspended
       * thread from it's SIGUSR1 signal handler
       */
      blockproc(getpid());

}

void
_MD_ResumeCPU(_PRCPU *cpu)
{
    unblockproc(cpu->md.id);
}

#if 0
/*
 * save the register context of a suspended sproc
 */
void get_context(PRThread *thr)
{
    int len, fd;
    char pidstr[24];
    char path[24];

    /*
     * open the file corresponding to this process in procfs
     */
    sprintf(path,"/proc/%s","00000");
    len = strlen(path);
    sprintf(pidstr,"%d",thr->md.id);
    len -= strlen(pidstr);
    sprintf(path + len,"%s",pidstr);
    fd = open(path,O_RDONLY);
    if (fd >= 0) {
        (void) ioctl(fd, PIOCGREG, thr->md.gregs);
        close(fd);
    }
    return;
}
#endif      /* 0 */

void
_MD_SuspendThread(PRThread *thread)
{
    PRInt32 rv;

    PR_ASSERT((thread->flags & _PR_GLOBAL_SCOPE) &&
        _PR_IS_GCABLE_THREAD(thread));

      thread->md.suspending_id = getpid();
      rv = kill(thread->md.id, SIGUSR1);
      PR_ASSERT(rv == 0);
      /*
       * now, block the current thread/cpu until woken up by the suspended
       * thread from it's SIGUSR1 signal handler
       */
      blockproc(getpid());
}

void
_MD_ResumeThread(PRThread *thread)
{
    PR_ASSERT((thread->flags & _PR_GLOBAL_SCOPE) &&
        _PR_IS_GCABLE_THREAD(thread));
    (void)unblockproc(thread->md.id);
}

/*
 * return the set of processors available for scheduling procs in the
 * "mask" argument
 */
PRInt32 _MD_GetThreadAffinityMask(PRThread *unused, PRUint32 *mask)
{
    PRInt32 nprocs, rv;
    struct pda_stat *pstat;
#define MAX_PROCESSORS    32

    nprocs = sysmp(MP_NPROCS);
    if (nprocs < 0)
        return(-1);
    pstat = (struct pda_stat*)PR_MALLOC(sizeof(struct pda_stat) * nprocs);
    if (pstat == NULL)
        return(-1);
    rv = sysmp(MP_STAT, pstat);
    if (rv < 0) {
        PR_DELETE(pstat);
        return(-1);
    }
    /*
     * look at the first 32 cpus
     */
    nprocs = (nprocs > MAX_PROCESSORS) ? MAX_PROCESSORS : nprocs;
    *mask = 0;
    while (nprocs) {
        if ((pstat->p_flags & PDAF_ENABLED) &&
            !(pstat->p_flags & PDAF_ISOLATED)) {
            *mask |= (1 << pstat->p_cpuid);
        }
        nprocs--;
        pstat++;
    }
    return 0;
}

static char *_thr_state[] = {
    "UNBORN",
    "RUNNABLE",
    "RUNNING",
    "LOCK_WAIT",
    "COND_WAIT",
    "JOIN_WAIT",
    "IO_WAIT",
    "SUSPENDED",
    "DEAD"
};

void _PR_List_Threads()
{
    PRThread *thr;
    void *handle;
    struct _PRCPU *cpu;
    PRCList *qp;
    int len, fd;
    char pidstr[24];
    char path[24];
    prpsinfo_t pinfo;


    printf("\n%s %-s\n"," ","LOCAL Threads");
    printf("%s %-s\n"," ","----- -------");
    printf("%s %-14s %-10s %-12s %-3s %-10s %-10s %-12s\n\n"," ",
        "Thread", "State", "Wait-Handle",
        "Cpu","Stk-Base","Stk-Sz","SP");
    for (qp = _PR_ACTIVE_LOCAL_THREADQ().next;
        qp != &_PR_ACTIVE_LOCAL_THREADQ(); qp = qp->next) {
        thr = _PR_ACTIVE_THREAD_PTR(qp);
        printf("%s 0x%-12x %-10s "," ",thr,_thr_state[thr->state]);
        if (thr->state == _PR_LOCK_WAIT)
            handle = thr->wait.lock;
        else if (thr->state == _PR_COND_WAIT)
            handle = thr->wait.cvar;
        else
            handle = NULL;
        if (handle)
            printf("0x%-10x ",handle);
        else
            printf("%-12s "," ");
        printf("%-3d ",thr->cpu->id);
        printf("0x%-8x ",thr->stack->stackBottom);
        printf("0x%-8x ",thr->stack->stackSize);
        printf("0x%-10x\n",thr->md.jb[JB_SP]);
    }

    printf("\n%s %-s\n"," ","GLOBAL Threads");
    printf("%s %-s\n"," ","------ -------");
    printf("%s %-14s %-6s %-12s %-12s %-12s %-12s\n\n"," ","Thread",
        "Pid","State","Wait-Handle",
        "Stk-Base","Stk-Sz");

    for (qp = _PR_ACTIVE_GLOBAL_THREADQ().next;
        qp != &_PR_ACTIVE_GLOBAL_THREADQ(); qp = qp->next) {
        thr = _PR_ACTIVE_THREAD_PTR(qp);
        if (thr->cpu != NULL)
            continue;        /* it is a cpu thread */
        printf("%s 0x%-12x %-6d "," ",thr,thr->md.id);
        /*
         * check if the sproc is still running
         * first call prctl(PR_GETSHMASK,pid) to check if
         * the process is part of the share group (the pid
         * could have been recycled by the OS)
         */
        if (prctl(PR_GETSHMASK,thr->md.id) < 0) {
            printf("%-12s\n","TERMINATED");
            continue;
        }
        /*
         * Now, check if the sproc terminated and is in zombie
         * state
         */
        sprintf(path,"/proc/pinfo/%s","00000");
        len = strlen(path);
        sprintf(pidstr,"%d",thr->md.id);
        len -= strlen(pidstr);
        sprintf(path + len,"%s",pidstr);
        fd = open(path,O_RDONLY);
        if (fd >= 0) {
            if (ioctl(fd, PIOCPSINFO, &pinfo) < 0)
                printf("%-12s ","TERMINATED");
            else if (pinfo.pr_zomb)
                printf("%-12s ","TERMINATED");
            else
                printf("%-12s ",_thr_state[thr->state]);
            close(fd);
        } else {
            printf("%-12s ","TERMINATED");
        }

        if (thr->state == _PR_LOCK_WAIT)
            handle = thr->wait.lock;
        else if (thr->state == _PR_COND_WAIT)
            handle = thr->wait.cvar;
        else
            handle = NULL;
        if (handle)
            printf("%-12x ",handle);
        else
            printf("%-12s "," ");
        printf("0x%-10x ",thr->stack->stackBottom);
        printf("0x%-10x\n",thr->stack->stackSize);
    }

    printf("\n%s %-s\n"," ","CPUs");
    printf("%s %-s\n"," ","----");
    printf("%s %-14s %-6s %-12s \n\n"," ","Id","Pid","State");


    for (qp = _PR_CPUQ().next; qp != &_PR_CPUQ(); qp = qp->next) {
        cpu = _PR_CPU_PTR(qp);
        printf("%s %-14d %-6d "," ",cpu->id,cpu->md.id);
        /*
         * check if the sproc is still running
         * first call prctl(PR_GETSHMASK,pid) to check if
         * the process is part of the share group (the pid
         * could have been recycled by the OS)
         */
        if (prctl(PR_GETSHMASK,cpu->md.id) < 0) {
            printf("%-12s\n","TERMINATED");
            continue;
        }
        /*
         * Now, check if the sproc terminated and is in zombie
         * state
         */
        sprintf(path,"/proc/pinfo/%s","00000");
        len = strlen(path);
        sprintf(pidstr,"%d",cpu->md.id);
        len -= strlen(pidstr);
        sprintf(path + len,"%s",pidstr);
        fd = open(path,O_RDONLY);
        if (fd >= 0) {
            if (ioctl(fd, PIOCPSINFO, &pinfo) < 0)
                printf("%-12s\n","TERMINATED");
            else if (pinfo.pr_zomb)
                printf("%-12s\n","TERMINATED");
            else
                printf("%-12s\n","RUNNING");
            close(fd);
        } else {
            printf("%-12s\n","TERMINATED");
        }

    }
    fflush(stdout);
}
#endif /* defined(_PR_PTHREADS) */ 

PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np)
{
#if !defined(_PR_PTHREADS)
    if (isCurrent) {
        (void) setjmp(t->md.jb);
    }
    *np = sizeof(t->md.jb) / sizeof(PRWord);
    return (PRWord *) (t->md.jb);
#else
      *np = 0;
      return NULL;
#endif
}

void _MD_EarlyInit(void)
{
#if !defined(_PR_PTHREADS)
    char *eval;
    int fd;
      extern int __ateachexit(void (*func)(void));

    sigemptyset(&ints_off);
    sigaddset(&ints_off, SIGALRM);
    sigaddset(&ints_off, SIGIO);
    sigaddset(&ints_off, SIGCLD);

    if (eval = getenv("_NSPR_TERMINATE_ON_ERROR"))
        _nspr_terminate_on_error = (0 == atoi(eval) == 0) ? PR_FALSE : PR_TRUE;

    fd = open("/dev/zero",O_RDWR , 0);
    if (fd < 0) {
        perror("open /dev/zero failed");
        exit(1);
    }
    /*
     * Set up the sproc private data area.
     * This region exists at the same address, _nspr_sproc_private, for
     * every sproc, but each sproc gets a private copy of the region.
     */
    _nspr_sproc_private = (char*)mmap(0, _pr_pageSize, PROT_READ | PROT_WRITE,
        MAP_PRIVATE| MAP_LOCAL, fd, 0);
    if (_nspr_sproc_private == (void*)-1) {
        perror("mmap /dev/zero failed");
        exit(1);
    }
      _MD_SET_SPROC_PID(getpid());  
    close(fd);
      __ateachexit(irix_detach_sproc);
#endif
    _MD_IrixIntervalInit();
}  /* _MD_EarlyInit */

void _MD_IrixInit(void)
{
#if !defined(_PR_PTHREADS)
    struct sigaction sigact;
    PRThread *me = _PR_MD_CURRENT_THREAD();
      int rv;

#ifdef _PR_HAVE_SGI_PRDA_PROCMASK
      /*
       * enable user-level processing of sigprocmask(); this is an undocumented
       * feature available in Irix 6.2, 6.3, 6.4 and 6.5
       */
      __sgi_prda_procmask(USER_LEVEL);
#endif

      /*
       * set up SIGUSR1 handler; this is used to save state
       * during PR_SuspendAll
       */
      sigact.sa_handler = save_context_and_block;
      sigact.sa_flags = SA_RESTART;
      sigact.sa_mask = ints_off;
      sigaction(SIGUSR1, &sigact, 0);

    /*
     * Change the name of the core file from core to core.pid,
     * This is inherited by the sprocs created by this process
     */
#ifdef PR_COREPID
    prctl(PR_COREPID, 0, 1);
#endif
    /*
     * Irix-specific terminate on error processing
     */
      /*
       * PR_SETABORTSIG is a new command implemented in a patch to
       * Irix 6.2, 6.3 and 6.4. This causes a signal to be sent to all
       * sprocs in the process when one of them terminates abnormally
       *
       */
      if (prctl(PR_SETABORTSIG, SIGKILL) < 0) {
            /*
             *  if (errno == EINVAL)
             *
             *    PR_SETABORTSIG not supported under this OS.
             *    You may want to get a recent kernel rollup patch that
             *    supports this feature.
             *
             */
      }
      /*
       * PR_SETEXITSIG -  send the SIGCLD signal to the parent
       *            sproc when any sproc terminates
       *
       *    This is used to cause the entire application to
       *    terminate when    any sproc terminates abnormally by
       *     receipt of a SIGSEGV, SIGBUS or SIGABRT signal.
       *    If this is not done, the application may seem
       *     "hung" to the user because the other sprocs may be
       *    waiting for resources held by the
       *    abnormally-terminating sproc.
       */
      prctl(PR_SETEXITSIG, 0);

      sigact.sa_handler = sigchld_handler;
      sigact.sa_flags = SA_RESTART;
      sigact.sa_mask = ints_off;
      sigaction(SIGCLD, &sigact, NULL);

    /*
     * setup stack fields for the primordial thread
     */
    me->stack->stackSize = prctl(PR_GETSTACKSIZE);
    me->stack->stackBottom = me->stack->stackTop - me->stack->stackSize;

    rv = pipe(_pr_irix_primoridal_cpu_fd);
    PR_ASSERT(rv == 0);
#ifndef _PR_USE_POLL
    _PR_IOQ_MAX_OSFD(me->cpu) = _pr_irix_primoridal_cpu_fd[0];
    FD_SET(_pr_irix_primoridal_cpu_fd[0], &_PR_FD_READ_SET(me->cpu));
#endif

      libc_handle = dlopen("libc.so",RTLD_NOW);
      PR_ASSERT(libc_handle != NULL);
      libc_exit = (void (*)(int)) dlsym(libc_handle, "exit");
      PR_ASSERT(libc_exit != NULL);
      /* dlclose(libc_handle); */

#endif /* _PR_PTHREADS */

    _PR_UnixInit();
}

/**************************************************************************/
/************** code and such for NSPR 2.0's interval times ***************/
/**************************************************************************/

#define PR_PSEC_PER_SEC 1000000000000ULL  /* 10^12 */

#ifndef SGI_CYCLECNTR_SIZE
#define SGI_CYCLECNTR_SIZE      165     /* Size user needs to use to read CC */
#endif

static PRIntn mmem_fd = -1;
static PRIntn clock_width = 0;
static void *iotimer_addr = NULL;
static PRUint32 pr_clock_mask = 0;
static PRUint32 pr_clock_shift = 0;
static PRIntervalTime pr_ticks = 0;
static PRUint32 pr_clock_granularity = 1;
static PRUint32 pr_previous = 0, pr_residual = 0;
static PRUint32 pr_ticks_per_second = 0;

extern PRIntervalTime _PR_UNIX_GetInterval(void);
extern PRIntervalTime _PR_UNIX_TicksPerSecond(void);

static void _MD_IrixIntervalInit(void)
{
    /*
     * As much as I would like, the service available through this
     * interface on R3000's (aka, IP12) just isn't going to make it.
     * The register is only 24 bits wide, and rolls over at a verocious
     * rate.
     */
    PRUint32 one_tick = 0;
    struct utsname utsinfo;
    uname(&utsinfo);
    if ((strncmp("IP12", utsinfo.machine, 4) != 0)
        && ((mmem_fd = open("/dev/mmem", O_RDONLY)) != -1))
    {
        int poffmask = getpagesize() - 1;
        __psunsigned_t phys_addr, raddr, cycleval;

        phys_addr = syssgi(SGI_QUERY_CYCLECNTR, &cycleval);
        raddr = phys_addr & ~poffmask;
        iotimer_addr = mmap(
            0, poffmask, PROT_READ, MAP_PRIVATE, mmem_fd, (__psint_t)raddr);

        clock_width = syssgi(SGI_CYCLECNTR_SIZE);
        if (clock_width < 0)
        {
            /* 
             * We must be executing on a 6.0 or earlier system, since the
             * SGI_CYCLECNTR_SIZE call is not supported.
             * 
             * The only pre-6.1 platforms with 64-bit counters are
             * IP19 and IP21 (Challenge, PowerChallenge, Onyx).
             */
            if (!strncmp(utsinfo.machine, "IP19", 4) ||
                !strncmp(utsinfo.machine, "IP21", 4))
                clock_width = 64;
            else
                clock_width = 32;
        }

        /*
         * 'cycleval' is picoseconds / increment of the counter.
         * I'm pushing for a tick to be 100 microseconds, 10^(-4).
         * That leaves 10^(-8) left over, or 10^8 / cycleval.
         * Did I do that right?
         */

        one_tick =  100000000UL / cycleval ;  /* 100 microseconds */

        while (0 != one_tick)
        {
            pr_clock_shift += 1;
            one_tick = one_tick >> 1;
            pr_clock_granularity = pr_clock_granularity << 1;
        }
        pr_clock_mask = pr_clock_granularity - 1;  /* to make a mask out of it */
        pr_ticks_per_second = PR_PSEC_PER_SEC
                / ((PRUint64)pr_clock_granularity * (PRUint64)cycleval);
            
        iotimer_addr = (void*)
            ((__psunsigned_t)iotimer_addr + (phys_addr & poffmask));
    }
    else
    {
        pr_ticks_per_second = _PR_UNIX_TicksPerSecond();
    }
}  /* _MD_IrixIntervalInit */

PRIntervalTime _MD_IrixIntervalPerSec(void)
{
    return pr_ticks_per_second;
}

PRIntervalTime _MD_IrixGetInterval(void)
{
    if (mmem_fd != -1)
    {
        if (64 == clock_width)
        {
            PRUint64 temp = *(PRUint64*)iotimer_addr;
            pr_ticks = (PRIntervalTime)(temp >> pr_clock_shift);
        }
        else
        {
            PRIntervalTime ticks = pr_ticks;
            PRUint32 now = *(PRUint32*)iotimer_addr, temp;
            PRUint32 residual = pr_residual, previous = pr_previous;

            temp = now - previous + residual;
            residual = temp & pr_clock_mask;
            ticks += temp >> pr_clock_shift;

            pr_previous = now;
            pr_residual = residual;
            pr_ticks = ticks;
        }
    }
    else
    {
        /*
         * No fast access. Use the time of day clock. This isn't the
         * right answer since this clock can get set back, tick at odd
         * rates, and it's expensive to acqurie.
         */
        pr_ticks = _PR_UNIX_GetInterval();
    }
    return pr_ticks;
}  /* _MD_IrixGetInterval */


Generated by  Doxygen 1.6.0   Back to index