Files
Arquivotheca.Solaris-2.5/lib/libthread/common/condvar.c
seta75D 7c4988eac0 Init
2021-10-11 19:38:01 -03:00

532 lines
14 KiB
C
Executable File

/* Copyright (c) 1993 SMI */
/* All Rights Reserved */
/* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF SMI */
/* The copyright notice above does not evidence any */
/* actual or intended publication of such source code. */
#pragma ident "@(#)condvar.c 1.58 95/09/07 SMI"
#ifdef __STDC__
#pragma weak cond_init = _cond_init
#pragma weak cond_destroy = _cond_destroy
#pragma weak cond_wait = _cond_wait_cancel
#pragma weak cond_timedwait = _cond_timedwait_cancel
#pragma weak cond_signal = _cond_signal
#pragma weak cond_broadcast = _cond_broadcast
#pragma weak pthread_cond_destroy = _cond_destroy
#pragma weak pthread_cond_wait = _pthread_cond_wait
#pragma weak pthread_cond_signal = _cond_signal
#pragma weak pthread_cond_broadcast = _cond_broadcast
#pragma weak pthread_cond_timedwait = _pthread_cond_timedwait
#pragma weak _pthread_cond_destroy = _cond_destroy
#pragma weak _pthread_cond_signal = _cond_signal
#pragma weak _pthread_cond_broadcast = _cond_broadcast
#endif /* __STDC__ */
#include "libthread.h"
#if defined(UTRACE) || defined(ITRACE)
#define TRACE_NAME(x) (((x)->type & TRACE_TYPE) ? (x)->name : "<noname>")
#include <string.h>
#endif
int
_cond_init(cond_t *cvp, int type, void *arg)
{
if (type != USYNC_THREAD && type != USYNC_PROCESS)
return (EINVAL);
cvp->cond_type = type;
cvp->cond_waiters = 0;
#if defined(UTRACE) || defined(ITRACE)
if (arg) {
cvp->cond_type |= TRACE_TYPE;
}
#endif
return (0);
}
int
_cond_destroy(cond_t *cvp)
{
return (0);
}
/*
* "ts" is the timeout value and is an absolute time expressed as number
* of seconds and nanoseconds since epoch (00:00, UCT, 1/1/1970).
*
* This function is a cancellation point. If libthread needs to use
* _cond_timedwait internally and calling function is NOT a cancellation
* point then you use another _cond_timedwait which does not
* have _cancelon/_canceloff code in it.
* Currently sleep in libthread is using cancel-version because
* sleep is a cancellation point.
*/
int
_pthread_cond_timedwait(pthread_cond_t *cvp,
pthread_mutex_t *mp, struct timespec *ts)
{
int ret;
ret = _cond_timedwait_cancel((cond_t *)cvp,
(mutex_t *)mp, (timestruc_t *)ts);
if (ret == ETIME)
return (ETIMEDOUT);
else if (ret == EINTR)
return (0);
else
return (ret);
}
int
_cond_timedwait_cancel(cond_t *cvp, mutex_t *mp, timestruc_t *ts)
{
int cv_timedout = 0;
timestruc_t ts2;
struct timeval tv;
struct timeval curtime;
int ret = 0;
register uthread_t *t = curthread;
TRACE_5(UTR_FAC_THREAD_SYNC, UTR_COND_TIMEDWAIT_START,
"cond_timedwait start:name %s, addr 0x%x, type 0x%x, \
mutex_name_addr %s, 0x%x", TRACE_NAME(cvp), (u_long)cvp,
(u_long)cvp->cond_type, TRACE_NAME(mp), mp);
/*
* cond_eot is initialized at program start-up to be
* (pgm_start_time_in_secs_from_epoch + COND_REL_EOT)
* which is a huge absolute time, sufficiently far into the future.
*/
if (ts->tv_sec < 0 || ts->tv_sec > _cond_eot ||
ts->tv_nsec < 0 || ts->tv_nsec >= 1000000000)
return (EINVAL);
if (cvp->cond_type & USYNC_PROCESS) {
_sigoff();
*(&ts2) = *ts;
_cancelon();
if ((ret = _lwp_cond_timedwait(cvp, mp, &ts2)) == ETIME)
cv_timedout = 1;
_canceloff();
_sigon();
} else {
/*
* First, compute relative time from absolute time.
*/
if (_gettimeofday(&curtime, NULL) == -1) {
perror("cond_timedwait: gettimeofday");
}
tv.tv_sec = ts->tv_sec;
tv.tv_usec = ts->tv_nsec/1000;
if (tv.tv_usec >= curtime.tv_usec) {
if (tv.tv_sec >= curtime.tv_sec) {
tv.tv_sec -= curtime.tv_sec;
tv.tv_usec -= curtime.tv_usec;
} else
return (ETIME);
} else {
if (tv.tv_sec > curtime.tv_sec) {
tv.tv_sec -= (curtime.tv_sec + 1);
tv.tv_usec -= (curtime.tv_usec - 1000000);
} else
return (ETIME);
}
_setcallout(&curthread->t_cv_callo, NULL, &tv, _setrun,
(int)curthread);
_sched_lock();
if (ISTIMEDOUT(&curthread->t_cv_callo)) {
_sched_unlock();
cv_timedout = 1;
} else {
t->t_flag &= ~T_INTR;
t->t_flag |= T_WAITCV;
_t_block((caddr_t)cvp);
cvp->cond_waiters = 1;
_sched_unlock_nosig();
_cancelon();
_mutex_unlock(mp);
_swtch(0);
cv_timedout = _rmcallout(&curthread->t_cv_callo);
_mutex_lock(mp);
_canceloff();
_sigon();
/* do not need to grab schedlock here */
if (t->t_flag & T_INTR) {
t->t_flag &= ~T_INTR;
TRACE_5(UTR_FAC_THREAD_SYNC,
UTR_COND_TIMEDWAIT_END,
"cond_timedwait end:name %s, addr 0x%x, \
type 0x%x, mutex_name %s how %d",
TRACE_NAME(cvp), (u_long)cvp,
(u_long)cvp->cond_type, TRACE_NAME(mp), 2);
return (EINTR);
}
}
}
if (cv_timedout) {
TRACE_5(UTR_FAC_THREAD_SYNC, UTR_COND_TIMEDWAIT_END,
"cond_timedwait end:name %s, addr 0x%x, type 0x%x, \
mutex_name %s how %d ",
TRACE_NAME(cvp), (u_long)cvp,
(u_long)cvp->cond_type, TRACE_NAME(mp), 1);
return (ETIME);
}
TRACE_5(UTR_FAC_THREAD_SYNC, UTR_COND_TIMEDWAIT_END,
"cond_timedwait end:name %s, addr 0x%x, type 0x%x, \
mutex_name %s how %d",
TRACE_NAME(cvp), (u_long)cvp,
(u_long)cvp->cond_type, TRACE_NAME(mp), 0);
return (ret);
}
/*
* Non-cancel version of _cond_timedwait to be used internally
* if calling function is NOT a cancellation point.
*/
int
_cond_timedwait(cond_t *cvp, mutex_t *mp, timestruc_t *ts)
{
int cv_timedout = 0;
timestruc_t ts2;
struct timeval tv;
struct timeval curtime;
int ret = 0;
register uthread_t *t = curthread;
TRACE_5(UTR_FAC_THREAD_SYNC, UTR_COND_TIMEDWAIT_START,
"cond_timedwait start:name %s, addr 0x%x, type 0x%x, \
mutex_name_addr %s, 0x%x", TRACE_NAME(cvp), (u_long)cvp,
(u_long)cvp->cond_type, TRACE_NAME(mp), mp);
/*
* cond_eot is initialized at program start-up to be
* (pgm_start_time_in_secs_from_epoch + COND_REL_EOT)
* which is a huge absolute time, sufficiently far into the future.
*/
if (ts->tv_sec < 0 || ts->tv_sec > _cond_eot ||
ts->tv_nsec < 0 || ts->tv_nsec >= 1000000000)
return (EINVAL);
if (cvp->cond_type & USYNC_PROCESS) {
_sigoff();
*(&ts2) = *ts;
if ((ret = _lwp_cond_timedwait(cvp, mp, &ts2)) == ETIME)
cv_timedout = 1;
_sigon();
} else {
/*
* First, compute relative time from absolute time.
*/
if (_gettimeofday(&curtime, NULL) == -1) {
perror("cond_timedwait: gettimeofday");
}
tv.tv_sec = ts->tv_sec;
tv.tv_usec = ts->tv_nsec/1000;
if (tv.tv_usec >= curtime.tv_usec) {
if (tv.tv_sec >= curtime.tv_sec) {
tv.tv_sec -= curtime.tv_sec;
tv.tv_usec -= curtime.tv_usec;
} else
return (ETIME);
} else {
if (tv.tv_sec > curtime.tv_sec) {
tv.tv_sec -= (curtime.tv_sec + 1);
tv.tv_usec -= (curtime.tv_usec - 1000000);
} else
return (ETIME);
}
_setcallout(&curthread->t_cv_callo, NULL, &tv, _setrun,
(int)curthread);
_sched_lock();
if (ISTIMEDOUT(&curthread->t_cv_callo)) {
_sched_unlock();
cv_timedout = 1;
} else {
t->t_flag &= ~T_INTR;
t->t_flag |= T_WAITCV;
_t_block((caddr_t)cvp);
cvp->cond_waiters = 1;
_sched_unlock_nosig();
_mutex_unlock(mp);
_swtch(0);
cv_timedout = _rmcallout(&curthread->t_cv_callo);
_mutex_lock(mp);
_sigon();
/* do not need to grab schedlock here */
if (t->t_flag & T_INTR) {
t->t_flag &= ~T_INTR;
TRACE_5(UTR_FAC_THREAD_SYNC,
UTR_COND_TIMEDWAIT_END,
"cond_timedwait end:name %s, addr 0x%x, \
type 0x%x, mutex_name %s how %d",
TRACE_NAME(cvp), (u_long)cvp,
(u_long)cvp->cond_type, TRACE_NAME(mp), 2);
return (EINTR);
}
}
}
if (cv_timedout) {
TRACE_5(UTR_FAC_THREAD_SYNC, UTR_COND_TIMEDWAIT_END,
"cond_timedwait end:name %s, addr 0x%x, type 0x%x, \
mutex_name %s how %d ",
TRACE_NAME(cvp), (u_long)cvp,
(u_long)cvp->cond_type, TRACE_NAME(mp), 1);
return (ETIME);
}
TRACE_5(UTR_FAC_THREAD_SYNC, UTR_COND_TIMEDWAIT_END,
"cond_timedwait end:name %s, addr 0x%x, type 0x%x, \
mutex_name %s how %d",
TRACE_NAME(cvp), (u_long)cvp,
(u_long)cvp->cond_type, TRACE_NAME(mp), 0);
return (ret);
}
/*
* Cancel version of _cond_wait for exported API use.
* We dont want internal functions to be cancellation points
* just because cond_wait is, and they need to use _cond_wait().
* This should be called internally only if calling function is
* a cancellation point.
*/
int
_pthread_cond_wait(pthread_cond_t *cvp, pthread_mutex_t *mp)
{
int ret;
ret = _cond_wait_cancel((cond_t *)cvp, (mutex_t *)mp);
if (ret == EINTR)
return (0);
else
return (ret);
}
int
_cond_wait_cancel(cond_t *cvp, mutex_t *mp)
{
int retcode;
register uthread_t *t = curthread;
TRACE_5(UTR_FAC_THREAD_SYNC, UTR_COND_WAIT_START,
"cond_wait start:name %s, addr 0x%x, type 0x%x, \
mutex_name_addr %s, 0x%x", TRACE_NAME(cvp), (u_long)cvp,
(u_long)cvp->cond_type, TRACE_NAME(mp), mp);
/* leave the sigoff effective */
if (cvp->cond_type & USYNC_PROCESS) {
_sigoff();
_cancelon();
retcode = __lwp_cond_wait(cvp, mp);
_canceloff();
_sigon();
return (retcode);
} else {
_sched_lock();
t->t_flag &= ~T_INTR;
/*
* The T_WAITCV flag is turned on here to indicate that the
* thread about to sleep in _t_block() is sleeping for a CV.
* It is not turned off after thread wakes-up, in a bracketing
* manner, since the flag is read by _t_release() only for a
* thread found on the sleep queue. Whenever a thread calls
* _t_block(), T_WAITCV is either turned on (for cvs, as here)
* or off (for mutexes - see mutex.c)
*/
t->t_flag |= T_WAITCV;
_t_block((caddr_t)cvp);
cvp->cond_waiters = 1;
_sched_unlock_nosig();
_cancelon();
_mutex_unlock(mp);
_swtch(0);
_mutex_lock(mp);
_canceloff();
_sigon();
ASSERT(t->t_link == NULL);
ASSERT(t->t_wchan == NULL);
ASSERT(t->t_state == TS_ONPROC);
}
TRACE_5(UTR_FAC_THREAD_SYNC, UTR_COND_WAIT_END,
"cond_wait end:name %s, addr 0x%x, type 0x%x, \
mutex_name_addr %s, 0x%x", TRACE_NAME(cvp), (u_long)cvp,
(u_long)cvp->cond_type, TRACE_NAME(mp), mp);
/* do not need to grab schedlock here */
if (curthread->t_flag & T_INTR) {
curthread->t_flag &= ~T_INTR;
return (EINTR);
}
return (0);
}
/*
* Non-cancel version of _cond_wait for internal use.
* We dont want internal functions to be cancellation points
* just because cond_wait is, and they need to use cond_wait().
*/
int
_cond_wait(cond_t *cvp, mutex_t *mp)
{
int retcode;
register uthread_t *t = curthread;
TRACE_5(UTR_FAC_THREAD_SYNC, UTR_COND_WAIT_START,
"cond_wait_i start:name %s, addr 0x%x, type 0x%x, \
mutex_name_addr %s, 0x%x", TRACE_NAME(cvp), (u_long)cvp,
(u_long)cvp->cond_type, TRACE_NAME(mp), mp);
/* leave the sigoff effective */
if (cvp->cond_type & USYNC_PROCESS) {
_sigoff();
retcode = __lwp_cond_wait(cvp, mp);
_sigon();
return (retcode);
} else {
_sched_lock();
t->t_flag &= ~T_INTR;
t->t_flag |= T_WAITCV;
_t_block((caddr_t)cvp);
cvp->cond_waiters = 1;
_sched_unlock_nosig();
_mutex_unlock(mp);
_swtch(0);
_mutex_lock(mp);
_sigon();
ASSERT(t->t_link == NULL);
ASSERT(t->t_wchan == NULL);
ASSERT(t->t_state == TS_ONPROC);
}
TRACE_5(UTR_FAC_THREAD_SYNC, UTR_COND_WAIT_END,
"cond_wait_i end:name %s, addr 0x%x, type 0x%x, \
mutex_name_addr %s, 0x%x", TRACE_NAME(cvp), (u_long)cvp,
(u_long)cvp->cond_type, TRACE_NAME(mp), mp);
/* do not need to grab schedlock here */
if (curthread->t_flag & T_INTR) {
curthread->t_flag &= ~T_INTR;
return (EINTR);
}
return (0);
}
int
_cond_signal(cond_t *cvp)
{
u_char waiters;
TRACE_3(UTR_FAC_THREAD_SYNC, UTR_COND_SIGNAL_START,
"cond_signal start:name %s, addr 0x%x, type 0x%x",
TRACE_NAME(cvp), (u_long)cvp, (u_long)cvp->cond_type);
if (cvp->cond_waiters) {
if (cvp->cond_type & USYNC_PROCESS) {
if (_lwp_cond_signal(cvp) != 0) {
_panic("cond_signal: _lwp_cond_signal failed");
}
} else {
_sched_lock();
_t_release((caddr_t)cvp, &waiters, T_WAITCV);
cvp->cond_waiters = waiters;
_sched_unlock();
}
}
TRACE_3(UTR_FAC_THREAD_SYNC, UTR_COND_SIGNAL_END,
"cond_signal end:name %s, addr 0x%x, type 0x%x",
TRACE_NAME(cvp), (u_long)cvp, (u_long)cvp->cond_type);
return (0);
}
int
_cond_broadcast(cond_t *cvp)
{
TRACE_3(UTR_FAC_THREAD_SYNC, UTR_COND_BCST_START,
"cond_broadcast start:name %s, addr %x, type 0x%x",
TRACE_NAME(cvp), (u_long)cvp, (u_long)cvp->cond_type);
if (cvp->cond_waiters) {
if (cvp->cond_type & USYNC_PROCESS) {
_lwp_cond_broadcast(cvp);
} else {
_sched_lock();
_t_release_all((caddr_t)cvp);
cvp->cond_waiters = 0;
_sched_unlock();
}
}
TRACE_3(UTR_FAC_THREAD_SYNC, UTR_COND_BCST_END,
"cond_broadcast end:name %s, addr %x, type 0x%x",
TRACE_NAME(cvp), (u_long)cvp, (u_long)cvp->cond_type);
return (0);
}
#if defined(UTRACE) || defined(ITRACE)
int
trace_cond_init(cond_t *cvp, char type, void *arg)
{
cvp->cond_type = type;
cvp->cond_waiters = 0;
return (0);
}
int
trace_cond_wait(cond_t *cvp, mutex_t *mp)
{
int retcode;
if (cvp->type & USYNC_PROCESS) {
___lwp_cond_wait(cvp, mp, 0);
} else {
_sched_lock();
t->t_flag |= T_WAITCV;
_t_block((caddr_t)cvp);
cvp->cond_waiters = 1;
_sched_unlock_nosig();
_mutex_unlock(mp);
_swtch(0);
_sigon();
ASSERT(curthread->t_link == NULL);
ASSERT(curthread->t_wchan == NULL);
ASSERT(curthread->t_state == TS_ONPROC);
}
trace_mutex_lock(mp);
return (0);
}
int
trace_cond_signal(cond_t *cvp)
{
u_char waiters;
if (cvp->cond_waiters) {
if (cvp->cond_type & USYNC_PROCESS) {
if (_lwp_cond_signal(cvp) != 0) {
_panic("_cond_signal: lwp_cond_signal failed");
}
} else {
_sched_lock();
_t_release((caddr_t)cvp, &waiters, T_WAITCV);
cvp->cond_waiters = waiters;
_sched_unlock();
}
}
return (0);
}
int
trace_cond_broadcast(cond_t *cvp)
{
if (cvp->cond_waiters) {
if (cvp->cond_type & USYNC_PROCESS) {
_lwp_cond_broadcast(cvp);
} else {
_sched_lock();
_t_release_all((caddr_t)cvp);
cvp->cond_waiters = 0;
_sched_unlock();
}
}
return (0);
}
#endif