nsprpub/pr/src/md/unix/unixware.c

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/nsprpub/pr/src/md/unix/unixware.c	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,551 @@
     1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include "primpl.h"
    1.10 +
    1.11 +#if !defined (USE_SVR4_THREADS)
    1.12 +
    1.13 +/*
    1.14 +         * using only NSPR threads here
    1.15 + */
    1.16 +
    1.17 +#include <setjmp.h>
    1.18 +
    1.19 +void _MD_EarlyInit(void)
    1.20 +{
    1.21 +}
    1.22 +
    1.23 +PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np)
    1.24 +{
    1.25 +    if (isCurrent) {
    1.26 +	(void) setjmp(CONTEXT(t));
    1.27 +    }
    1.28 +    *np = sizeof(CONTEXT(t)) / sizeof(PRWord);
    1.29 +    return (PRWord *) CONTEXT(t);
    1.30 +}
    1.31 +
    1.32 +#ifdef ALARMS_BREAK_TCP /* I don't think they do */
    1.33 +
    1.34 +PRInt32 _MD_connect(PRInt32 osfd, const PRNetAddr *addr, PRInt32 addrlen,
    1.35 +                        PRIntervalTime timeout)
    1.36 +{
    1.37 +    PRInt32 rv;
    1.38 +
    1.39 +    _MD_BLOCK_CLOCK_INTERRUPTS();
    1.40 +    rv = _connect(osfd,addr,addrlen);
    1.41 +    _MD_UNBLOCK_CLOCK_INTERRUPTS();
    1.42 +}
    1.43 +
    1.44 +PRInt32 _MD_accept(PRInt32 osfd, PRNetAddr *addr, PRInt32 addrlen,
    1.45 +                        PRIntervalTime timeout)
    1.46 +{
    1.47 +    PRInt32 rv;
    1.48 +
    1.49 +    _MD_BLOCK_CLOCK_INTERRUPTS();
    1.50 +    rv = _accept(osfd,addr,addrlen);
    1.51 +    _MD_UNBLOCK_CLOCK_INTERRUPTS();
    1.52 +    return(rv);
    1.53 +}
    1.54 +#endif
    1.55 +
    1.56 +/*
    1.57 + * These are also implemented in pratom.c using NSPR locks.  Any reason
    1.58 + * this might be better or worse?  If you like this better, define
    1.59 + * _PR_HAVE_ATOMIC_OPS in include/md/unixware.h
    1.60 + */
    1.61 +#ifdef _PR_HAVE_ATOMIC_OPS
    1.62 +/* Atomic operations */
    1.63 +#include  <stdio.h>
    1.64 +static FILE *_uw_semf;
    1.65 +
    1.66 +void
    1.67 +_MD_INIT_ATOMIC(void)
    1.68 +{
    1.69 +    /* Sigh.  Sure wish SYSV semaphores weren't such a pain to use */
    1.70 +    if ((_uw_semf = tmpfile()) == NULL)
    1.71 +        PR_ASSERT(0);
    1.72 +
    1.73 +    return;
    1.74 +}
    1.75 +
    1.76 +void
    1.77 +_MD_ATOMIC_INCREMENT(PRInt32 *val)
    1.78 +{
    1.79 +    flockfile(_uw_semf);
    1.80 +    (*val)++;
    1.81 +    unflockfile(_uw_semf);
    1.82 +}
    1.83 +
    1.84 +void
    1.85 +_MD_ATOMIC_ADD(PRInt32 *ptr, PRInt32 val)
    1.86 +{
    1.87 +    flockfile(_uw_semf);
    1.88 +    (*ptr) += val;
    1.89 +    unflockfile(_uw_semf);
    1.90 +}
    1.91 +
    1.92 +void
    1.93 +_MD_ATOMIC_DECREMENT(PRInt32 *val)
    1.94 +{
    1.95 +    flockfile(_uw_semf);
    1.96 +    (*val)--;
    1.97 +    unflockfile(_uw_semf);
    1.98 +}
    1.99 +
   1.100 +void
   1.101 +_MD_ATOMIC_SET(PRInt32 *val, PRInt32 newval)
   1.102 +{
   1.103 +    flockfile(_uw_semf);
   1.104 +    *val = newval;
   1.105 +    unflockfile(_uw_semf);
   1.106 +}
   1.107 +#endif
   1.108 +
   1.109 +void
   1.110 +_MD_SET_PRIORITY(_MDThread *thread, PRUintn newPri)
   1.111 +{
   1.112 +    return;
   1.113 +}
   1.114 +
   1.115 +PRStatus
   1.116 +_MD_InitializeThread(PRThread *thread)
   1.117 +{
   1.118 +	return PR_SUCCESS;
   1.119 +}
   1.120 +
   1.121 +PRStatus
   1.122 +_MD_WAIT(PRThread *thread, PRIntervalTime ticks)
   1.123 +{
   1.124 +    PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE));
   1.125 +    _PR_MD_SWITCH_CONTEXT(thread);
   1.126 +    return PR_SUCCESS;
   1.127 +}
   1.128 +
   1.129 +PRStatus
   1.130 +_MD_WAKEUP_WAITER(PRThread *thread)
   1.131 +{
   1.132 +    if (thread) {
   1.133 +	PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE));
   1.134 +    }
   1.135 +    return PR_SUCCESS;
   1.136 +}
   1.137 +
   1.138 +/* These functions should not be called for Unixware */
   1.139 +void
   1.140 +_MD_YIELD(void)
   1.141 +{
   1.142 +    PR_NOT_REACHED("_MD_YIELD should not be called for Unixware.");
   1.143 +}
   1.144 +
   1.145 +PRStatus
   1.146 +_MD_CREATE_THREAD(
   1.147 +    PRThread *thread,
   1.148 +    void (*start) (void *),
   1.149 +    PRThreadPriority priority,
   1.150 +    PRThreadScope scope,
   1.151 +    PRThreadState state,
   1.152 +    PRUint32 stackSize)
   1.153 +{
   1.154 +    PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for Unixware.");
   1.155 +}
   1.156 +
   1.157 +#else  /* USE_SVR4_THREADS */
   1.158 +
   1.159 +/* NOTE:
   1.160 + * SPARC v9 (Ultras) do have an atomic test-and-set operation.  But
   1.161 + * SPARC v8 doesn't.  We should detect in the init if we are running on
   1.162 + * v8 or v9, and then use assembly where we can.
   1.163 + */
   1.164 +
   1.165 +#include <thread.h>
   1.166 +#include <synch.h>
   1.167 +
   1.168 +static mutex_t _unixware_atomic = DEFAULTMUTEX;
   1.169 +
   1.170 +#define TEST_THEN_ADD(where, inc) \
   1.171 +    if (mutex_lock(&_unixware_atomic) != 0)\
   1.172 +        PR_ASSERT(0);\
   1.173 +    *where += inc;\
   1.174 +    if (mutex_unlock(&_unixware_atomic) != 0)\
   1.175 +        PR_ASSERT(0);
   1.176 +
   1.177 +#define TEST_THEN_SET(where, val) \
   1.178 +    if (mutex_lock(&_unixware_atomic) != 0)\
   1.179 +        PR_ASSERT(0);\
   1.180 +    *where = val;\
   1.181 +    if (mutex_unlock(&_unixware_atomic) != 0)\
   1.182 +        PR_ASSERT(0);
   1.183 +
   1.184 +void
   1.185 +_MD_INIT_ATOMIC(void)
   1.186 +{
   1.187 +}
   1.188 +
   1.189 +void
   1.190 +_MD_ATOMIC_INCREMENT(PRInt32 *val)
   1.191 +{
   1.192 +    TEST_THEN_ADD(val, 1);
   1.193 +}
   1.194 +
   1.195 +void
   1.196 +_MD_ATOMIC_ADD(PRInt32 *ptr, PRInt32 val)
   1.197 +{
   1.198 +    TEST_THEN_ADD(ptr, val);
   1.199 +}
   1.200 +
   1.201 +void
   1.202 +_MD_ATOMIC_DECREMENT(PRInt32 *val)
   1.203 +{
   1.204 +    TEST_THEN_ADD(val, 0xffffffff);
   1.205 +}
   1.206 +
   1.207 +void
   1.208 +_MD_ATOMIC_SET(PRInt32 *val, PRInt32 newval)
   1.209 +{
   1.210 +    TEST_THEN_SET(val, newval);
   1.211 +}
   1.212 +
   1.213 +#include <signal.h>
   1.214 +#include <errno.h>
   1.215 +#include <fcntl.h>
   1.216 +
   1.217 +#include <sys/lwp.h>
   1.218 +#include <sys/procfs.h>
   1.219 +#include <sys/syscall.h>
   1.220 +
   1.221 +
   1.222 +THREAD_KEY_T threadid_key;
   1.223 +THREAD_KEY_T cpuid_key;
   1.224 +THREAD_KEY_T last_thread_key;
   1.225 +static sigset_t set, oldset;
   1.226 +
   1.227 +void _MD_EarlyInit(void)
   1.228 +{
   1.229 +    THR_KEYCREATE(&threadid_key, NULL);
   1.230 +    THR_KEYCREATE(&cpuid_key, NULL);
   1.231 +    THR_KEYCREATE(&last_thread_key, NULL);
   1.232 +    sigemptyset(&set);
   1.233 +    sigaddset(&set, SIGALRM);
   1.234 +}
   1.235 +
   1.236 +PRStatus _MD_CREATE_THREAD(PRThread *thread, 
   1.237 +					void (*start)(void *), 
   1.238 +					PRThreadPriority priority,
   1.239 +					PRThreadScope scope, 
   1.240 +					PRThreadState state, 
   1.241 +					PRUint32 stackSize) 
   1.242 +{
   1.243 +	long flags;
   1.244 +	
   1.245 +    /* mask out SIGALRM for native thread creation */
   1.246 +    thr_sigsetmask(SIG_BLOCK, &set, &oldset); 
   1.247 +
   1.248 +    flags = (state == PR_JOINABLE_THREAD ? THR_SUSPENDED/*|THR_NEW_LWP*/ 
   1.249 +			   : THR_SUSPENDED|THR_DETACHED/*|THR_NEW_LWP*/);
   1.250 +	if (_PR_IS_GCABLE_THREAD(thread) ||
   1.251 +							(scope == PR_GLOBAL_BOUND_THREAD))
   1.252 +		flags |= THR_BOUND;
   1.253 +
   1.254 +    if (thr_create(NULL, thread->stack->stackSize,
   1.255 +                  (void *(*)(void *)) start, (void *) thread, 
   1.256 +				  flags,
   1.257 +                  &thread->md.handle)) {
   1.258 +        thr_sigsetmask(SIG_SETMASK, &oldset, NULL); 
   1.259 +	return PR_FAILURE;
   1.260 +    }
   1.261 +
   1.262 +
   1.263 +    /* When the thread starts running, then the lwpid is set to the right
   1.264 +     * value. Until then we want to mark this as 'uninit' so that
   1.265 +     * its register state is initialized properly for GC */
   1.266 +
   1.267 +    thread->md.lwpid = -1;
   1.268 +    thr_sigsetmask(SIG_SETMASK, &oldset, NULL); 
   1.269 +    _MD_NEW_SEM(&thread->md.waiter_sem, 0);
   1.270 +
   1.271 +	if ((scope == PR_GLOBAL_THREAD) || (scope == PR_GLOBAL_BOUND_THREAD)) {
   1.272 +		thread->flags |= _PR_GLOBAL_SCOPE;
   1.273 +    }
   1.274 +
   1.275 +    /* 
   1.276 +    ** Set the thread priority.  This will also place the thread on 
   1.277 +    ** the runQ.
   1.278 +    **
   1.279 +    ** Force PR_SetThreadPriority to set the priority by
   1.280 +    ** setting thread->priority to 100.
   1.281 +    */
   1.282 +    {
   1.283 +    int pri;
   1.284 +    pri = thread->priority;
   1.285 +    thread->priority = 100;
   1.286 +    PR_SetThreadPriority( thread, pri );
   1.287 +
   1.288 +    PR_LOG(_pr_thread_lm, PR_LOG_MIN, 
   1.289 +            ("(0X%x)[Start]: on to runq at priority %d",
   1.290 +            thread, thread->priority));
   1.291 +    }
   1.292 +
   1.293 +    /* Activate the thread */
   1.294 +    if (thr_continue( thread->md.handle ) ) {
   1.295 +	return PR_FAILURE;
   1.296 +    }
   1.297 +    return PR_SUCCESS;
   1.298 +}
   1.299 +
   1.300 +void _MD_cleanup_thread(PRThread *thread)
   1.301 +{
   1.302 +    thread_t hdl;
   1.303 +    PRMonitor *mon;
   1.304 +
   1.305 +    hdl = thread->md.handle;
   1.306 +
   1.307 +    /* 
   1.308 +    ** First, suspend the thread (unless it's the active one)
   1.309 +    ** Because we suspend it first, we don't have to use LOCK_SCHEDULER to
   1.310 +    ** prevent both of us modifying the thread structure at the same time.
   1.311 +    */
   1.312 +    if ( thread != _PR_MD_CURRENT_THREAD() ) {
   1.313 +        thr_suspend(hdl);
   1.314 +    }
   1.315 +    PR_LOG(_pr_thread_lm, PR_LOG_MIN,
   1.316 +            ("(0X%x)[DestroyThread]\n", thread));
   1.317 +
   1.318 +    _MD_DESTROY_SEM(&thread->md.waiter_sem);
   1.319 +}
   1.320 +
   1.321 +void _MD_SET_PRIORITY(_MDThread *md_thread, PRUintn newPri)
   1.322 +{
   1.323 +	if(thr_setprio((thread_t)md_thread->handle, newPri)) {
   1.324 +		PR_LOG(_pr_thread_lm, PR_LOG_MIN,
   1.325 +		   ("_PR_SetThreadPriority: can't set thread priority\n"));
   1.326 +	}
   1.327 +}
   1.328 +
   1.329 +void _MD_WAIT_CV(
   1.330 +    struct _MDCVar *md_cv, struct _MDLock *md_lock, PRIntervalTime timeout)
   1.331 +{
   1.332 +    struct timespec tt;
   1.333 +    PRUint32 msec;
   1.334 +    int rv;
   1.335 +    PRThread *me = _PR_MD_CURRENT_THREAD();
   1.336 +
   1.337 +    msec = PR_IntervalToMilliseconds(timeout);
   1.338 +
   1.339 +    GETTIME (&tt);
   1.340 +
   1.341 +    tt.tv_sec += msec / PR_MSEC_PER_SEC;
   1.342 +    tt.tv_nsec += (msec % PR_MSEC_PER_SEC) * PR_NSEC_PER_MSEC;
   1.343 +    /* Check for nsec overflow - otherwise we'll get an EINVAL */
   1.344 +    if (tt.tv_nsec >= PR_NSEC_PER_SEC) {
   1.345 +        tt.tv_sec++;
   1.346 +        tt.tv_nsec -= PR_NSEC_PER_SEC;
   1.347 +    }
   1.348 +    me->md.sp = unixware_getsp();
   1.349 +
   1.350 +
   1.351 +    /* XXX Solaris 2.5.x gives back EINTR occasionally for no reason
   1.352 +     * hence ignore EINTR for now */
   1.353 +
   1.354 +    COND_TIMEDWAIT(&md_cv->cv, &md_lock->lock, &tt);
   1.355 +}
   1.356 +
   1.357 +void _MD_lock(struct _MDLock *md_lock)
   1.358 +{
   1.359 +    mutex_lock(&md_lock->lock);
   1.360 +}
   1.361 +
   1.362 +void _MD_unlock(struct _MDLock *md_lock)
   1.363 +{
   1.364 +    mutex_unlock(&((md_lock)->lock));
   1.365 +}
   1.366 +
   1.367 +
   1.368 +PRThread *_pr_current_thread_tls()
   1.369 +{
   1.370 +    PRThread *ret;
   1.371 +
   1.372 +    thr_getspecific(threadid_key, (void **)&ret);
   1.373 +    return ret;
   1.374 +}
   1.375 +
   1.376 +PRStatus
   1.377 +_MD_WAIT(PRThread *thread, PRIntervalTime ticks)
   1.378 +{
   1.379 +        _MD_WAIT_SEM(&thread->md.waiter_sem);
   1.380 +        return PR_SUCCESS;
   1.381 +}
   1.382 +
   1.383 +PRStatus
   1.384 +_MD_WAKEUP_WAITER(PRThread *thread)
   1.385 +{
   1.386 +	if (thread == NULL) {
   1.387 +		return PR_SUCCESS;
   1.388 +	}
   1.389 +	_MD_POST_SEM(&thread->md.waiter_sem);
   1.390 +	return PR_SUCCESS;
   1.391 +}
   1.392 +
   1.393 +_PRCPU *_pr_current_cpu_tls()
   1.394 +{
   1.395 +    _PRCPU *ret;
   1.396 +
   1.397 +    thr_getspecific(cpuid_key, (void **)&ret);
   1.398 +    return ret;
   1.399 +}
   1.400 +
   1.401 +PRThread *_pr_last_thread_tls()
   1.402 +{
   1.403 +    PRThread *ret;
   1.404 +
   1.405 +    thr_getspecific(last_thread_key, (void **)&ret);
   1.406 +    return ret;
   1.407 +}
   1.408 +
   1.409 +_MDLock _pr_ioq_lock;
   1.410 +
   1.411 +void _MD_INIT_IO (void)
   1.412 +{
   1.413 +    _MD_NEW_LOCK(&_pr_ioq_lock);
   1.414 +}
   1.415 +
   1.416 +PRStatus _MD_InitializeThread(PRThread *thread)
   1.417 +{
   1.418 +    if (!_PR_IS_NATIVE_THREAD(thread))
   1.419 +        return;
   1.420 +		/* prime the sp; substract 4 so we don't hit the assert that
   1.421 +		 * curr sp > base_stack
   1.422 +		 */
   1.423 +    thread->md.sp = (uint_t) thread->stack->allocBase - sizeof(long);
   1.424 +    thread->md.lwpid = _lwp_self();
   1.425 +    thread->md.handle = THR_SELF();
   1.426 +
   1.427 +	/* all threads on Solaris are global threads from NSPR's perspective
   1.428 +	 * since all of them are mapped to Solaris threads.
   1.429 +	 */
   1.430 +    thread->flags |= _PR_GLOBAL_SCOPE;
   1.431 +
   1.432 + 	/* For primordial/attached thread, we don't create an underlying native thread.
   1.433 + 	 * So, _MD_CREATE_THREAD() does not get called.  We need to do initialization
   1.434 + 	 * like allocating thread's synchronization variables and set the underlying
   1.435 + 	 * native thread's priority.
   1.436 + 	 */
   1.437 +	if (thread->flags & (_PR_PRIMORDIAL | _PR_ATTACHED)) {
   1.438 +	    _MD_NEW_SEM(&thread->md.waiter_sem, 0);
   1.439 +	    _MD_SET_PRIORITY(&(thread->md), thread->priority);
   1.440 +	}
   1.441 +	return PR_SUCCESS;
   1.442 +}
   1.443 +
   1.444 +static sigset_t old_mask;	/* store away original gc thread sigmask */
   1.445 +static int gcprio;		/* store away original gc thread priority */
   1.446 +static lwpid_t *all_lwps=NULL;	/* list of lwps that we suspended */
   1.447 +static int num_lwps ;
   1.448 +static int suspendAllOn = 0;
   1.449 +
   1.450 +#define VALID_SP(sp, bottom, top)	\
   1.451 +       (((uint_t)(sp)) > ((uint_t)(bottom)) && ((uint_t)(sp)) < ((uint_t)(top)))
   1.452 +
   1.453 +void unixware_preempt_off()
   1.454 +{
   1.455 +    sigset_t set;
   1.456 +    (void)sigfillset(&set);
   1.457 +    sigprocmask (SIG_SETMASK, &set, &old_mask);
   1.458 +}
   1.459 +
   1.460 +void unixware_preempt_on()
   1.461 +{
   1.462 +    sigprocmask (SIG_SETMASK, &old_mask, NULL);      
   1.463 +}
   1.464 +
   1.465 +void _MD_Begin_SuspendAll()
   1.466 +{
   1.467 +    unixware_preempt_off();
   1.468 +
   1.469 +    PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin_SuspendAll\n"));
   1.470 +    /* run at highest prio so I cannot be preempted */
   1.471 +    thr_getprio(thr_self(), &gcprio);
   1.472 +    thr_setprio(thr_self(), 0x7fffffff); 
   1.473 +    suspendAllOn = 1;
   1.474 +}
   1.475 +
   1.476 +void _MD_End_SuspendAll()
   1.477 +{
   1.478 +}
   1.479 +
   1.480 +void _MD_End_ResumeAll()
   1.481 +{
   1.482 +    PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("End_ResumeAll\n"));
   1.483 +    thr_setprio(thr_self(), gcprio);
   1.484 +    unixware_preempt_on();
   1.485 +    suspendAllOn = 0;
   1.486 +}
   1.487 +
   1.488 +void _MD_Suspend(PRThread *thr)
   1.489 +{
   1.490 +   int lwp_fd, result;
   1.491 +   int lwp_main_proc_fd = 0;
   1.492 +
   1.493 +    thr_suspend(thr->md.handle);
   1.494 +    if (!_PR_IS_GCABLE_THREAD(thr))
   1.495 +      return;
   1.496 +    /* XXX Primordial thread can't be bound to an lwp, hence there is no
   1.497 +     * way we can assume that we can get the lwp status for primordial
   1.498 +     * thread reliably. Hence we skip this for primordial thread, hoping
   1.499 +     * that the SP is saved during lock and cond. wait. 
   1.500 +     * XXX - Again this is concern only for java interpreter, not for the
   1.501 +     * server, 'cause primordial thread in the server does not do java work
   1.502 +     */
   1.503 +    if (thr->flags & _PR_PRIMORDIAL)
   1.504 +      return;
   1.505 +
   1.506 +    /* if the thread is not started yet then don't do anything */
   1.507 +    if (!suspendAllOn || thr->md.lwpid == -1)
   1.508 +      return;
   1.509 +
   1.510 +}
   1.511 +void _MD_Resume(PRThread *thr)
   1.512 +{
   1.513 +   if (!_PR_IS_GCABLE_THREAD(thr) || !suspendAllOn){
   1.514 +     /*XXX When the suspendAllOn is set, we will be trying to do lwp_suspend
   1.515 +      * during that time we can't call any thread lib or libc calls. Hence
   1.516 +      * make sure that no resume is requested for Non gcable thread
   1.517 +      * during suspendAllOn */
   1.518 +      PR_ASSERT(!suspendAllOn);
   1.519 +      thr_continue(thr->md.handle);
   1.520 +      return;
   1.521 +   }
   1.522 +   if (thr->md.lwpid == -1)
   1.523 +     return;
   1.524 + 
   1.525 +   if ( _lwp_continue(thr->md.lwpid) < 0) {
   1.526 +      PR_ASSERT(0);  /* ARGH, we are hosed! */
   1.527 +   }
   1.528 +}
   1.529 +
   1.530 +
   1.531 +PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np)
   1.532 +{
   1.533 +    if (isCurrent) {
   1.534 +	(void) getcontext(CONTEXT(t));	/* XXX tune me: set md_IRIX.c */
   1.535 +    }
   1.536 +    *np = NGREG;
   1.537 +    if (t->md.lwpid == -1)
   1.538 +      memset(&t->md.context.uc_mcontext.gregs[0], 0, NGREG * sizeof(PRWord));
   1.539 +    return (PRWord*) &t->md.context.uc_mcontext.gregs[0];
   1.540 +}
   1.541 +
   1.542 +int
   1.543 +_pr_unixware_clock_gettime (struct timespec *tp)
   1.544 +{
   1.545 +    struct timeval tv;
   1.546 + 
   1.547 +    gettimeofday(&tv, NULL);
   1.548 +    tp->tv_sec = tv.tv_sec;
   1.549 +    tp->tv_nsec = tv.tv_usec * 1000;
   1.550 +    return 0;
   1.551 +}
   1.552 +
   1.553 +
   1.554 +#endif /* USE_SVR4_THREADS */

mercurial