add pthread_cancel, obtained from OpenBSD.
eischen (Daniel Eischen) added wrappers to protect against cancled threads orphaning internal resources. the cancelability code is still a bit fuzzy but works for test programs of my own, OpenBSD's and some examples from ORA's books. add readdir_r to both libc and libc_r add some 'const' attributes to function parameters Reviewed by: eischen, jasone
This commit is contained in:
parent
d29b1c8b20
commit
7285bccf1a
@ -31,6 +31,9 @@
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)dirent.h 8.2 (Berkeley) 7/28/94
|
||||
*
|
||||
* $FreeBSD$
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _DIRENT_H_
|
||||
@ -95,6 +98,7 @@ int scandir __P((const char *, struct dirent ***,
|
||||
int alphasort __P((const void *, const void *));
|
||||
int getdirentries __P((int, char *, int, long *));
|
||||
#endif /* not POSIX */
|
||||
int readdir_r __P((DIR *, struct dirent *, struct dirent **));
|
||||
__END_DECLS
|
||||
|
||||
#endif /* !KERNEL */
|
||||
|
@ -87,6 +87,15 @@
|
||||
#define PTHREAD_PROCESS_PRIVATE 0
|
||||
#define PTHREAD_PROCESS_SHARED 1
|
||||
|
||||
/*
|
||||
* Flags for cancelling threads
|
||||
*/
|
||||
#define PTHREAD_CANCEL_ENABLE 0
|
||||
#define PTHREAD_CANCEL_DISABLE 1
|
||||
#define PTHREAD_CANCEL_DEFERRED 0
|
||||
#define PTHREAD_CANCEL_ASYNCHRONOUS 2
|
||||
#define PTHREAD_CANCELED ((void *) 1)
|
||||
|
||||
/*
|
||||
* Forward structure definitions.
|
||||
*
|
||||
@ -266,12 +275,10 @@ pthread_t pthread_self __P((void));
|
||||
int pthread_setspecific __P((pthread_key_t, const void *));
|
||||
int pthread_sigmask __P((int, const sigset_t *, sigset_t *));
|
||||
|
||||
#ifdef NOT_YET
|
||||
int pthread_cancel __P((pthread_t));
|
||||
int pthread_setcancelstate __P((int, int *));
|
||||
int pthread_setcanceltype __P((int, int *));
|
||||
void pthread_testcancel __P((void));
|
||||
#endif
|
||||
|
||||
int pthread_getprio __P((pthread_t));
|
||||
int pthread_setprio __P((pthread_t, int));
|
||||
@ -301,11 +308,11 @@ int pthread_mutexattr_setprotocol __P((pthread_mutexattr_t *,
|
||||
#endif
|
||||
|
||||
#if defined(_POSIX_THREAD_PRIORITY_SCHEDULING)
|
||||
int pthread_attr_getinheritsched __P((pthread_attr_t *, int *));
|
||||
int pthread_attr_getschedparam __P((pthread_attr_t *,
|
||||
int pthread_attr_getinheritsched __P((const pthread_attr_t *, int *));
|
||||
int pthread_attr_getschedparam __P((const pthread_attr_t *,
|
||||
struct sched_param *));
|
||||
int pthread_attr_getschedpolicy __P((pthread_attr_t *, int *));
|
||||
int pthread_attr_getscope __P((pthread_attr_t *, int *));
|
||||
int pthread_attr_getschedpolicy __P((const pthread_attr_t *, int *));
|
||||
int pthread_attr_getscope __P((const pthread_attr_t *, int *));
|
||||
int pthread_attr_setinheritsched __P((pthread_attr_t *, int));
|
||||
int pthread_attr_setschedparam __P((pthread_attr_t *,
|
||||
struct sched_param *));
|
||||
@ -314,7 +321,7 @@ int pthread_attr_setscope __P((pthread_attr_t *, int));
|
||||
int pthread_getschedparam __P((pthread_t pthread, int *,
|
||||
struct sched_param *));
|
||||
int pthread_setschedparam __P((pthread_t, int,
|
||||
struct sched_param *));
|
||||
const struct sched_param *));
|
||||
#endif
|
||||
|
||||
int pthread_attr_setfloatstate __P((pthread_attr_t *, int));
|
||||
|
@ -29,6 +29,9 @@
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*
|
||||
*/
|
||||
|
||||
#if defined(LIBC_SCCS) && !defined(lint)
|
||||
@ -37,6 +40,11 @@ static char sccsid[] = "@(#)readdir.c 8.3 (Berkeley) 9/29/94";
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#ifdef _THREAD_SAFE
|
||||
#include <pthread.h>
|
||||
#include "pthread_private.h"
|
||||
#endif _THREAD_SAFE
|
||||
|
||||
/*
|
||||
* get next entry in a directory.
|
||||
@ -73,3 +81,39 @@ readdir(dirp)
|
||||
return (dp);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
readdir_r(dirp, entry, result)
|
||||
DIR *dirp;
|
||||
struct dirent *entry;
|
||||
struct dirent **result;
|
||||
{
|
||||
struct dirent *dp;
|
||||
int ret;
|
||||
|
||||
if (dirp->dd_fd < 0) {
|
||||
return EBADF;
|
||||
}
|
||||
#ifdef _THREAD_SAFE
|
||||
if ((ret = _FD_LOCK(dirp->dd_fd, FD_READ, NULL)) != 0)
|
||||
return ret;
|
||||
#endif
|
||||
errno = 0;
|
||||
dp = readdir(dirp);
|
||||
if (dp == NULL && errno != 0) {
|
||||
#ifdef _THREAD_SAFE
|
||||
_FD_UNLOCK(dirp->dd_fd, FD_READ);
|
||||
#endif
|
||||
return errno;
|
||||
}
|
||||
if (dp != NULL)
|
||||
memcpy(entry, dp, sizeof *entry);
|
||||
#ifdef _THREAD_SAFE
|
||||
_FD_UNLOCK(dirp->dd_fd, FD_READ);
|
||||
#endif
|
||||
if (dp != NULL)
|
||||
*result = entry;
|
||||
else
|
||||
*result = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
@ -15,6 +15,9 @@ CFLAGS+=-DPTHREAD_KERNEL -D_THREAD_SAFE -I${.CURDIR}/uthread
|
||||
# thread locking.
|
||||
CFLAGS+=-D_LOCK_DEBUG
|
||||
|
||||
# enable extra internal consistancy checks
|
||||
# CFLAGS+=-D_PTHREADS_INVARIANTS
|
||||
|
||||
AINC= -I${.CURDIR}/../libc/${MACHINE_ARCH} -I${.CURDIR}/uthread
|
||||
PRECIOUSLIB= yes
|
||||
|
||||
@ -25,11 +28,12 @@ PRECIOUSLIB= yes
|
||||
HIDDEN_SYSCALLS= accept.o bind.o close.o connect.o dup.o dup2.o \
|
||||
execve.o fchflags.o fchmod.o fchown.o fcntl.o \
|
||||
flock.o fpathconf.o fstat.o fstatfs.o fsync.o getdirentries.o \
|
||||
getpeername.o getsockname.o getsockopt.o ioctl.o listen.o \
|
||||
nanosleep.o nfssvc.o open.o poll.o read.o readv.o recvfrom.o \
|
||||
getlogin.o getpeername.o getsockname.o getsockopt.o ioctl.o listen.o \
|
||||
msync.o nanosleep.o nfssvc.o open.o poll.o read.o readv.o recvfrom.o \
|
||||
recvmsg.o sched_yield.o select.o sendmsg.o sendto.o \
|
||||
setsockopt.o shutdown.o sigaction.o sigaltstack.o \
|
||||
signanosleep.o sigpending.o sigprocmask.o sigsuspend.o socket.o \
|
||||
signanosleep.o sigpending.o sigprocmask.o sigreturn.o sigsetmask.o \
|
||||
sigsuspend.o socket.o \
|
||||
socketpair.o wait4.o write.o writev.o
|
||||
|
||||
.include "${.CURDIR}/../libc/Makefile.inc"
|
||||
|
@ -12,6 +12,7 @@ MAN3+= pthread_cleanup_pop.3 \
|
||||
pthread_cond_signal.3 \
|
||||
pthread_cond_timedwait.3 \
|
||||
pthread_cond_wait.3 \
|
||||
pthread_cancel.3 \
|
||||
pthread_create.3 \
|
||||
pthread_detach.3 \
|
||||
pthread_equal.3 \
|
||||
@ -36,4 +37,8 @@ MAN3+= pthread_cleanup_pop.3 \
|
||||
pthread_rwlockattr_init.3 \
|
||||
pthread_rwlockattr_setpshared.3 \
|
||||
pthread_self.3 \
|
||||
pthread_setspecific.3
|
||||
pthread_setspecific.3 \
|
||||
pthread_testcancel.3
|
||||
|
||||
MLINKS+= pthread_cancel.3 pthread_setcancelstate.3 \
|
||||
pthread_cancel.3 pthread_getcancelstate.3
|
||||
|
70
lib/libc_r/man/pthread_cancel.3
Normal file
70
lib/libc_r/man/pthread_cancel.3
Normal file
@ -0,0 +1,70 @@
|
||||
.\" $FreeBSD$
|
||||
.Dd January 17, 1999
|
||||
.Dt PTHREAD_CANCEL 3
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm pthread_cancel
|
||||
.Nd cancel execution of a thread
|
||||
.Sh SYNOPSIS
|
||||
.Fd #include <pthread.h>
|
||||
.Ft int
|
||||
.Fn pthread_cancel "pthread_t thread"
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Fn pthread_cancel
|
||||
function requests that
|
||||
.Fa thread
|
||||
be canceled. The target thread's cancelability state and type determines
|
||||
when the cancellation takes effect. When the cancellation is acted on,
|
||||
the cancellation cleanup handlers for
|
||||
.Fa thread
|
||||
are called. When the last cancellation cleanup handler returns,
|
||||
the thread-specific data destructor functions will be called for
|
||||
.Fa thread .
|
||||
When the last destructor function returns,
|
||||
.Fa thread
|
||||
will be terminated.
|
||||
.Pp
|
||||
The cancellation processing in the target thread runs asynchronously with
|
||||
respect to the calling thread returning from
|
||||
.Fn pthread_cancel .
|
||||
.Pp
|
||||
A status of
|
||||
.Dv PTHREAD_CANCELED
|
||||
is made available to any threads joining with the target. The symbolic
|
||||
constant
|
||||
.Dv PTHREAD_CANCELED
|
||||
expands to a constant expression of type
|
||||
.Ft "(void *)" ,
|
||||
whose value matches no pointer to an object in memory nor the value
|
||||
.Dv NULL .
|
||||
.Sh RETURN VALUES
|
||||
If successful, the
|
||||
.Fn pthread_cancel
|
||||
functions will return zero. Otherwise an error number will be returned to
|
||||
indicate the error.
|
||||
.Sh ERRORS
|
||||
.Fn pthread_cancel
|
||||
will fail if:
|
||||
.Bl -tag -width Er
|
||||
.It Bq Er ESRCH
|
||||
No thread could be found corresponding to that specified by the given
|
||||
thread ID.
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr pthread_cleanup_pop 3 ,
|
||||
.Xr pthread_cleanup_push 3 ,
|
||||
.Xr pthread_exit 3 ,
|
||||
.Xr pthread_join 3 ,
|
||||
.Xr pthread_setcancelstate 3 ,
|
||||
.Xr pthread_setcanceltype 3 ,
|
||||
.Xr pthread_testcancel 3
|
||||
.Sh STANDARDS
|
||||
.Fn pthread_cancel
|
||||
conforms to ISO/IEC 9945-1 ANSI/IEEE
|
||||
.Pq Dq Tn POSIX
|
||||
Std 1003.1 Second Edition 1996-07-12.
|
||||
.Sh AUTHORS
|
||||
This man page was written by
|
||||
.An David Leonard <d@openbsd.org>
|
||||
for the OpenBSD implementation of pthread_cancel.
|
187
lib/libc_r/man/pthread_testcancel.3
Normal file
187
lib/libc_r/man/pthread_testcancel.3
Normal file
@ -0,0 +1,187 @@
|
||||
.\" $FreeBSD$
|
||||
.Dd January 17, 1999
|
||||
.Dt PTHREAD_TESTCANCEL 3
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm pthread_setcancelstate ,
|
||||
.Nm pthread_setcanceltype ,
|
||||
.Nm pthread_testcancel
|
||||
.Nd set cancelability state
|
||||
.Sh SYNOPSIS
|
||||
.Fd #include <pthread.h>
|
||||
.Ft int
|
||||
.Fn pthread_setcancelstate "int state" "int *oldstate"
|
||||
.Ft int
|
||||
.Fn pthread_setcanceltype "int type" "int *oldtype"
|
||||
.Ft void
|
||||
.Fn pthread_testcancel "void"
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Fn pthread_setcancelstate
|
||||
function atomically both sets the calling thread's cancelability state
|
||||
to the indicated
|
||||
.Fa state
|
||||
and returns the previous cancelability state at the location referenced by
|
||||
.Fa oldstate .
|
||||
Legal values for
|
||||
.Fa state
|
||||
are
|
||||
.Dv PTHREAD_CANCEL_ENABLE
|
||||
and
|
||||
.Dv PTHREAD_CANCEL_DISABLE .
|
||||
.Pp
|
||||
The
|
||||
.Fn pthread_setcanceltype
|
||||
function atomically both sets the calling thread's cancelability type
|
||||
to the indicated
|
||||
.Fa type
|
||||
and returns the previous cancelability type at the location referenced by
|
||||
.Fa oldtype .
|
||||
Legal values for
|
||||
.Fa type
|
||||
are
|
||||
.Dv PTHREAD_CANCEL_DEFERRED
|
||||
and
|
||||
.Dv PTHREAD_CANCEL_ASYNCHRONOUS .
|
||||
.Pp
|
||||
The cancelability state and type of any newly created threads, including the
|
||||
thread in which
|
||||
.Fn main
|
||||
was first invoked, are
|
||||
.Dv PTHREAD_CANCEL_ENABLE
|
||||
and
|
||||
.Dv PTHREAD_CANCEL_DEFERRED
|
||||
respectively.
|
||||
.Pp
|
||||
The
|
||||
.Fn pthread_testcancel
|
||||
function creates a cancellation point in the calling thread. The
|
||||
.Fn pthread_testcancel
|
||||
function has no effect if cancelability is disabled.
|
||||
.Pp
|
||||
.Ss Cancelability States
|
||||
The cancelability state of a thread determines the action taken upon
|
||||
receipt of a cancellation request. The thread may control cancellation in
|
||||
a number of ways.
|
||||
.Pp
|
||||
Each thread maintains its own
|
||||
.Dq cancelability state
|
||||
which may be encoded in two bits:
|
||||
.Bl -hang
|
||||
.It Em Cancelability Enable
|
||||
When cancelability is
|
||||
.Dv PTHREAD_CANCEL_DISABLE ,
|
||||
cancellation requests against the target thread are held pending.
|
||||
.It Em Cancelability Type
|
||||
When cancelability is enabled and the cancelability type is
|
||||
.Dv PTHREAD_CANCEL_ASYNCHRONOUS ,
|
||||
new or pending cancellation requests may be acted upon at any time.
|
||||
When cancelability is enabled and the cancelability type is
|
||||
.Dv PTHREAD_CANCEL_DEFERRED ,
|
||||
cancellation requests are held pending until a cancellation point (see
|
||||
below) is reached. If cancelability is disabled, the setting of the
|
||||
cancelability type has no immediate effect as all cancellation requests
|
||||
are held pending; however, once cancelability is enabled again the new
|
||||
type will be in effect.
|
||||
.El
|
||||
.Ss Cancellation Points
|
||||
Cancellation points will occur when a thread is executing the following
|
||||
functions:
|
||||
.Fn close ,
|
||||
.Fn creat ,
|
||||
.Fn fcntl ,
|
||||
.Fn fsync ,
|
||||
.Fn msync ,
|
||||
.Fn nanosleep ,
|
||||
.Fn open ,
|
||||
.Fn pause ,
|
||||
.Fn pthread_cond_timedwait ,
|
||||
.Fn pthread_cond_wait ,
|
||||
.Fn pthread_join ,
|
||||
.Fn pthread_testcancel ,
|
||||
.Fn read ,
|
||||
.Fn sigwaitinfo ,
|
||||
.Fn sigsuspend ,
|
||||
.Fn sigwait ,
|
||||
.Fn sleep ,
|
||||
.Fn system ,
|
||||
.Fn tcdrain ,
|
||||
.Fn wait ,
|
||||
.Fn waitpid ,
|
||||
.Fn write .
|
||||
.Sh RETURN VALUES
|
||||
If successful, the
|
||||
.Fn pthread_setcancelstate
|
||||
and
|
||||
.Fn pthread_setcanceltype
|
||||
functions will return zero. Otherwise, an error number shall be returned to
|
||||
indicate the error.
|
||||
.Pp
|
||||
The
|
||||
.Fn pthread_setcancelstate
|
||||
and
|
||||
.Fn pthread_setcanceltype
|
||||
functions are used to control the points at which a thread may be
|
||||
asynchronously canceled. For cancellation control to be usable in modular
|
||||
fashion, some rules must be followed.
|
||||
.Pp
|
||||
For purposes of this discussion, consider an object to be a generalization
|
||||
of a procedure. It is a set of procedures and global variables written as
|
||||
a unit and called by clients not known by the object. Objects may depend
|
||||
on other objects.
|
||||
.Pp
|
||||
First, cancelability should only be disabled on entry to an object, never
|
||||
explicitly enabled. On exit from an object, the cancelability state should
|
||||
always be restored to its value on entry to the object.
|
||||
.Pp
|
||||
This follows from a modularity argument: if the client of an object (or the
|
||||
client of an object that uses that object) has disabled cancelability, it is
|
||||
because the client doesn't want to have to worry about how to clean up if the
|
||||
thread is canceled while executing some sequence of actions. If an object
|
||||
is called in such a state and it enables cancelability and a cancellation
|
||||
request is pending for that thread, then the thread will be canceled,
|
||||
contrary to the wish of the client that disabled.
|
||||
.Pp
|
||||
Second, the cancelability type may be explicitly set to either
|
||||
.Em deferred
|
||||
or
|
||||
.Em asynchronous
|
||||
upon entry to an object. But as with the cancelability state, on exit from
|
||||
an object that cancelability type should always be restored to its value on
|
||||
entry to the object.
|
||||
.Pp
|
||||
Finally, only functions that are cancel-safe may be called from a thread that
|
||||
is asynchronously cancelable.
|
||||
.Sh ERRORS
|
||||
The function
|
||||
.Fn pthread_setcancelstate
|
||||
may fail with:
|
||||
.Bl -tag -width Er
|
||||
.It Bq Er EINVAL
|
||||
The specified state is not
|
||||
.Dv PTHREAD_CANCEL_ENABLE
|
||||
or
|
||||
.Dv PTHREAD_CANCEL_DISABLE .
|
||||
.El
|
||||
.Pp
|
||||
The function
|
||||
.Fn pthread_setcanceltype
|
||||
may fail with:
|
||||
.Bl -tag -width Er
|
||||
.It Bq Er EINVAL
|
||||
The specified state is not
|
||||
.Dv PTHREAD_CANCEL_DEFERRED
|
||||
or
|
||||
.Dv PTHREAD_CANCEL_ASYNCHRONOUS .
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr pthread_cancel 3
|
||||
.Sh STANDARDS
|
||||
.Fn pthread_testcancel
|
||||
conforms to ISO/IEC 9945-1 ANSI/IEEE
|
||||
.Pq Dq Tn POSIX
|
||||
Std 1003.1 Second Edition 1996-07-12.
|
||||
.Sh AUTHORS
|
||||
This man page was written by
|
||||
.An David Leonard <d@openbsd.org>
|
||||
for the OpenBSD implementation of pthread_cancel.
|
@ -24,6 +24,7 @@ SRCS+= \
|
||||
uthread_attr_setstacksize.c \
|
||||
uthread_autoinit.cc \
|
||||
uthread_bind.c \
|
||||
uthread_cancel.c \
|
||||
uthread_clean.c \
|
||||
uthread_close.c \
|
||||
uthread_cond.c \
|
||||
@ -37,6 +38,7 @@ SRCS+= \
|
||||
uthread_equal.c \
|
||||
uthread_execve.c \
|
||||
uthread_exit.c \
|
||||
uthread_fchflags.c \
|
||||
uthread_fchmod.c \
|
||||
uthread_fchown.c \
|
||||
uthread_fcntl.c \
|
||||
@ -64,6 +66,7 @@ SRCS+= \
|
||||
uthread_listen.c \
|
||||
uthread_mattr_init.c \
|
||||
uthread_mattr_kind_np.c \
|
||||
uthread_msync.c \
|
||||
uthread_multi_np.c \
|
||||
uthread_mutex.c \
|
||||
uthread_mutex_prioceiling.c \
|
||||
|
@ -253,7 +253,7 @@ struct pthread_mutex {
|
||||
*/
|
||||
#define PTHREAD_MUTEX_STATIC_INITIALIZER \
|
||||
{ PTHREAD_MUTEX_DEFAULT, PTHREAD_PRIO_NONE, TAILQ_INITIALIZER, \
|
||||
NULL, { NULL }, 0, 0, 0, 0, TAILQ_INITIALIZER, \
|
||||
NULL, { NULL }, MUTEX_FLAGS_PRIVATE, 0, 0, 0, TAILQ_INITIALIZER, \
|
||||
_SPINLOCK_INITIALIZER }
|
||||
|
||||
struct pthread_mutex_attr {
|
||||
@ -513,6 +513,15 @@ struct pthread {
|
||||
*/
|
||||
int sig_saved;
|
||||
|
||||
/*
|
||||
* Cancelability flags - the lower 2 bits are used by cancel
|
||||
* definitions in pthread.h
|
||||
*/
|
||||
#define PTHREAD_AT_CANCEL_POINT 0x0004
|
||||
#define PTHREAD_CANCELLING 0x0008
|
||||
#define PTHREAD_CANCEL_NEEDED 0x0010
|
||||
int cancelflags;
|
||||
|
||||
/*
|
||||
* Current signal mask and pending signals.
|
||||
*/
|
||||
@ -610,15 +619,18 @@ struct pthread {
|
||||
*/
|
||||
int yield_on_sig_undefer;
|
||||
|
||||
/* Miscellaneous data. */
|
||||
/* Miscellaneous flags; only set with signals deferred. */
|
||||
int flags;
|
||||
#define PTHREAD_FLAGS_PRIVATE 0x0001
|
||||
#define PTHREAD_EXITING 0x0002
|
||||
#define PTHREAD_FLAGS_IN_CONDQ 0x0004 /* in condition queue using qe link*/
|
||||
#define PTHREAD_FLAGS_IN_WORKQ 0x0008 /* in work queue using qe link */
|
||||
#define PTHREAD_FLAGS_IN_WAITQ 0x0010 /* in waiting queue using pqe link*/
|
||||
#define PTHREAD_FLAGS_IN_PRIOQ 0x0020 /* in priority queue using pqe link*/
|
||||
#define PTHREAD_FLAGS_TRACE 0x0040 /* for debugging purposes */
|
||||
#define PTHREAD_FLAGS_IN_WAITQ 0x0010 /* in waiting queue using pqe link */
|
||||
#define PTHREAD_FLAGS_IN_PRIOQ 0x0020 /* in priority queue using pqe link */
|
||||
#define PTHREAD_FLAGS_IN_MUTEXQ 0x0040 /* in mutex queue using qe link */
|
||||
#define PTHREAD_FLAGS_IN_FILEQ 0x0080 /* in file lock queue using qe link */
|
||||
#define PTHREAD_FLAGS_IN_FDQ 0x0100 /* in fd lock queue using qe link */
|
||||
#define PTHREAD_FLAGS_TRACE 0x0200 /* for debugging purposes */
|
||||
|
||||
/*
|
||||
* Base priority is the user setable and retrievable priority
|
||||
@ -925,6 +937,7 @@ char *__ttyname_r_basic(int, char *, size_t);
|
||||
char *ttyname_r(int, char *, size_t);
|
||||
int _find_dead_thread(pthread_t);
|
||||
int _find_thread(pthread_t);
|
||||
void _funlock_owned(pthread_t);
|
||||
int _thread_create(pthread_t *,const pthread_attr_t *,void *(*start_routine)(void *),void *,pthread_t);
|
||||
int _thread_fd_lock(int, int, struct timespec *);
|
||||
int _thread_fd_lock_debug(int, int, struct timespec *,char *fname,int lineno);
|
||||
@ -932,8 +945,9 @@ void _dispatch_signals(void);
|
||||
void _thread_signal(pthread_t, int);
|
||||
int _mutex_cv_lock(pthread_mutex_t *);
|
||||
int _mutex_cv_unlock(pthread_mutex_t *);
|
||||
void _mutex_notify_priochange(pthread_t);
|
||||
int _mutex_reinit(pthread_mutex_t *);
|
||||
void _mutex_notify_priochange(struct pthread *);
|
||||
void _mutex_unlock_private(pthread_t);
|
||||
int _cond_reinit(pthread_cond_t *);
|
||||
int _pq_alloc(struct pq_queue *, int, int);
|
||||
int _pq_init(struct pq_queue *);
|
||||
@ -948,8 +962,10 @@ void _waitq_setactive(void);
|
||||
void _waitq_clearactive(void);
|
||||
#endif
|
||||
void _thread_exit(char *, int, char *);
|
||||
void _thread_exit_cleanup(void);
|
||||
void _thread_fd_unlock(int, int);
|
||||
void _thread_fd_unlock_debug(int, int, char *, int);
|
||||
void _thread_fd_unlock_owned(pthread_t);
|
||||
void *_thread_cleanup(pthread_t);
|
||||
void _thread_cleanupspecific(void);
|
||||
void _thread_dump_info(void);
|
||||
@ -969,6 +985,9 @@ void _thread_start_sig_handler(void);
|
||||
void _thread_seterrno(pthread_t,int);
|
||||
int _thread_fd_table_init(int fd);
|
||||
pthread_addr_t _thread_gc(pthread_addr_t);
|
||||
void _thread_enter_cancellation_point(void);
|
||||
void _thread_leave_cancellation_point(void);
|
||||
void _thread_cancellation_point(void);
|
||||
|
||||
/* #include <signal.h> */
|
||||
int _thread_sys_sigaction(int, const struct sigaction *, struct sigaction *);
|
||||
@ -1148,6 +1167,8 @@ pid_t _thread_sys_wait4(pid_t, int *, int, struct rusage *);
|
||||
#ifdef _SYS_POLL_H_
|
||||
int _thread_sys_poll(struct pollfd *, unsigned, int);
|
||||
#endif
|
||||
/* #include <sys/mman.h> */
|
||||
int _thread_sys_msync(void *, size_t, int);
|
||||
__END_DECLS
|
||||
|
||||
#endif /* !_PTHREAD_PRIVATE_H */
|
||||
|
@ -37,7 +37,7 @@
|
||||
#include "pthread_private.h"
|
||||
|
||||
int
|
||||
pthread_attr_getinheritsched(pthread_attr_t *attr, int *sched_inherit)
|
||||
pthread_attr_getinheritsched(const pthread_attr_t *attr, int *sched_inherit)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
|
@ -37,7 +37,7 @@
|
||||
#include "pthread_private.h"
|
||||
|
||||
int
|
||||
pthread_attr_getschedparam(pthread_attr_t *attr, struct sched_param *param)
|
||||
pthread_attr_getschedparam(const pthread_attr_t *attr, struct sched_param *param)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
|
@ -37,7 +37,7 @@
|
||||
#include "pthread_private.h"
|
||||
|
||||
int
|
||||
pthread_attr_getschedpolicy(pthread_attr_t *attr, int *policy)
|
||||
pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
|
@ -37,7 +37,7 @@
|
||||
#include "pthread_private.h"
|
||||
|
||||
int
|
||||
pthread_attr_getscope(pthread_attr_t *attr, int *contentionscope)
|
||||
pthread_attr_getscope(const pthread_attr_t *attr, int *contentionscope)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
|
179
lib/libc_r/uthread/uthread_cancel.c
Normal file
179
lib/libc_r/uthread/uthread_cancel.c
Normal file
@ -0,0 +1,179 @@
|
||||
/*
|
||||
* David Leonard <d@openbsd.org>, 1999. Public domain.
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <sys/errno.h>
|
||||
#include <pthread.h>
|
||||
#include "pthread_private.h"
|
||||
|
||||
int
|
||||
pthread_cancel(pthread_t pthread)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if ((ret = _find_thread(pthread)) != 0) {
|
||||
/* NOTHING */
|
||||
} else if (pthread->state == PS_DEAD || pthread->state == PS_DEADLOCK) {
|
||||
ret = 0;
|
||||
} else {
|
||||
/* Protect the scheduling queues: */
|
||||
_thread_kern_sig_defer();
|
||||
|
||||
/* Check if we need to kick it back into the run queue: */
|
||||
if ((pthread->cancelflags & PTHREAD_CANCEL_DISABLE) == 0)
|
||||
switch (pthread->state) {
|
||||
case PS_RUNNING:
|
||||
/* No need to resume: */
|
||||
pthread->cancelflags |= PTHREAD_CANCELLING;
|
||||
break;
|
||||
|
||||
case PS_SPINBLOCK:
|
||||
case PS_FDR_WAIT:
|
||||
case PS_FDW_WAIT:
|
||||
case PS_POLL_WAIT:
|
||||
case PS_SELECT_WAIT:
|
||||
/* Remove these threads from the work queue: */
|
||||
if ((pthread->flags & PTHREAD_FLAGS_IN_WORKQ)
|
||||
!= 0)
|
||||
PTHREAD_WORKQ_REMOVE(pthread);
|
||||
/* Fall through: */
|
||||
case PS_SIGTHREAD:
|
||||
case PS_SLEEP_WAIT:
|
||||
case PS_WAIT_WAIT:
|
||||
case PS_SIGSUSPEND:
|
||||
case PS_SIGWAIT:
|
||||
case PS_SUSPENDED:
|
||||
/* Interrupt and resume: */
|
||||
pthread->interrupted = 1;
|
||||
pthread->cancelflags |= PTHREAD_CANCELLING;
|
||||
PTHREAD_NEW_STATE(pthread,PS_RUNNING);
|
||||
break;
|
||||
|
||||
case PS_MUTEX_WAIT:
|
||||
case PS_COND_WAIT:
|
||||
case PS_FDLR_WAIT:
|
||||
case PS_FDLW_WAIT:
|
||||
case PS_FILE_WAIT:
|
||||
case PS_JOIN:
|
||||
/*
|
||||
* Threads in these states may be in queues.
|
||||
* In order to preserve queue integrity, the
|
||||
* cancelled thread must remove itself from the
|
||||
* queue. Mark the thread as interrupted and
|
||||
* needing cancellation, and set the state to
|
||||
* running. When the thread resumes, it will
|
||||
* exit after removing itself from the queue.
|
||||
*/
|
||||
pthread->interrupted = 1;
|
||||
pthread->cancelflags |= PTHREAD_CANCEL_NEEDED;
|
||||
PTHREAD_NEW_STATE(pthread,PS_RUNNING);
|
||||
break;
|
||||
|
||||
case PS_DEAD:
|
||||
case PS_DEADLOCK:
|
||||
case PS_STATE_MAX:
|
||||
/* Ignore - only here to silence -Wall: */
|
||||
break;
|
||||
}
|
||||
/* Unprotect the scheduling queues: */
|
||||
_thread_kern_sig_undefer();
|
||||
|
||||
ret = 0;
|
||||
}
|
||||
return (ret);
|
||||
}
|
||||
|
||||
int
|
||||
pthread_setcancelstate(int state, int *oldstate)
|
||||
{
|
||||
int ostate;
|
||||
int ret;
|
||||
|
||||
ostate = _thread_run->cancelflags & PTHREAD_CANCEL_DISABLE;
|
||||
|
||||
switch (state) {
|
||||
case PTHREAD_CANCEL_ENABLE:
|
||||
if (oldstate != NULL)
|
||||
*oldstate = ostate;
|
||||
_thread_run->cancelflags &= PTHREAD_CANCEL_ENABLE;
|
||||
if ((_thread_run->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) != 0)
|
||||
pthread_testcancel();
|
||||
ret = 0;
|
||||
break;
|
||||
case PTHREAD_CANCEL_DISABLE:
|
||||
if (oldstate != NULL)
|
||||
*oldstate = ostate;
|
||||
_thread_run->cancelflags |= PTHREAD_CANCEL_DISABLE;
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
ret = EINVAL;
|
||||
}
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
int
|
||||
pthread_setcanceltype(int type, int *oldtype)
|
||||
{
|
||||
int otype;
|
||||
int ret;
|
||||
|
||||
otype = _thread_run->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS;
|
||||
switch (type) {
|
||||
case PTHREAD_CANCEL_ASYNCHRONOUS:
|
||||
if (oldtype != NULL)
|
||||
*oldtype = otype;
|
||||
_thread_run->cancelflags |= PTHREAD_CANCEL_ASYNCHRONOUS;
|
||||
pthread_testcancel();
|
||||
ret = 0;
|
||||
break;
|
||||
case PTHREAD_CANCEL_DEFERRED:
|
||||
if (oldtype != NULL)
|
||||
*oldtype = otype;
|
||||
_thread_run->cancelflags &= ~PTHREAD_CANCEL_ASYNCHRONOUS;
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
ret = EINVAL;
|
||||
}
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
void
|
||||
pthread_testcancel(void)
|
||||
{
|
||||
|
||||
if (((_thread_run->cancelflags & PTHREAD_CANCEL_DISABLE) == 0) &&
|
||||
((_thread_run->cancelflags & PTHREAD_CANCELLING) != 0)) {
|
||||
/*
|
||||
* It is possible for this thread to be swapped out
|
||||
* while performing cancellation; do not allow it
|
||||
* to be cancelled again.
|
||||
*/
|
||||
_thread_run->cancelflags &= ~PTHREAD_CANCELLING;
|
||||
_thread_exit_cleanup();
|
||||
pthread_exit(PTHREAD_CANCELED);
|
||||
PANIC("cancel");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
_thread_enter_cancellation_point(void)
|
||||
{
|
||||
|
||||
/* Look for a cancellation before we block: */
|
||||
pthread_testcancel();
|
||||
_thread_run->cancelflags |= PTHREAD_AT_CANCEL_POINT;
|
||||
}
|
||||
|
||||
void
|
||||
_thread_leave_cancellation_point(void)
|
||||
{
|
||||
|
||||
_thread_run->cancelflags &= ~PTHREAD_AT_CANCEL_POINT;
|
||||
/* Look for a cancellation after we unblock: */
|
||||
pthread_testcancel();
|
||||
}
|
@ -45,10 +45,11 @@ close(int fd)
|
||||
{
|
||||
int flags;
|
||||
int ret;
|
||||
int status;
|
||||
struct stat sb;
|
||||
struct fd_table_entry *entry;
|
||||
|
||||
_thread_enter_cancellation_point();
|
||||
|
||||
if ((fd == _thread_kern_pipe[0]) || (fd == _thread_kern_pipe[1])) {
|
||||
/*
|
||||
* Don't allow silly programs to close the kernel pipe.
|
||||
@ -98,6 +99,7 @@ close(int fd)
|
||||
/* Close the file descriptor: */
|
||||
ret = _thread_sys_close(fd);
|
||||
}
|
||||
_thread_leave_cancellation_point();
|
||||
return (ret);
|
||||
}
|
||||
#endif
|
||||
|
@ -157,8 +157,7 @@ pthread_cond_destroy(pthread_cond_t * cond)
|
||||
int
|
||||
pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
|
||||
{
|
||||
int rval = 0;
|
||||
int status;
|
||||
int rval = 0;
|
||||
|
||||
if (cond == NULL)
|
||||
rval = EINVAL;
|
||||
@ -169,6 +168,9 @@ pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
|
||||
*/
|
||||
else if (*cond != NULL ||
|
||||
(rval = pthread_cond_init(cond,NULL)) == 0) {
|
||||
|
||||
_thread_enter_cancellation_point();
|
||||
|
||||
/* Lock the condition variable structure: */
|
||||
_SPINLOCK(&(*cond)->lock);
|
||||
|
||||
@ -193,8 +195,9 @@ pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
|
||||
/* Return invalid argument error: */
|
||||
rval = EINVAL;
|
||||
} else {
|
||||
/* Reset the timeout flag: */
|
||||
/* Reset the timeout and interrupted flags: */
|
||||
_thread_run->timeout = 0;
|
||||
_thread_run->interrupted = 0;
|
||||
|
||||
/*
|
||||
* Queue the running thread for the condition
|
||||
@ -233,7 +236,28 @@ pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
|
||||
_thread_kern_sched_state_unlock(PS_COND_WAIT,
|
||||
&(*cond)->lock, __FILE__, __LINE__);
|
||||
|
||||
/* Lock the mutex: */
|
||||
if (_thread_run->interrupted != 0) {
|
||||
/*
|
||||
* Lock the condition variable
|
||||
* while removing the thread.
|
||||
*/
|
||||
_SPINLOCK(&(*cond)->lock);
|
||||
|
||||
cond_queue_remove(*cond,
|
||||
_thread_run);
|
||||
|
||||
/* Check for no more waiters: */
|
||||
if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
|
||||
(*cond)->c_mutex = NULL;
|
||||
|
||||
_SPINUNLOCK(&(*cond)->lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Note that even though this thread may have
|
||||
* been canceled, POSIX requires that the mutex
|
||||
* be reaquired prior to cancellation.
|
||||
*/
|
||||
rval = _mutex_cv_lock(mutex);
|
||||
}
|
||||
}
|
||||
@ -248,6 +272,13 @@ pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
|
||||
rval = EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((_thread_run->cancelflags & PTHREAD_CANCEL_NEEDED) != 0) {
|
||||
_thread_exit_cleanup();
|
||||
pthread_exit(PTHREAD_CANCELED);
|
||||
}
|
||||
|
||||
_thread_leave_cancellation_point();
|
||||
}
|
||||
|
||||
/* Return the completion status: */
|
||||
@ -258,8 +289,7 @@ int
|
||||
pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
|
||||
const struct timespec * abstime)
|
||||
{
|
||||
int rval = 0;
|
||||
int status;
|
||||
int rval = 0;
|
||||
|
||||
if (cond == NULL || abstime == NULL)
|
||||
rval = EINVAL;
|
||||
@ -276,6 +306,9 @@ pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
|
||||
*/
|
||||
if (*cond != NULL ||
|
||||
(rval = pthread_cond_init(cond,NULL)) == 0) {
|
||||
|
||||
_thread_enter_cancellation_point();
|
||||
|
||||
/* Lock the condition variable structure: */
|
||||
_SPINLOCK(&(*cond)->lock);
|
||||
|
||||
@ -306,8 +339,9 @@ pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
|
||||
_thread_run->wakeup_time.tv_nsec =
|
||||
abstime->tv_nsec;
|
||||
|
||||
/* Reset the timeout flag: */
|
||||
/* Reset the timeout and interrupted flags: */
|
||||
_thread_run->timeout = 0;
|
||||
_thread_run->interrupted = 0;
|
||||
|
||||
/*
|
||||
* Queue the running thread for the condition
|
||||
@ -341,12 +375,16 @@ pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
|
||||
_thread_kern_sched_state_unlock(PS_COND_WAIT,
|
||||
&(*cond)->lock, __FILE__, __LINE__);
|
||||
|
||||
/* Check if the wait timedout: */
|
||||
if (_thread_run->timeout == 0) {
|
||||
/*
|
||||
* Check if the wait timedout or was
|
||||
* interrupted (canceled):
|
||||
*/
|
||||
if ((_thread_run->timeout == 0) &&
|
||||
(_thread_run->interrupted == 0)) {
|
||||
/* Lock the mutex: */
|
||||
rval = _mutex_cv_lock(mutex);
|
||||
}
|
||||
else {
|
||||
|
||||
} else {
|
||||
/* Lock the condition variable structure: */
|
||||
_SPINLOCK(&(*cond)->lock);
|
||||
|
||||
@ -369,8 +407,12 @@ pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
|
||||
rval = ETIMEDOUT;
|
||||
|
||||
/*
|
||||
* Lock the mutex and ignore
|
||||
* any errors:
|
||||
* Lock the mutex and ignore any
|
||||
* errors. Note that even though
|
||||
* this thread may have been
|
||||
* canceled, POSIX requires that
|
||||
* the mutex be reaquired prior
|
||||
* to cancellation.
|
||||
*/
|
||||
(void)_mutex_cv_lock(mutex);
|
||||
}
|
||||
@ -388,6 +430,12 @@ pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
|
||||
break;
|
||||
}
|
||||
|
||||
if ((_thread_run->cancelflags & PTHREAD_CANCEL_NEEDED) != 0) {
|
||||
_thread_exit_cleanup();
|
||||
pthread_exit(PTHREAD_CANCELED);
|
||||
}
|
||||
|
||||
_thread_leave_cancellation_point();
|
||||
}
|
||||
|
||||
/* Return the completion status: */
|
||||
@ -416,16 +464,7 @@ pthread_cond_signal(pthread_cond_t * cond)
|
||||
switch ((*cond)->c_type) {
|
||||
/* Fast condition variable: */
|
||||
case COND_TYPE_FAST:
|
||||
/*
|
||||
* Enter a loop to dequeue threads from the condition
|
||||
* queue until we find one that hasn't previously
|
||||
* timed out.
|
||||
*/
|
||||
while (((pthread = cond_queue_deq(*cond)) != NULL) &&
|
||||
(pthread->timeout != 0)) {
|
||||
}
|
||||
|
||||
if (pthread != NULL)
|
||||
if ((pthread = cond_queue_deq(*cond)) != NULL)
|
||||
/* Allow the thread to run: */
|
||||
PTHREAD_NEW_STATE(pthread,PS_RUNNING);
|
||||
|
||||
@ -482,12 +521,7 @@ pthread_cond_broadcast(pthread_cond_t * cond)
|
||||
* condition queue:
|
||||
*/
|
||||
while ((pthread = cond_queue_deq(*cond)) != NULL) {
|
||||
/*
|
||||
* The thread is already running if the
|
||||
* timeout flag is set.
|
||||
*/
|
||||
if (pthread->timeout == 0)
|
||||
PTHREAD_NEW_STATE(pthread,PS_RUNNING);
|
||||
PTHREAD_NEW_STATE(pthread,PS_RUNNING);
|
||||
}
|
||||
|
||||
/* There are no more waiting threads: */
|
||||
@ -524,9 +558,17 @@ cond_queue_deq(pthread_cond_t cond)
|
||||
{
|
||||
pthread_t pthread;
|
||||
|
||||
if ((pthread = TAILQ_FIRST(&cond->c_queue)) != NULL) {
|
||||
while ((pthread = TAILQ_FIRST(&cond->c_queue)) != NULL) {
|
||||
TAILQ_REMOVE(&cond->c_queue, pthread, qe);
|
||||
pthread->flags &= ~PTHREAD_FLAGS_IN_CONDQ;
|
||||
if ((pthread->timeout == 0) && (pthread->interrupted == 0))
|
||||
/*
|
||||
* Only exit the loop when we find a thread
|
||||
* that hasn't timed out or been canceled;
|
||||
* those threads are already running and don't
|
||||
* need their run state changed.
|
||||
*/
|
||||
break;
|
||||
}
|
||||
|
||||
return(pthread);
|
||||
|
@ -50,9 +50,7 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr,
|
||||
void *(*start_routine) (void *), void *arg)
|
||||
{
|
||||
int f_gc = 0;
|
||||
int i;
|
||||
int ret = 0;
|
||||
int status;
|
||||
pthread_t gc_thread;
|
||||
pthread_t new_thread;
|
||||
pthread_attr_t pattr;
|
||||
@ -166,6 +164,9 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr,
|
||||
new_thread->start_routine = start_routine;
|
||||
new_thread->arg = arg;
|
||||
|
||||
new_thread->cancelflags = PTHREAD_CANCEL_ENABLE |
|
||||
PTHREAD_CANCEL_DEFERRED;
|
||||
|
||||
/*
|
||||
* Write a magic value to the thread structure
|
||||
* to help identify valid ones:
|
||||
|
@ -34,6 +34,8 @@
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifdef _THREAD_SAFE
|
||||
#include <pthread.h>
|
||||
@ -101,17 +103,45 @@ _thread_exit(char *fname, int lineno, char *string)
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Only called when a thread is cancelled. It may be more useful
|
||||
* to call it from pthread_exit() if other ways of asynchronous or
|
||||
* abnormal thread termination can be found.
|
||||
*/
|
||||
void
|
||||
_thread_exit_cleanup(void)
|
||||
{
|
||||
/*
|
||||
* POSIX states that cancellation/termination of a thread should
|
||||
* not release any visible resources (such as mutexes) and that
|
||||
* it is the applications responsibility. Resources that are
|
||||
* internal to the threads library, including file and fd locks,
|
||||
* are not visible to the application and need to be released.
|
||||
*/
|
||||
/* Unlock all owned fd locks: */
|
||||
_thread_fd_unlock_owned(_thread_run);
|
||||
|
||||
/* Unlock all owned file locks: */
|
||||
_funlock_owned(_thread_run);
|
||||
|
||||
/* Unlock all private mutexes: */
|
||||
_mutex_unlock_private(_thread_run);
|
||||
|
||||
/*
|
||||
* This still isn't quite correct because we don't account
|
||||
* for held spinlocks (see libc/stdlib/malloc.c).
|
||||
*/
|
||||
}
|
||||
|
||||
void
|
||||
pthread_exit(void *status)
|
||||
{
|
||||
int sig;
|
||||
long l;
|
||||
pthread_t pthread;
|
||||
pthread_t pthread;
|
||||
|
||||
/* Check if this thread is already in the process of exiting: */
|
||||
if ((_thread_run->flags & PTHREAD_EXITING) != 0) {
|
||||
char msg[128];
|
||||
snprintf(msg,"Thread %p has called pthread_exit() from a destructor. POSIX 1003.1 1996 s16.2.5.2 does not allow this!",_thread_run);
|
||||
snprintf(msg, sizeof(msg), "Thread %p has called pthread_exit() from a destructor. POSIX 1003.1 1996 s16.2.5.2 does not allow this!",_thread_run);
|
||||
PANIC(msg);
|
||||
}
|
||||
|
||||
@ -134,7 +164,7 @@ pthread_exit(void *status)
|
||||
_thread_cleanupspecific();
|
||||
}
|
||||
|
||||
/* Free thread-specific poll_data structure, if allocated */
|
||||
/* Free thread-specific poll_data structure, if allocated: */
|
||||
if (_thread_run->poll_data.fds != NULL) {
|
||||
free(_thread_run->poll_data.fds);
|
||||
_thread_run->poll_data.fds = NULL;
|
||||
|
25
lib/libc_r/uthread/uthread_fchflags.c
Normal file
25
lib/libc_r/uthread/uthread_fchflags.c
Normal file
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* David Leonard <d@openbsd.org>, 1999. Public Domain.
|
||||
*
|
||||
* $OpenBSD: uthread_fchflags.c,v 1.1 1999/01/08 05:42:18 d Exp $
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#ifdef _THREAD_SAFE
|
||||
#include <pthread.h>
|
||||
#include "pthread_private.h"
|
||||
|
||||
int
|
||||
fchflags(int fd, u_long flags)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if ((ret = _FD_LOCK(fd, FD_WRITE, NULL)) == 0) {
|
||||
ret = _thread_sys_fchflags(fd, flags);
|
||||
_FD_UNLOCK(fd, FD_WRITE);
|
||||
}
|
||||
return (ret);
|
||||
}
|
||||
#endif
|
@ -47,6 +47,8 @@ fcntl(int fd, int cmd,...)
|
||||
int ret;
|
||||
va_list ap;
|
||||
|
||||
_thread_enter_cancellation_point();
|
||||
|
||||
/* Lock the file descriptor: */
|
||||
if ((ret = _FD_LOCK(fd, FD_RDWR, NULL)) == 0) {
|
||||
/* Initialise the variable argument list: */
|
||||
@ -135,6 +137,7 @@ fcntl(int fd, int cmd,...)
|
||||
/* Unlock the file descriptor: */
|
||||
_FD_UNLOCK(fd, FD_RDWR);
|
||||
}
|
||||
_thread_leave_cancellation_point();
|
||||
|
||||
/* Return the completion status: */
|
||||
return (ret);
|
||||
|
@ -40,9 +40,29 @@
|
||||
#include <pthread.h>
|
||||
#include "pthread_private.h"
|
||||
|
||||
#define FDQ_INSERT(q,p) \
|
||||
do { \
|
||||
TAILQ_INSERT_TAIL(q,p,qe); \
|
||||
p->flags |= PTHREAD_FLAGS_IN_FDQ; \
|
||||
} while (0)
|
||||
|
||||
#define FDQ_REMOVE(q,p) \
|
||||
do { \
|
||||
if ((p->flags & PTHREAD_FLAGS_IN_FDQ) != 0) { \
|
||||
TAILQ_REMOVE(q,p,qe); \
|
||||
p->flags &= ~PTHREAD_FLAGS_IN_FDQ; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
/* Static variables: */
|
||||
static spinlock_t fd_table_lock = _SPINLOCK_INITIALIZER;
|
||||
|
||||
/* Prototypes: */
|
||||
static inline pthread_t fd_next_reader(int fd);
|
||||
static inline pthread_t fd_next_writer(int fd);
|
||||
|
||||
|
||||
/*
|
||||
* This function *must* return -1 and set the thread specific errno
|
||||
* as a system call. This is because the error return from this
|
||||
@ -201,11 +221,11 @@ _thread_fd_unlock(int fd, int lock_type)
|
||||
* Get the next thread in the queue for a
|
||||
* read lock on this file descriptor:
|
||||
*/
|
||||
else if ((_thread_fd_table[fd]->r_owner = TAILQ_FIRST(&_thread_fd_table[fd]->r_queue)) == NULL) {
|
||||
else if ((_thread_fd_table[fd]->r_owner = fd_next_reader(fd)) == NULL) {
|
||||
} else {
|
||||
/* Remove this thread from the queue: */
|
||||
TAILQ_REMOVE(&_thread_fd_table[fd]->r_queue,
|
||||
_thread_fd_table[fd]->r_owner, qe);
|
||||
FDQ_REMOVE(&_thread_fd_table[fd]->r_queue,
|
||||
_thread_fd_table[fd]->r_owner);
|
||||
|
||||
/*
|
||||
* Set the state of the new owner of
|
||||
@ -243,11 +263,11 @@ _thread_fd_unlock(int fd, int lock_type)
|
||||
* Get the next thread in the queue for a
|
||||
* write lock on this file descriptor:
|
||||
*/
|
||||
else if ((_thread_fd_table[fd]->w_owner = TAILQ_FIRST(&_thread_fd_table[fd]->w_queue)) == NULL) {
|
||||
else if ((_thread_fd_table[fd]->w_owner = fd_next_writer(fd)) == NULL) {
|
||||
} else {
|
||||
/* Remove this thread from the queue: */
|
||||
TAILQ_REMOVE(&_thread_fd_table[fd]->w_queue,
|
||||
_thread_fd_table[fd]->w_owner, qe);
|
||||
FDQ_REMOVE(&_thread_fd_table[fd]->w_queue,
|
||||
_thread_fd_table[fd]->w_owner);
|
||||
|
||||
/*
|
||||
* Set the state of the new owner of
|
||||
@ -290,6 +310,9 @@ _thread_fd_lock(int fd, int lock_type, struct timespec * timeout)
|
||||
* entry:
|
||||
*/
|
||||
if ((ret = _thread_fd_table_init(fd)) == 0) {
|
||||
/* Clear the interrupted flag: */
|
||||
_thread_run->interrupted = 0;
|
||||
|
||||
/*
|
||||
* Lock the file descriptor table entry to prevent
|
||||
* other threads for clashing with the current
|
||||
@ -300,10 +323,10 @@ _thread_fd_lock(int fd, int lock_type, struct timespec * timeout)
|
||||
/* Check the file descriptor and lock types: */
|
||||
if (lock_type == FD_READ || lock_type == FD_RDWR) {
|
||||
/*
|
||||
* Enter a loop to wait for the file descriptor to be
|
||||
* locked for read for the current thread:
|
||||
* Wait for the file descriptor to be locked
|
||||
* for read for the current thread:
|
||||
*/
|
||||
while (_thread_fd_table[fd]->r_owner != _thread_run) {
|
||||
if (_thread_fd_table[fd]->r_owner != _thread_run) {
|
||||
/*
|
||||
* Check if the file descriptor is locked by
|
||||
* another thread:
|
||||
@ -315,7 +338,7 @@ _thread_fd_lock(int fd, int lock_type, struct timespec * timeout)
|
||||
* queue of threads waiting for a
|
||||
* read lock on this file descriptor:
|
||||
*/
|
||||
TAILQ_INSERT_TAIL(&_thread_fd_table[fd]->r_queue, _thread_run, qe);
|
||||
FDQ_INSERT(&_thread_fd_table[fd]->r_queue, _thread_run);
|
||||
|
||||
/*
|
||||
* Save the file descriptor details
|
||||
@ -350,6 +373,10 @@ _thread_fd_lock(int fd, int lock_type, struct timespec * timeout)
|
||||
*/
|
||||
_SPINLOCK(&_thread_fd_table[fd]->lock);
|
||||
|
||||
if (_thread_run->interrupted != 0) {
|
||||
FDQ_REMOVE(&_thread_fd_table[fd]->r_queue,
|
||||
_thread_run);
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* The running thread now owns the
|
||||
@ -365,8 +392,9 @@ _thread_fd_lock(int fd, int lock_type, struct timespec * timeout)
|
||||
}
|
||||
}
|
||||
|
||||
/* Increment the read lock count: */
|
||||
_thread_fd_table[fd]->r_lockcount++;
|
||||
if (_thread_fd_table[fd]->r_owner == _thread_run)
|
||||
/* Increment the read lock count: */
|
||||
_thread_fd_table[fd]->r_lockcount++;
|
||||
}
|
||||
|
||||
/* Check the file descriptor and lock types: */
|
||||
@ -388,7 +416,7 @@ _thread_fd_lock(int fd, int lock_type, struct timespec * timeout)
|
||||
* write lock on this file
|
||||
* descriptor:
|
||||
*/
|
||||
TAILQ_INSERT_TAIL(&_thread_fd_table[fd]->w_queue, _thread_run, qe);
|
||||
FDQ_INSERT(&_thread_fd_table[fd]->w_queue, _thread_run);
|
||||
|
||||
/*
|
||||
* Save the file descriptor details
|
||||
@ -421,6 +449,11 @@ _thread_fd_lock(int fd, int lock_type, struct timespec * timeout)
|
||||
* table entry again:
|
||||
*/
|
||||
_SPINLOCK(&_thread_fd_table[fd]->lock);
|
||||
|
||||
if (_thread_run->interrupted != 0) {
|
||||
FDQ_REMOVE(&_thread_fd_table[fd]->w_queue,
|
||||
_thread_run);
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* The running thread now owns the
|
||||
@ -437,12 +470,23 @@ _thread_fd_lock(int fd, int lock_type, struct timespec * timeout)
|
||||
}
|
||||
}
|
||||
|
||||
/* Increment the write lock count: */
|
||||
_thread_fd_table[fd]->w_lockcount++;
|
||||
if (_thread_fd_table[fd]->w_owner == _thread_run)
|
||||
/* Increment the write lock count: */
|
||||
_thread_fd_table[fd]->w_lockcount++;
|
||||
}
|
||||
|
||||
/* Unlock the file descriptor table entry: */
|
||||
_SPINUNLOCK(&_thread_fd_table[fd]->lock);
|
||||
|
||||
if (_thread_run->interrupted != 0) {
|
||||
if ((_thread_run->cancelflags & PTHREAD_CANCEL_NEEDED) == 0) {
|
||||
ret = -1;
|
||||
errno = EINTR;
|
||||
} else {
|
||||
_thread_exit_cleanup();
|
||||
pthread_exit(PTHREAD_CANCELED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Return the completion status: */
|
||||
@ -492,11 +536,11 @@ _thread_fd_unlock_debug(int fd, int lock_type, char *fname, int lineno)
|
||||
* Get the next thread in the queue for a
|
||||
* read lock on this file descriptor:
|
||||
*/
|
||||
else if ((_thread_fd_table[fd]->r_owner = TAILQ_FIRST(&_thread_fd_table[fd]->r_queue)) == NULL) {
|
||||
else if ((_thread_fd_table[fd]->r_owner = fd_next_reader(fd)) == NULL) {
|
||||
} else {
|
||||
/* Remove this thread from the queue: */
|
||||
TAILQ_REMOVE(&_thread_fd_table[fd]->r_queue,
|
||||
_thread_fd_table[fd]->r_owner, qe);
|
||||
FDQ_REMOVE(&_thread_fd_table[fd]->r_queue,
|
||||
_thread_fd_table[fd]->r_owner);
|
||||
|
||||
/*
|
||||
* Set the state of the new owner of
|
||||
@ -534,11 +578,11 @@ _thread_fd_unlock_debug(int fd, int lock_type, char *fname, int lineno)
|
||||
* Get the next thread in the queue for a
|
||||
* write lock on this file descriptor:
|
||||
*/
|
||||
else if ((_thread_fd_table[fd]->w_owner = TAILQ_FIRST(&_thread_fd_table[fd]->w_queue)) == NULL) {
|
||||
else if ((_thread_fd_table[fd]->w_owner = fd_next_writer(fd)) == NULL) {
|
||||
} else {
|
||||
/* Remove this thread from the queue: */
|
||||
TAILQ_REMOVE(&_thread_fd_table[fd]->w_queue,
|
||||
_thread_fd_table[fd]->w_owner, qe);
|
||||
FDQ_REMOVE(&_thread_fd_table[fd]->w_queue,
|
||||
_thread_fd_table[fd]->w_owner);
|
||||
|
||||
/*
|
||||
* Set the state of the new owner of
|
||||
@ -582,6 +626,9 @@ _thread_fd_lock_debug(int fd, int lock_type, struct timespec * timeout,
|
||||
* entry:
|
||||
*/
|
||||
if ((ret = _thread_fd_table_init(fd)) == 0) {
|
||||
/* Clear the interrupted flag: */
|
||||
_thread_run->interrupted = 0;
|
||||
|
||||
/*
|
||||
* Lock the file descriptor table entry to prevent
|
||||
* other threads for clashing with the current
|
||||
@ -607,7 +654,7 @@ _thread_fd_lock_debug(int fd, int lock_type, struct timespec * timeout,
|
||||
* queue of threads waiting for a
|
||||
* read lock on this file descriptor:
|
||||
*/
|
||||
TAILQ_INSERT_TAIL(&_thread_fd_table[fd]->r_queue, _thread_run, qe);
|
||||
FDQ_INSERT(&_thread_fd_table[fd]->r_queue, _thread_run);
|
||||
|
||||
/*
|
||||
* Save the file descriptor details
|
||||
@ -689,7 +736,7 @@ _thread_fd_lock_debug(int fd, int lock_type, struct timespec * timeout,
|
||||
* write lock on this file
|
||||
* descriptor:
|
||||
*/
|
||||
TAILQ_INSERT_TAIL(&_thread_fd_table[fd]->w_queue, _thread_run, qe);
|
||||
FDQ_INSERT(&_thread_fd_table[fd]->w_queue, _thread_run);
|
||||
|
||||
/*
|
||||
* Save the file descriptor details
|
||||
@ -753,9 +800,132 @@ _thread_fd_lock_debug(int fd, int lock_type, struct timespec * timeout,
|
||||
|
||||
/* Unlock the file descriptor table entry: */
|
||||
_SPINUNLOCK(&_thread_fd_table[fd]->lock);
|
||||
|
||||
if (_thread_run->interrupted != 0) {
|
||||
if ((_thread_run->cancelflags & PTHREAD_CANCEL_NEEDED) == 0) {
|
||||
ret = -1;
|
||||
errno = EINTR;
|
||||
} else {
|
||||
_thread_exit_cleanup();
|
||||
pthread_exit(PTHREAD_CANCELED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Return the completion status: */
|
||||
return (ret);
|
||||
}
|
||||
|
||||
void
|
||||
_thread_fd_unlock_owned(pthread_t pthread)
|
||||
{
|
||||
int fd;
|
||||
|
||||
for (fd = 0; fd < _thread_dtablesize; fd++) {
|
||||
if ((_thread_fd_table[fd] != NULL) &&
|
||||
((_thread_fd_table[fd]->r_owner == pthread) ||
|
||||
(_thread_fd_table[fd]->w_owner == pthread))) {
|
||||
/*
|
||||
* Defer signals to protect the scheduling queues
|
||||
* from access by the signal handler:
|
||||
*/
|
||||
_thread_kern_sig_defer();
|
||||
|
||||
/*
|
||||
* Lock the file descriptor table entry to prevent
|
||||
* other threads for clashing with the current
|
||||
* thread's accesses:
|
||||
*/
|
||||
_SPINLOCK(&_thread_fd_table[fd]->lock);
|
||||
|
||||
/* Check if the thread owns the read lock: */
|
||||
if (_thread_fd_table[fd]->r_owner == pthread) {
|
||||
/* Clear the read lock count: */
|
||||
_thread_fd_table[fd]->r_lockcount = 0;
|
||||
|
||||
/*
|
||||
* Get the next thread in the queue for a
|
||||
* read lock on this file descriptor:
|
||||
*/
|
||||
if ((_thread_fd_table[fd]->r_owner = fd_next_reader(fd)) != NULL) {
|
||||
/* Remove this thread from the queue: */
|
||||
FDQ_REMOVE(&_thread_fd_table[fd]->r_queue,
|
||||
_thread_fd_table[fd]->r_owner);
|
||||
|
||||
/*
|
||||
* Set the state of the new owner of
|
||||
* the thread to running:
|
||||
*/
|
||||
PTHREAD_NEW_STATE(_thread_fd_table[fd]->r_owner,PS_RUNNING);
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if the thread owns the write lock: */
|
||||
if (_thread_fd_table[fd]->w_owner == pthread) {
|
||||
/* Clear the write lock count: */
|
||||
_thread_fd_table[fd]->w_lockcount = 0;
|
||||
|
||||
/*
|
||||
* Get the next thread in the queue for a
|
||||
* write lock on this file descriptor:
|
||||
*/
|
||||
if ((_thread_fd_table[fd]->w_owner = fd_next_writer(fd)) != NULL) {
|
||||
/* Remove this thread from the queue: */
|
||||
FDQ_REMOVE(&_thread_fd_table[fd]->w_queue,
|
||||
_thread_fd_table[fd]->w_owner);
|
||||
|
||||
/*
|
||||
* Set the state of the new owner of
|
||||
* the thread to running:
|
||||
*/
|
||||
PTHREAD_NEW_STATE(_thread_fd_table[fd]->w_owner,PS_RUNNING);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* Unlock the file descriptor table entry: */
|
||||
_SPINUNLOCK(&_thread_fd_table[fd]->lock);
|
||||
|
||||
/*
|
||||
* Undefer and handle pending signals, yielding if
|
||||
* necessary.
|
||||
*/
|
||||
_thread_kern_sig_undefer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline pthread_t
|
||||
fd_next_reader(int fd)
|
||||
{
|
||||
pthread_t pthread;
|
||||
|
||||
while (((pthread = TAILQ_FIRST(&_thread_fd_table[fd]->r_queue)) != NULL) &&
|
||||
(pthread->interrupted != 0)) {
|
||||
/*
|
||||
* This thread has either been interrupted by a signal or
|
||||
* it has been canceled. Remove it from the queue.
|
||||
*/
|
||||
FDQ_REMOVE(&_thread_fd_table[fd]->r_queue, pthread);
|
||||
}
|
||||
|
||||
return (pthread);
|
||||
}
|
||||
|
||||
static inline pthread_t
|
||||
fd_next_writer(int fd)
|
||||
{
|
||||
pthread_t pthread;
|
||||
|
||||
while (((pthread = TAILQ_FIRST(&_thread_fd_table[fd]->w_queue)) != NULL) &&
|
||||
(pthread->interrupted != 0)) {
|
||||
/*
|
||||
* This thread has either been interrupted by a signal or
|
||||
* it has been canceled. Remove it from the queue.
|
||||
*/
|
||||
FDQ_REMOVE(&_thread_fd_table[fd]->w_queue, pthread);
|
||||
}
|
||||
|
||||
return (pthread);
|
||||
}
|
||||
#endif
|
||||
|
@ -225,18 +225,41 @@ _flockfile_debug(FILE * fp, char *fname, int lineno)
|
||||
/* Unlock the hash table: */
|
||||
_SPINUNLOCK(&hash_lock);
|
||||
} else {
|
||||
/* Clear the interrupted flag: */
|
||||
_thread_run->interrupted = 0;
|
||||
|
||||
/*
|
||||
* Prevent being context switched out while
|
||||
* adding this thread to the file lock queue.
|
||||
*/
|
||||
_thread_kern_sig_defer();
|
||||
|
||||
/*
|
||||
* The file is locked for another thread.
|
||||
* Append this thread to the queue of
|
||||
* threads waiting on the lock.
|
||||
*/
|
||||
TAILQ_INSERT_TAIL(&p->l_head,_thread_run,qe);
|
||||
_thread_run->flags |= PTHREAD_FLAGS_IN_FILEQ;
|
||||
|
||||
/* Unlock the hash table: */
|
||||
_SPINUNLOCK(&hash_lock);
|
||||
|
||||
/* Wait on the FILE lock: */
|
||||
_thread_kern_sched_state(PS_FILE_WAIT, fname, lineno);
|
||||
|
||||
if ((_thread_run->flags & PTHREAD_FLAGS_IN_FILEQ) != 0) {
|
||||
TAILQ_REMOVE(&p->l_head,_thread_run,qe);
|
||||
_thread_run->flags &= ~PTHREAD_FLAGS_IN_FILEQ;
|
||||
}
|
||||
|
||||
_thread_kern_sig_undefer();
|
||||
|
||||
if (((_thread_run->cancelflags & PTHREAD_CANCEL_NEEDED) != 0) &&
|
||||
(_thread_run->cancelflags & PTHREAD_CANCEL_DISABLE) != 0) {
|
||||
_thread_exit_cleanup();
|
||||
pthread_exit(PTHREAD_CANCELED);
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
@ -304,7 +327,6 @@ _ftrylockfile(FILE * fp)
|
||||
void
|
||||
_funlockfile(FILE * fp)
|
||||
{
|
||||
int status;
|
||||
int idx = file_idx(fp);
|
||||
struct file_lock *p;
|
||||
|
||||
@ -344,18 +366,27 @@ _funlockfile(FILE * fp)
|
||||
p->count = 0;
|
||||
|
||||
/* Get the new owner of the lock: */
|
||||
if ((p->owner = TAILQ_FIRST(&p->l_head)) != NULL) {
|
||||
while ((p->owner = TAILQ_FIRST(&p->l_head)) != NULL) {
|
||||
/* Pop the thread off the queue: */
|
||||
TAILQ_REMOVE(&p->l_head,p->owner,qe);
|
||||
p->owner->flags &= ~PTHREAD_FLAGS_IN_FILEQ;
|
||||
|
||||
/*
|
||||
* This is the first lock for the new
|
||||
* owner:
|
||||
*/
|
||||
p->count = 1;
|
||||
if (p->owner->interrupted == 0) {
|
||||
/*
|
||||
* This is the first lock for
|
||||
* the new owner:
|
||||
*/
|
||||
p->count = 1;
|
||||
|
||||
/* Allow the new owner to run: */
|
||||
PTHREAD_NEW_STATE(p->owner,PS_RUNNING);
|
||||
/* Allow the new owner to run: */
|
||||
PTHREAD_NEW_STATE(p->owner,PS_RUNNING);
|
||||
|
||||
/* End the loop when we find a
|
||||
* thread that hasn't been
|
||||
* cancelled or interrupted;
|
||||
*/
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -372,4 +403,72 @@ _funlockfile(FILE * fp)
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
_funlock_owned(pthread_t pthread)
|
||||
{
|
||||
int idx;
|
||||
struct file_lock *p, *next_p;
|
||||
|
||||
/*
|
||||
* Defer signals to protect the scheduling queues from
|
||||
* access by the signal handler:
|
||||
*/
|
||||
_thread_kern_sig_defer();
|
||||
|
||||
/* Lock the hash table: */
|
||||
_SPINLOCK(&hash_lock);
|
||||
|
||||
for (idx = 0; idx < NUM_HEADS; idx++) {
|
||||
/* Check the static file lock first: */
|
||||
p = &flh[idx].fl;
|
||||
next_p = LIST_FIRST(&flh[idx].head);
|
||||
|
||||
while (p != NULL) {
|
||||
if (p->owner == pthread) {
|
||||
/*
|
||||
* The running thread will release the
|
||||
* lock now:
|
||||
*/
|
||||
p->count = 0;
|
||||
|
||||
/* Get the new owner of the lock: */
|
||||
while ((p->owner = TAILQ_FIRST(&p->l_head)) != NULL) {
|
||||
/* Pop the thread off the queue: */
|
||||
TAILQ_REMOVE(&p->l_head,p->owner,qe);
|
||||
p->owner->flags &= ~PTHREAD_FLAGS_IN_FILEQ;
|
||||
|
||||
if (p->owner->interrupted == 0) {
|
||||
/*
|
||||
* This is the first lock for
|
||||
* the new owner:
|
||||
*/
|
||||
p->count = 1;
|
||||
|
||||
/* Allow the new owner to run: */
|
||||
PTHREAD_NEW_STATE(p->owner,PS_RUNNING);
|
||||
|
||||
/* End the loop when we find a
|
||||
* thread that hasn't been
|
||||
* cancelled or interrupted;
|
||||
*/
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
p = next_p;
|
||||
if (next_p != NULL)
|
||||
next_p = LIST_NEXT(next_p, entry);
|
||||
}
|
||||
}
|
||||
|
||||
/* Unlock the hash table: */
|
||||
_SPINUNLOCK(&hash_lock);
|
||||
|
||||
/*
|
||||
* Undefer and handle pending signals, yielding if
|
||||
* necessary:
|
||||
*/
|
||||
_thread_kern_sig_undefer();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -41,10 +41,12 @@ fsync(int fd)
|
||||
{
|
||||
int ret;
|
||||
|
||||
_thread_enter_cancellation_point();
|
||||
if ((ret = _FD_LOCK(fd, FD_RDWR, NULL)) == 0) {
|
||||
ret = _thread_sys_fsync(fd);
|
||||
_FD_UNLOCK(fd, FD_RDWR);
|
||||
}
|
||||
_thread_leave_cancellation_point();
|
||||
return (ret);
|
||||
}
|
||||
#endif
|
||||
|
@ -37,7 +37,8 @@
|
||||
#include "pthread_private.h"
|
||||
|
||||
int
|
||||
pthread_getschedparam(pthread_t pthread, int *policy, struct sched_param *param)
|
||||
pthread_getschedparam(pthread_t pthread, int *policy,
|
||||
struct sched_param *param)
|
||||
{
|
||||
int ret;
|
||||
|
||||
|
@ -42,6 +42,7 @@
|
||||
#include <paths.h>
|
||||
#include <poll.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/ttycom.h>
|
||||
@ -199,6 +200,10 @@ _thread_init(void)
|
||||
*/
|
||||
_thread_initial->magic = PTHREAD_MAGIC;
|
||||
|
||||
/* Set the initial cancel state */
|
||||
_thread_initial->cancelflags = PTHREAD_CANCEL_ENABLE |
|
||||
PTHREAD_CANCEL_DEFERRED;
|
||||
|
||||
/* Default the priority of the initial thread: */
|
||||
_thread_initial->base_priority = PTHREAD_DEFAULT_PRIORITY;
|
||||
_thread_initial->active_priority = PTHREAD_DEFAULT_PRIORITY;
|
||||
|
@ -41,16 +41,22 @@ pthread_join(pthread_t pthread, void **thread_return)
|
||||
{
|
||||
int ret = 0;
|
||||
pthread_t pthread1 = NULL;
|
||||
|
||||
_thread_enter_cancellation_point();
|
||||
|
||||
/* Check if the caller has specified an invalid thread: */
|
||||
if (pthread == NULL || pthread->magic != PTHREAD_MAGIC)
|
||||
if (pthread == NULL || pthread->magic != PTHREAD_MAGIC) {
|
||||
/* Invalid thread: */
|
||||
_thread_leave_cancellation_point();
|
||||
return(EINVAL);
|
||||
}
|
||||
|
||||
/* Check if the caller has specified itself: */
|
||||
if (pthread == _thread_run)
|
||||
if (pthread == _thread_run) {
|
||||
/* Avoid a deadlock condition: */
|
||||
_thread_leave_cancellation_point();
|
||||
return(EDEADLK);
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the thread in the list of active threads or in the
|
||||
@ -71,12 +77,31 @@ pthread_join(pthread_t pthread, void **thread_return)
|
||||
|
||||
/* Check if the thread is not dead: */
|
||||
else if (pthread->state != PS_DEAD) {
|
||||
/* Clear the interrupted flag: */
|
||||
_thread_run->interrupted = 0;
|
||||
|
||||
/*
|
||||
* Protect against being context switched out while
|
||||
* adding this thread to the join queue.
|
||||
*/
|
||||
_thread_kern_sig_defer();
|
||||
|
||||
/* Add the running thread to the join queue: */
|
||||
TAILQ_INSERT_TAIL(&(pthread->join_queue), _thread_run, qe);
|
||||
|
||||
/* Schedule the next thread: */
|
||||
_thread_kern_sched_state(PS_JOIN, __FILE__, __LINE__);
|
||||
|
||||
if (_thread_run->interrupted != 0)
|
||||
TAILQ_REMOVE(&(pthread->join_queue), _thread_run, qe);
|
||||
|
||||
_thread_kern_sig_undefer();
|
||||
|
||||
if ((_thread_run->cancelflags & PTHREAD_CANCEL_NEEDED) != 0) {
|
||||
_thread_exit_cleanup();
|
||||
pthread_exit(PTHREAD_CANCELED);
|
||||
}
|
||||
|
||||
/* Check if the thread is not detached: */
|
||||
if ((pthread->attr.flags & PTHREAD_DETACHED) == 0) {
|
||||
/* Check if the return value is required: */
|
||||
@ -93,6 +118,8 @@ pthread_join(pthread_t pthread, void **thread_return)
|
||||
/* Return the thread's return value: */
|
||||
*thread_return = pthread->ret;
|
||||
|
||||
_thread_leave_cancellation_point();
|
||||
|
||||
/* Return the completion status: */
|
||||
return (ret);
|
||||
}
|
||||
|
@ -67,11 +67,10 @@ _thread_kern_sched(ucontext_t * scp)
|
||||
char *fdata;
|
||||
#endif
|
||||
pthread_t pthread, pthread_h = NULL;
|
||||
pthread_t last_thread = NULL;
|
||||
struct itimerval itimer;
|
||||
struct timespec ts, ts1;
|
||||
struct timeval tv, tv1;
|
||||
int i, set_timer = 0;
|
||||
int set_timer = 0;
|
||||
|
||||
/*
|
||||
* Flag the pthread kernel as executing scheduler code
|
||||
@ -109,6 +108,20 @@ __asm__("fnsave %0": :"m"(*fdata));
|
||||
*/
|
||||
_thread_kern_in_sched = 0;
|
||||
|
||||
if (((_thread_run->cancelflags & PTHREAD_AT_CANCEL_POINT) == 0) &&
|
||||
((_thread_run->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) != 0)) {
|
||||
/*
|
||||
* Cancelations override signals.
|
||||
*
|
||||
* Stick a cancellation point at the start of
|
||||
* each async-cancellable thread's resumption.
|
||||
*
|
||||
* We allow threads woken at cancel points to do their
|
||||
* own checks.
|
||||
*/
|
||||
pthread_testcancel();
|
||||
}
|
||||
|
||||
if (_sched_switch_hook != NULL) {
|
||||
/* Run the installed switch hook: */
|
||||
thread_run_switch_hook(_last_user_thread, _thread_run);
|
||||
@ -161,6 +174,7 @@ __asm__("fnsave %0": :"m"(*fdata));
|
||||
*/
|
||||
switch (_thread_run->state) {
|
||||
case PS_DEAD:
|
||||
case PS_STATE_MAX: /* to silence -Wall */
|
||||
/*
|
||||
* Dead threads are not placed in any queue:
|
||||
*/
|
||||
@ -249,6 +263,7 @@ __asm__("fnsave %0": :"m"(*fdata));
|
||||
|
||||
/* Insert into the work queue: */
|
||||
PTHREAD_WORKQ_INSERT(_thread_run);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -627,14 +642,12 @@ _thread_kern_sched_state_unlock(enum pthread_state state,
|
||||
static void
|
||||
_thread_kern_poll(int wait_reqd)
|
||||
{
|
||||
char bufr[128];
|
||||
int count = 0;
|
||||
int i, found;
|
||||
int kern_pipe_added = 0;
|
||||
int nfds = 0;
|
||||
int timeout_ms = 0;
|
||||
struct pthread *pthread, *pthread_next;
|
||||
ssize_t num;
|
||||
struct pthread *pthread;
|
||||
struct timespec ts;
|
||||
struct timeval tv;
|
||||
|
||||
@ -1103,10 +1116,10 @@ thread_run_switch_hook(pthread_t thread_out, pthread_t thread_in)
|
||||
pthread_t tid_in = thread_in;
|
||||
|
||||
if ((tid_out != NULL) &&
|
||||
(tid_out->flags & PTHREAD_FLAGS_PRIVATE != 0))
|
||||
(tid_out->flags & PTHREAD_FLAGS_PRIVATE) != 0)
|
||||
tid_out = NULL;
|
||||
if ((tid_in != NULL) &&
|
||||
(tid_in->flags & PTHREAD_FLAGS_PRIVATE != 0))
|
||||
(tid_in->flags & PTHREAD_FLAGS_PRIVATE) != 0)
|
||||
tid_in = NULL;
|
||||
|
||||
if ((_sched_switch_hook != NULL) && (tid_out != tid_in)) {
|
||||
|
40
lib/libc_r/uthread/uthread_msync.c
Normal file
40
lib/libc_r/uthread/uthread_msync.c
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* David Leonard <d@openbsd.org>, 1999. Public Domain.
|
||||
*
|
||||
* $OpenBSD: uthread_msync.c,v 1.2 1999/06/09 07:16:17 d Exp $
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/mman.h>
|
||||
#ifdef _THREAD_SAFE
|
||||
#include <pthread.h>
|
||||
#include "pthread_private.h"
|
||||
|
||||
int
|
||||
msync(addr, len, flags)
|
||||
void *addr;
|
||||
size_t len;
|
||||
int flags;
|
||||
{
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* XXX This is quite pointless unless we know how to get the
|
||||
* file descriptor associated with the memory, and lock it for
|
||||
* write. The only real use of this wrapper is to guarantee
|
||||
* a cancellation point, as per the standard. sigh.
|
||||
*/
|
||||
|
||||
/* This is a cancellation point: */
|
||||
_thread_enter_cancellation_point();
|
||||
|
||||
ret = _thread_sys_msync(addr, len, flags);
|
||||
|
||||
/* No longer in a cancellation point: */
|
||||
_thread_leave_cancellation_point();
|
||||
|
||||
return (ret);
|
||||
}
|
||||
#endif
|
@ -94,7 +94,8 @@ _mutex_reinit(pthread_mutex_t * mutex)
|
||||
TAILQ_INIT(&(*mutex)->m_queue);
|
||||
(*mutex)->m_owner = NULL;
|
||||
(*mutex)->m_data.m_count = 0;
|
||||
(*mutex)->m_flags = MUTEX_FLAGS_INITED;
|
||||
(*mutex)->m_flags &= MUTEX_FLAGS_PRIVATE;
|
||||
(*mutex)->m_flags |= MUTEX_FLAGS_INITED;
|
||||
(*mutex)->m_refcount = 0;
|
||||
(*mutex)->m_prio = 0;
|
||||
(*mutex)->m_saved_prio = 0;
|
||||
@ -428,6 +429,9 @@ pthread_mutex_lock(pthread_mutex_t * mutex)
|
||||
_MUTEX_INIT_LINK(*mutex);
|
||||
}
|
||||
|
||||
/* Reset the interrupted flag: */
|
||||
_thread_run->interrupted = 0;
|
||||
|
||||
/* Process according to mutex type: */
|
||||
switch ((*mutex)->m_protocol) {
|
||||
/* Default POSIX mutex: */
|
||||
@ -602,6 +606,13 @@ pthread_mutex_lock(pthread_mutex_t * mutex)
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check to see if this thread was interrupted and
|
||||
* is still in the mutex queue of waiting threads:
|
||||
*/
|
||||
if (_thread_run->interrupted != 0)
|
||||
mutex_queue_remove(*mutex, _thread_run);
|
||||
|
||||
/* Unlock the mutex structure: */
|
||||
_SPINUNLOCK(&(*mutex)->lock);
|
||||
|
||||
@ -610,6 +621,11 @@ pthread_mutex_lock(pthread_mutex_t * mutex)
|
||||
* necessary:
|
||||
*/
|
||||
_thread_kern_sig_undefer();
|
||||
|
||||
if ((_thread_run->cancelflags & PTHREAD_CANCEL_NEEDED) != 0) {
|
||||
_thread_exit_cleanup();
|
||||
pthread_exit(PTHREAD_CANCELED);
|
||||
}
|
||||
}
|
||||
|
||||
/* Return the completion status: */
|
||||
@ -1314,6 +1330,18 @@ mutex_rescan_owned (pthread_t pthread, pthread_mutex_t mutex)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
_mutex_unlock_private(pthread_t pthread)
|
||||
{
|
||||
struct pthread_mutex *m, *m_next;
|
||||
|
||||
for (m = TAILQ_FIRST(&pthread->mutexq); m != NULL; m = m_next) {
|
||||
m_next = TAILQ_NEXT(m, m_qe);
|
||||
if ((m->m_flags & MUTEX_FLAGS_PRIVATE) != 0)
|
||||
pthread_mutex_unlock(&m);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Dequeue a waiting thread from the head of a mutex queue in descending
|
||||
* priority order.
|
||||
@ -1323,8 +1351,17 @@ mutex_queue_deq(pthread_mutex_t mutex)
|
||||
{
|
||||
pthread_t pthread;
|
||||
|
||||
if ((pthread = TAILQ_FIRST(&mutex->m_queue)) != NULL)
|
||||
while ((pthread = TAILQ_FIRST(&mutex->m_queue)) != NULL) {
|
||||
TAILQ_REMOVE(&mutex->m_queue, pthread, qe);
|
||||
pthread->flags &= ~PTHREAD_FLAGS_IN_MUTEXQ;
|
||||
|
||||
/*
|
||||
* Only exit the loop if the thread hasn't been
|
||||
* cancelled.
|
||||
*/
|
||||
if (pthread->interrupted == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return(pthread);
|
||||
}
|
||||
@ -1335,7 +1372,10 @@ mutex_queue_deq(pthread_mutex_t mutex)
|
||||
static inline void
|
||||
mutex_queue_remove(pthread_mutex_t mutex, pthread_t pthread)
|
||||
{
|
||||
TAILQ_REMOVE(&mutex->m_queue, pthread, qe);
|
||||
if ((pthread->flags & PTHREAD_FLAGS_IN_MUTEXQ) != 0) {
|
||||
TAILQ_REMOVE(&mutex->m_queue, pthread, qe);
|
||||
pthread->flags &= ~PTHREAD_FLAGS_IN_MUTEXQ;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1359,6 +1399,7 @@ mutex_queue_enq(pthread_mutex_t mutex, pthread_t pthread)
|
||||
tid = TAILQ_NEXT(tid, qe);
|
||||
TAILQ_INSERT_BEFORE(tid, pthread, qe);
|
||||
}
|
||||
pthread->flags |= PTHREAD_FLAGS_IN_MUTEXQ;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -47,6 +47,7 @@ nanosleep(const struct timespec * time_to_sleep,
|
||||
struct timespec remaining_time;
|
||||
struct timeval tv;
|
||||
|
||||
_thread_enter_cancellation_point();
|
||||
/* Check if the time to sleep is legal: */
|
||||
if (time_to_sleep == NULL || time_to_sleep->tv_sec < 0 ||
|
||||
time_to_sleep->tv_nsec < 0 || time_to_sleep->tv_nsec >= 1000000000) {
|
||||
@ -116,6 +117,7 @@ nanosleep(const struct timespec * time_to_sleep,
|
||||
ret = -1;
|
||||
}
|
||||
}
|
||||
_thread_leave_cancellation_point();
|
||||
return (ret);
|
||||
}
|
||||
#endif
|
||||
|
@ -44,10 +44,11 @@
|
||||
int
|
||||
open(const char *path, int flags,...)
|
||||
{
|
||||
int fd;
|
||||
int mode = 0;
|
||||
int status;
|
||||
va_list ap;
|
||||
int fd;
|
||||
int mode = 0;
|
||||
va_list ap;
|
||||
|
||||
_thread_enter_cancellation_point();
|
||||
|
||||
/* Check if the file is being created: */
|
||||
if (flags & O_CREAT) {
|
||||
@ -68,6 +69,8 @@ open(const char *path, int flags,...)
|
||||
fd = -1;
|
||||
}
|
||||
|
||||
_thread_leave_cancellation_point();
|
||||
|
||||
/* Return the file descriptor or -1 on error: */
|
||||
return (fd);
|
||||
}
|
||||
|
@ -47,9 +47,13 @@ read(int fd, void *buf, size_t nbytes)
|
||||
int ret;
|
||||
int type;
|
||||
|
||||
_thread_enter_cancellation_point();
|
||||
|
||||
/* POSIX says to do just this: */
|
||||
if (nbytes == 0)
|
||||
if (nbytes == 0) {
|
||||
_thread_leave_cancellation_point();
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Lock the file descriptor for read: */
|
||||
if ((ret = _FD_LOCK(fd, FD_READ, NULL)) == 0) {
|
||||
@ -61,6 +65,7 @@ read(int fd, void *buf, size_t nbytes)
|
||||
/* File is not open for read: */
|
||||
errno = EBADF;
|
||||
_FD_UNLOCK(fd, FD_READ);
|
||||
_thread_leave_cancellation_point();
|
||||
return (-1);
|
||||
}
|
||||
|
||||
@ -92,6 +97,7 @@ read(int fd, void *buf, size_t nbytes)
|
||||
}
|
||||
_FD_UNLOCK(fd, FD_READ);
|
||||
}
|
||||
_thread_leave_cancellation_point();
|
||||
return (ret);
|
||||
}
|
||||
#endif
|
||||
|
@ -38,7 +38,8 @@
|
||||
#include "pthread_private.h"
|
||||
|
||||
int
|
||||
pthread_setschedparam(pthread_t pthread, int policy, struct sched_param *param)
|
||||
pthread_setschedparam(pthread_t pthread, int policy,
|
||||
const struct sched_param *param)
|
||||
{
|
||||
int old_prio, in_readyq = 0, ret = 0;
|
||||
|
||||
|
@ -47,6 +47,7 @@ sigwait(const sigset_t * set, int *sig)
|
||||
sigset_t tempset, waitset;
|
||||
struct sigaction act;
|
||||
|
||||
_thread_enter_cancellation_point();
|
||||
/*
|
||||
* Specify the thread kernel signal handler.
|
||||
*/
|
||||
@ -85,6 +86,7 @@ sigwait(const sigset_t * set, int *sig)
|
||||
/* Return the signal number to the caller: */
|
||||
*sig = i;
|
||||
|
||||
_thread_leave_cancellation_point();
|
||||
return (0);
|
||||
}
|
||||
|
||||
@ -137,6 +139,7 @@ sigwait(const sigset_t * set, int *sig)
|
||||
}
|
||||
}
|
||||
|
||||
_thread_leave_cancellation_point();
|
||||
/* Return the completion status: */
|
||||
return (ret);
|
||||
}
|
||||
|
@ -42,6 +42,7 @@ wait4(pid_t pid, int *istat, int options, struct rusage * rusage)
|
||||
{
|
||||
pid_t ret;
|
||||
|
||||
_thread_enter_cancellation_point();
|
||||
_thread_kern_sig_defer();
|
||||
|
||||
/* Perform a non-blocking wait4 syscall: */
|
||||
@ -61,6 +62,7 @@ wait4(pid_t pid, int *istat, int options, struct rusage * rusage)
|
||||
}
|
||||
|
||||
_thread_kern_sig_undefer();
|
||||
_thread_leave_cancellation_point();
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
@ -50,9 +50,12 @@ write(int fd, const void *buf, size_t nbytes)
|
||||
ssize_t num = 0;
|
||||
ssize_t ret;
|
||||
|
||||
_thread_enter_cancellation_point();
|
||||
/* POSIX says to do just this: */
|
||||
if (nbytes == 0)
|
||||
if (nbytes == 0) {
|
||||
_thread_leave_cancellation_point();
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Lock the file descriptor for write: */
|
||||
if ((ret = _FD_LOCK(fd, FD_WRITE, NULL)) == 0) {
|
||||
@ -64,7 +67,8 @@ write(int fd, const void *buf, size_t nbytes)
|
||||
/* File is not open for write: */
|
||||
errno = EBADF;
|
||||
_FD_UNLOCK(fd, FD_WRITE);
|
||||
return (-1);
|
||||
_thread_leave_cancellation_point();
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/* Check if file operations are to block */
|
||||
@ -129,6 +133,7 @@ write(int fd, const void *buf, size_t nbytes)
|
||||
}
|
||||
_FD_UNLOCK(fd, FD_RDWR);
|
||||
}
|
||||
_thread_leave_cancellation_point();
|
||||
return (ret);
|
||||
}
|
||||
#endif
|
||||
|
@ -15,6 +15,9 @@ CFLAGS+=-DPTHREAD_KERNEL -D_THREAD_SAFE -I${.CURDIR}/uthread
|
||||
# thread locking.
|
||||
CFLAGS+=-D_LOCK_DEBUG
|
||||
|
||||
# enable extra internal consistancy checks
|
||||
# CFLAGS+=-D_PTHREADS_INVARIANTS
|
||||
|
||||
AINC= -I${.CURDIR}/../libc/${MACHINE_ARCH} -I${.CURDIR}/uthread
|
||||
PRECIOUSLIB= yes
|
||||
|
||||
@ -25,11 +28,12 @@ PRECIOUSLIB= yes
|
||||
HIDDEN_SYSCALLS= accept.o bind.o close.o connect.o dup.o dup2.o \
|
||||
execve.o fchflags.o fchmod.o fchown.o fcntl.o \
|
||||
flock.o fpathconf.o fstat.o fstatfs.o fsync.o getdirentries.o \
|
||||
getpeername.o getsockname.o getsockopt.o ioctl.o listen.o \
|
||||
nanosleep.o nfssvc.o open.o poll.o read.o readv.o recvfrom.o \
|
||||
getlogin.o getpeername.o getsockname.o getsockopt.o ioctl.o listen.o \
|
||||
msync.o nanosleep.o nfssvc.o open.o poll.o read.o readv.o recvfrom.o \
|
||||
recvmsg.o sched_yield.o select.o sendmsg.o sendto.o \
|
||||
setsockopt.o shutdown.o sigaction.o sigaltstack.o \
|
||||
signanosleep.o sigpending.o sigprocmask.o sigsuspend.o socket.o \
|
||||
signanosleep.o sigpending.o sigprocmask.o sigreturn.o sigsetmask.o \
|
||||
sigsuspend.o socket.o \
|
||||
socketpair.o wait4.o write.o writev.o
|
||||
|
||||
.include "${.CURDIR}/../libc/Makefile.inc"
|
||||
|
@ -24,6 +24,7 @@ SRCS+= \
|
||||
uthread_attr_setstacksize.c \
|
||||
uthread_autoinit.cc \
|
||||
uthread_bind.c \
|
||||
uthread_cancel.c \
|
||||
uthread_clean.c \
|
||||
uthread_close.c \
|
||||
uthread_cond.c \
|
||||
@ -37,6 +38,7 @@ SRCS+= \
|
||||
uthread_equal.c \
|
||||
uthread_execve.c \
|
||||
uthread_exit.c \
|
||||
uthread_fchflags.c \
|
||||
uthread_fchmod.c \
|
||||
uthread_fchown.c \
|
||||
uthread_fcntl.c \
|
||||
@ -64,6 +66,7 @@ SRCS+= \
|
||||
uthread_listen.c \
|
||||
uthread_mattr_init.c \
|
||||
uthread_mattr_kind_np.c \
|
||||
uthread_msync.c \
|
||||
uthread_multi_np.c \
|
||||
uthread_mutex.c \
|
||||
uthread_mutex_prioceiling.c \
|
||||
|
@ -37,7 +37,7 @@
|
||||
#include "pthread_private.h"
|
||||
|
||||
int
|
||||
pthread_attr_getinheritsched(pthread_attr_t *attr, int *sched_inherit)
|
||||
pthread_attr_getinheritsched(const pthread_attr_t *attr, int *sched_inherit)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
|
@ -37,7 +37,7 @@
|
||||
#include "pthread_private.h"
|
||||
|
||||
int
|
||||
pthread_attr_getschedparam(pthread_attr_t *attr, struct sched_param *param)
|
||||
pthread_attr_getschedparam(const pthread_attr_t *attr, struct sched_param *param)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
|
@ -37,7 +37,7 @@
|
||||
#include "pthread_private.h"
|
||||
|
||||
int
|
||||
pthread_attr_getschedpolicy(pthread_attr_t *attr, int *policy)
|
||||
pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
|
@ -37,7 +37,7 @@
|
||||
#include "pthread_private.h"
|
||||
|
||||
int
|
||||
pthread_attr_getscope(pthread_attr_t *attr, int *contentionscope)
|
||||
pthread_attr_getscope(const pthread_attr_t *attr, int *contentionscope)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
|
179
lib/libkse/thread/thr_cancel.c
Normal file
179
lib/libkse/thread/thr_cancel.c
Normal file
@ -0,0 +1,179 @@
|
||||
/*
|
||||
* David Leonard <d@openbsd.org>, 1999. Public domain.
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <sys/errno.h>
|
||||
#include <pthread.h>
|
||||
#include "pthread_private.h"
|
||||
|
||||
int
|
||||
pthread_cancel(pthread_t pthread)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if ((ret = _find_thread(pthread)) != 0) {
|
||||
/* NOTHING */
|
||||
} else if (pthread->state == PS_DEAD || pthread->state == PS_DEADLOCK) {
|
||||
ret = 0;
|
||||
} else {
|
||||
/* Protect the scheduling queues: */
|
||||
_thread_kern_sig_defer();
|
||||
|
||||
/* Check if we need to kick it back into the run queue: */
|
||||
if ((pthread->cancelflags & PTHREAD_CANCEL_DISABLE) == 0)
|
||||
switch (pthread->state) {
|
||||
case PS_RUNNING:
|
||||
/* No need to resume: */
|
||||
pthread->cancelflags |= PTHREAD_CANCELLING;
|
||||
break;
|
||||
|
||||
case PS_SPINBLOCK:
|
||||
case PS_FDR_WAIT:
|
||||
case PS_FDW_WAIT:
|
||||
case PS_POLL_WAIT:
|
||||
case PS_SELECT_WAIT:
|
||||
/* Remove these threads from the work queue: */
|
||||
if ((pthread->flags & PTHREAD_FLAGS_IN_WORKQ)
|
||||
!= 0)
|
||||
PTHREAD_WORKQ_REMOVE(pthread);
|
||||
/* Fall through: */
|
||||
case PS_SIGTHREAD:
|
||||
case PS_SLEEP_WAIT:
|
||||
case PS_WAIT_WAIT:
|
||||
case PS_SIGSUSPEND:
|
||||
case PS_SIGWAIT:
|
||||
case PS_SUSPENDED:
|
||||
/* Interrupt and resume: */
|
||||
pthread->interrupted = 1;
|
||||
pthread->cancelflags |= PTHREAD_CANCELLING;
|
||||
PTHREAD_NEW_STATE(pthread,PS_RUNNING);
|
||||
break;
|
||||
|
||||
case PS_MUTEX_WAIT:
|
||||
case PS_COND_WAIT:
|
||||
case PS_FDLR_WAIT:
|
||||
case PS_FDLW_WAIT:
|
||||
case PS_FILE_WAIT:
|
||||
case PS_JOIN:
|
||||
/*
|
||||
* Threads in these states may be in queues.
|
||||
* In order to preserve queue integrity, the
|
||||
* cancelled thread must remove itself from the
|
||||
* queue. Mark the thread as interrupted and
|
||||
* needing cancellation, and set the state to
|
||||
* running. When the thread resumes, it will
|
||||
* exit after removing itself from the queue.
|
||||
*/
|
||||
pthread->interrupted = 1;
|
||||
pthread->cancelflags |= PTHREAD_CANCEL_NEEDED;
|
||||
PTHREAD_NEW_STATE(pthread,PS_RUNNING);
|
||||
break;
|
||||
|
||||
case PS_DEAD:
|
||||
case PS_DEADLOCK:
|
||||
case PS_STATE_MAX:
|
||||
/* Ignore - only here to silence -Wall: */
|
||||
break;
|
||||
}
|
||||
/* Unprotect the scheduling queues: */
|
||||
_thread_kern_sig_undefer();
|
||||
|
||||
ret = 0;
|
||||
}
|
||||
return (ret);
|
||||
}
|
||||
|
||||
int
|
||||
pthread_setcancelstate(int state, int *oldstate)
|
||||
{
|
||||
int ostate;
|
||||
int ret;
|
||||
|
||||
ostate = _thread_run->cancelflags & PTHREAD_CANCEL_DISABLE;
|
||||
|
||||
switch (state) {
|
||||
case PTHREAD_CANCEL_ENABLE:
|
||||
if (oldstate != NULL)
|
||||
*oldstate = ostate;
|
||||
_thread_run->cancelflags &= PTHREAD_CANCEL_ENABLE;
|
||||
if ((_thread_run->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) != 0)
|
||||
pthread_testcancel();
|
||||
ret = 0;
|
||||
break;
|
||||
case PTHREAD_CANCEL_DISABLE:
|
||||
if (oldstate != NULL)
|
||||
*oldstate = ostate;
|
||||
_thread_run->cancelflags |= PTHREAD_CANCEL_DISABLE;
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
ret = EINVAL;
|
||||
}
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
int
|
||||
pthread_setcanceltype(int type, int *oldtype)
|
||||
{
|
||||
int otype;
|
||||
int ret;
|
||||
|
||||
otype = _thread_run->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS;
|
||||
switch (type) {
|
||||
case PTHREAD_CANCEL_ASYNCHRONOUS:
|
||||
if (oldtype != NULL)
|
||||
*oldtype = otype;
|
||||
_thread_run->cancelflags |= PTHREAD_CANCEL_ASYNCHRONOUS;
|
||||
pthread_testcancel();
|
||||
ret = 0;
|
||||
break;
|
||||
case PTHREAD_CANCEL_DEFERRED:
|
||||
if (oldtype != NULL)
|
||||
*oldtype = otype;
|
||||
_thread_run->cancelflags &= ~PTHREAD_CANCEL_ASYNCHRONOUS;
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
ret = EINVAL;
|
||||
}
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
void
|
||||
pthread_testcancel(void)
|
||||
{
|
||||
|
||||
if (((_thread_run->cancelflags & PTHREAD_CANCEL_DISABLE) == 0) &&
|
||||
((_thread_run->cancelflags & PTHREAD_CANCELLING) != 0)) {
|
||||
/*
|
||||
* It is possible for this thread to be swapped out
|
||||
* while performing cancellation; do not allow it
|
||||
* to be cancelled again.
|
||||
*/
|
||||
_thread_run->cancelflags &= ~PTHREAD_CANCELLING;
|
||||
_thread_exit_cleanup();
|
||||
pthread_exit(PTHREAD_CANCELED);
|
||||
PANIC("cancel");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
_thread_enter_cancellation_point(void)
|
||||
{
|
||||
|
||||
/* Look for a cancellation before we block: */
|
||||
pthread_testcancel();
|
||||
_thread_run->cancelflags |= PTHREAD_AT_CANCEL_POINT;
|
||||
}
|
||||
|
||||
void
|
||||
_thread_leave_cancellation_point(void)
|
||||
{
|
||||
|
||||
_thread_run->cancelflags &= ~PTHREAD_AT_CANCEL_POINT;
|
||||
/* Look for a cancellation after we unblock: */
|
||||
pthread_testcancel();
|
||||
}
|
@ -45,10 +45,11 @@ close(int fd)
|
||||
{
|
||||
int flags;
|
||||
int ret;
|
||||
int status;
|
||||
struct stat sb;
|
||||
struct fd_table_entry *entry;
|
||||
|
||||
_thread_enter_cancellation_point();
|
||||
|
||||
if ((fd == _thread_kern_pipe[0]) || (fd == _thread_kern_pipe[1])) {
|
||||
/*
|
||||
* Don't allow silly programs to close the kernel pipe.
|
||||
@ -98,6 +99,7 @@ close(int fd)
|
||||
/* Close the file descriptor: */
|
||||
ret = _thread_sys_close(fd);
|
||||
}
|
||||
_thread_leave_cancellation_point();
|
||||
return (ret);
|
||||
}
|
||||
#endif
|
||||
|
@ -157,8 +157,7 @@ pthread_cond_destroy(pthread_cond_t * cond)
|
||||
int
|
||||
pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
|
||||
{
|
||||
int rval = 0;
|
||||
int status;
|
||||
int rval = 0;
|
||||
|
||||
if (cond == NULL)
|
||||
rval = EINVAL;
|
||||
@ -169,6 +168,9 @@ pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
|
||||
*/
|
||||
else if (*cond != NULL ||
|
||||
(rval = pthread_cond_init(cond,NULL)) == 0) {
|
||||
|
||||
_thread_enter_cancellation_point();
|
||||
|
||||
/* Lock the condition variable structure: */
|
||||
_SPINLOCK(&(*cond)->lock);
|
||||
|
||||
@ -193,8 +195,9 @@ pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
|
||||
/* Return invalid argument error: */
|
||||
rval = EINVAL;
|
||||
} else {
|
||||
/* Reset the timeout flag: */
|
||||
/* Reset the timeout and interrupted flags: */
|
||||
_thread_run->timeout = 0;
|
||||
_thread_run->interrupted = 0;
|
||||
|
||||
/*
|
||||
* Queue the running thread for the condition
|
||||
@ -233,7 +236,28 @@ pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
|
||||
_thread_kern_sched_state_unlock(PS_COND_WAIT,
|
||||
&(*cond)->lock, __FILE__, __LINE__);
|
||||
|
||||
/* Lock the mutex: */
|
||||
if (_thread_run->interrupted != 0) {
|
||||
/*
|
||||
* Lock the condition variable
|
||||
* while removing the thread.
|
||||
*/
|
||||
_SPINLOCK(&(*cond)->lock);
|
||||
|
||||
cond_queue_remove(*cond,
|
||||
_thread_run);
|
||||
|
||||
/* Check for no more waiters: */
|
||||
if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
|
||||
(*cond)->c_mutex = NULL;
|
||||
|
||||
_SPINUNLOCK(&(*cond)->lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Note that even though this thread may have
|
||||
* been canceled, POSIX requires that the mutex
|
||||
* be reaquired prior to cancellation.
|
||||
*/
|
||||
rval = _mutex_cv_lock(mutex);
|
||||
}
|
||||
}
|
||||
@ -248,6 +272,13 @@ pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
|
||||
rval = EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((_thread_run->cancelflags & PTHREAD_CANCEL_NEEDED) != 0) {
|
||||
_thread_exit_cleanup();
|
||||
pthread_exit(PTHREAD_CANCELED);
|
||||
}
|
||||
|
||||
_thread_leave_cancellation_point();
|
||||
}
|
||||
|
||||
/* Return the completion status: */
|
||||
@ -258,8 +289,7 @@ int
|
||||
pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
|
||||
const struct timespec * abstime)
|
||||
{
|
||||
int rval = 0;
|
||||
int status;
|
||||
int rval = 0;
|
||||
|
||||
if (cond == NULL || abstime == NULL)
|
||||
rval = EINVAL;
|
||||
@ -276,6 +306,9 @@ pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
|
||||
*/
|
||||
if (*cond != NULL ||
|
||||
(rval = pthread_cond_init(cond,NULL)) == 0) {
|
||||
|
||||
_thread_enter_cancellation_point();
|
||||
|
||||
/* Lock the condition variable structure: */
|
||||
_SPINLOCK(&(*cond)->lock);
|
||||
|
||||
@ -306,8 +339,9 @@ pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
|
||||
_thread_run->wakeup_time.tv_nsec =
|
||||
abstime->tv_nsec;
|
||||
|
||||
/* Reset the timeout flag: */
|
||||
/* Reset the timeout and interrupted flags: */
|
||||
_thread_run->timeout = 0;
|
||||
_thread_run->interrupted = 0;
|
||||
|
||||
/*
|
||||
* Queue the running thread for the condition
|
||||
@ -341,12 +375,16 @@ pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
|
||||
_thread_kern_sched_state_unlock(PS_COND_WAIT,
|
||||
&(*cond)->lock, __FILE__, __LINE__);
|
||||
|
||||
/* Check if the wait timedout: */
|
||||
if (_thread_run->timeout == 0) {
|
||||
/*
|
||||
* Check if the wait timedout or was
|
||||
* interrupted (canceled):
|
||||
*/
|
||||
if ((_thread_run->timeout == 0) &&
|
||||
(_thread_run->interrupted == 0)) {
|
||||
/* Lock the mutex: */
|
||||
rval = _mutex_cv_lock(mutex);
|
||||
}
|
||||
else {
|
||||
|
||||
} else {
|
||||
/* Lock the condition variable structure: */
|
||||
_SPINLOCK(&(*cond)->lock);
|
||||
|
||||
@ -369,8 +407,12 @@ pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
|
||||
rval = ETIMEDOUT;
|
||||
|
||||
/*
|
||||
* Lock the mutex and ignore
|
||||
* any errors:
|
||||
* Lock the mutex and ignore any
|
||||
* errors. Note that even though
|
||||
* this thread may have been
|
||||
* canceled, POSIX requires that
|
||||
* the mutex be reaquired prior
|
||||
* to cancellation.
|
||||
*/
|
||||
(void)_mutex_cv_lock(mutex);
|
||||
}
|
||||
@ -388,6 +430,12 @@ pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
|
||||
break;
|
||||
}
|
||||
|
||||
if ((_thread_run->cancelflags & PTHREAD_CANCEL_NEEDED) != 0) {
|
||||
_thread_exit_cleanup();
|
||||
pthread_exit(PTHREAD_CANCELED);
|
||||
}
|
||||
|
||||
_thread_leave_cancellation_point();
|
||||
}
|
||||
|
||||
/* Return the completion status: */
|
||||
@ -416,16 +464,7 @@ pthread_cond_signal(pthread_cond_t * cond)
|
||||
switch ((*cond)->c_type) {
|
||||
/* Fast condition variable: */
|
||||
case COND_TYPE_FAST:
|
||||
/*
|
||||
* Enter a loop to dequeue threads from the condition
|
||||
* queue until we find one that hasn't previously
|
||||
* timed out.
|
||||
*/
|
||||
while (((pthread = cond_queue_deq(*cond)) != NULL) &&
|
||||
(pthread->timeout != 0)) {
|
||||
}
|
||||
|
||||
if (pthread != NULL)
|
||||
if ((pthread = cond_queue_deq(*cond)) != NULL)
|
||||
/* Allow the thread to run: */
|
||||
PTHREAD_NEW_STATE(pthread,PS_RUNNING);
|
||||
|
||||
@ -482,12 +521,7 @@ pthread_cond_broadcast(pthread_cond_t * cond)
|
||||
* condition queue:
|
||||
*/
|
||||
while ((pthread = cond_queue_deq(*cond)) != NULL) {
|
||||
/*
|
||||
* The thread is already running if the
|
||||
* timeout flag is set.
|
||||
*/
|
||||
if (pthread->timeout == 0)
|
||||
PTHREAD_NEW_STATE(pthread,PS_RUNNING);
|
||||
PTHREAD_NEW_STATE(pthread,PS_RUNNING);
|
||||
}
|
||||
|
||||
/* There are no more waiting threads: */
|
||||
@ -524,9 +558,17 @@ cond_queue_deq(pthread_cond_t cond)
|
||||
{
|
||||
pthread_t pthread;
|
||||
|
||||
if ((pthread = TAILQ_FIRST(&cond->c_queue)) != NULL) {
|
||||
while ((pthread = TAILQ_FIRST(&cond->c_queue)) != NULL) {
|
||||
TAILQ_REMOVE(&cond->c_queue, pthread, qe);
|
||||
pthread->flags &= ~PTHREAD_FLAGS_IN_CONDQ;
|
||||
if ((pthread->timeout == 0) && (pthread->interrupted == 0))
|
||||
/*
|
||||
* Only exit the loop when we find a thread
|
||||
* that hasn't timed out or been canceled;
|
||||
* those threads are already running and don't
|
||||
* need their run state changed.
|
||||
*/
|
||||
break;
|
||||
}
|
||||
|
||||
return(pthread);
|
||||
|
@ -50,9 +50,7 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr,
|
||||
void *(*start_routine) (void *), void *arg)
|
||||
{
|
||||
int f_gc = 0;
|
||||
int i;
|
||||
int ret = 0;
|
||||
int status;
|
||||
pthread_t gc_thread;
|
||||
pthread_t new_thread;
|
||||
pthread_attr_t pattr;
|
||||
@ -166,6 +164,9 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr,
|
||||
new_thread->start_routine = start_routine;
|
||||
new_thread->arg = arg;
|
||||
|
||||
new_thread->cancelflags = PTHREAD_CANCEL_ENABLE |
|
||||
PTHREAD_CANCEL_DEFERRED;
|
||||
|
||||
/*
|
||||
* Write a magic value to the thread structure
|
||||
* to help identify valid ones:
|
||||
|
@ -34,6 +34,8 @@
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifdef _THREAD_SAFE
|
||||
#include <pthread.h>
|
||||
@ -101,17 +103,45 @@ _thread_exit(char *fname, int lineno, char *string)
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Only called when a thread is cancelled. It may be more useful
|
||||
* to call it from pthread_exit() if other ways of asynchronous or
|
||||
* abnormal thread termination can be found.
|
||||
*/
|
||||
void
|
||||
_thread_exit_cleanup(void)
|
||||
{
|
||||
/*
|
||||
* POSIX states that cancellation/termination of a thread should
|
||||
* not release any visible resources (such as mutexes) and that
|
||||
* it is the applications responsibility. Resources that are
|
||||
* internal to the threads library, including file and fd locks,
|
||||
* are not visible to the application and need to be released.
|
||||
*/
|
||||
/* Unlock all owned fd locks: */
|
||||
_thread_fd_unlock_owned(_thread_run);
|
||||
|
||||
/* Unlock all owned file locks: */
|
||||
_funlock_owned(_thread_run);
|
||||
|
||||
/* Unlock all private mutexes: */
|
||||
_mutex_unlock_private(_thread_run);
|
||||
|
||||
/*
|
||||
* This still isn't quite correct because we don't account
|
||||
* for held spinlocks (see libc/stdlib/malloc.c).
|
||||
*/
|
||||
}
|
||||
|
||||
void
|
||||
pthread_exit(void *status)
|
||||
{
|
||||
int sig;
|
||||
long l;
|
||||
pthread_t pthread;
|
||||
pthread_t pthread;
|
||||
|
||||
/* Check if this thread is already in the process of exiting: */
|
||||
if ((_thread_run->flags & PTHREAD_EXITING) != 0) {
|
||||
char msg[128];
|
||||
snprintf(msg,"Thread %p has called pthread_exit() from a destructor. POSIX 1003.1 1996 s16.2.5.2 does not allow this!",_thread_run);
|
||||
snprintf(msg, sizeof(msg), "Thread %p has called pthread_exit() from a destructor. POSIX 1003.1 1996 s16.2.5.2 does not allow this!",_thread_run);
|
||||
PANIC(msg);
|
||||
}
|
||||
|
||||
@ -134,7 +164,7 @@ pthread_exit(void *status)
|
||||
_thread_cleanupspecific();
|
||||
}
|
||||
|
||||
/* Free thread-specific poll_data structure, if allocated */
|
||||
/* Free thread-specific poll_data structure, if allocated: */
|
||||
if (_thread_run->poll_data.fds != NULL) {
|
||||
free(_thread_run->poll_data.fds);
|
||||
_thread_run->poll_data.fds = NULL;
|
||||
|
@ -47,6 +47,8 @@ fcntl(int fd, int cmd,...)
|
||||
int ret;
|
||||
va_list ap;
|
||||
|
||||
_thread_enter_cancellation_point();
|
||||
|
||||
/* Lock the file descriptor: */
|
||||
if ((ret = _FD_LOCK(fd, FD_RDWR, NULL)) == 0) {
|
||||
/* Initialise the variable argument list: */
|
||||
@ -135,6 +137,7 @@ fcntl(int fd, int cmd,...)
|
||||
/* Unlock the file descriptor: */
|
||||
_FD_UNLOCK(fd, FD_RDWR);
|
||||
}
|
||||
_thread_leave_cancellation_point();
|
||||
|
||||
/* Return the completion status: */
|
||||
return (ret);
|
||||
|
@ -41,10 +41,12 @@ fsync(int fd)
|
||||
{
|
||||
int ret;
|
||||
|
||||
_thread_enter_cancellation_point();
|
||||
if ((ret = _FD_LOCK(fd, FD_RDWR, NULL)) == 0) {
|
||||
ret = _thread_sys_fsync(fd);
|
||||
_FD_UNLOCK(fd, FD_RDWR);
|
||||
}
|
||||
_thread_leave_cancellation_point();
|
||||
return (ret);
|
||||
}
|
||||
#endif
|
||||
|
@ -37,7 +37,8 @@
|
||||
#include "pthread_private.h"
|
||||
|
||||
int
|
||||
pthread_getschedparam(pthread_t pthread, int *policy, struct sched_param *param)
|
||||
pthread_getschedparam(pthread_t pthread, int *policy,
|
||||
struct sched_param *param)
|
||||
{
|
||||
int ret;
|
||||
|
||||
|
@ -42,6 +42,7 @@
|
||||
#include <paths.h>
|
||||
#include <poll.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/ttycom.h>
|
||||
@ -199,6 +200,10 @@ _thread_init(void)
|
||||
*/
|
||||
_thread_initial->magic = PTHREAD_MAGIC;
|
||||
|
||||
/* Set the initial cancel state */
|
||||
_thread_initial->cancelflags = PTHREAD_CANCEL_ENABLE |
|
||||
PTHREAD_CANCEL_DEFERRED;
|
||||
|
||||
/* Default the priority of the initial thread: */
|
||||
_thread_initial->base_priority = PTHREAD_DEFAULT_PRIORITY;
|
||||
_thread_initial->active_priority = PTHREAD_DEFAULT_PRIORITY;
|
||||
|
@ -41,16 +41,22 @@ pthread_join(pthread_t pthread, void **thread_return)
|
||||
{
|
||||
int ret = 0;
|
||||
pthread_t pthread1 = NULL;
|
||||
|
||||
_thread_enter_cancellation_point();
|
||||
|
||||
/* Check if the caller has specified an invalid thread: */
|
||||
if (pthread == NULL || pthread->magic != PTHREAD_MAGIC)
|
||||
if (pthread == NULL || pthread->magic != PTHREAD_MAGIC) {
|
||||
/* Invalid thread: */
|
||||
_thread_leave_cancellation_point();
|
||||
return(EINVAL);
|
||||
}
|
||||
|
||||
/* Check if the caller has specified itself: */
|
||||
if (pthread == _thread_run)
|
||||
if (pthread == _thread_run) {
|
||||
/* Avoid a deadlock condition: */
|
||||
_thread_leave_cancellation_point();
|
||||
return(EDEADLK);
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the thread in the list of active threads or in the
|
||||
@ -71,12 +77,31 @@ pthread_join(pthread_t pthread, void **thread_return)
|
||||
|
||||
/* Check if the thread is not dead: */
|
||||
else if (pthread->state != PS_DEAD) {
|
||||
/* Clear the interrupted flag: */
|
||||
_thread_run->interrupted = 0;
|
||||
|
||||
/*
|
||||
* Protect against being context switched out while
|
||||
* adding this thread to the join queue.
|
||||
*/
|
||||
_thread_kern_sig_defer();
|
||||
|
||||
/* Add the running thread to the join queue: */
|
||||
TAILQ_INSERT_TAIL(&(pthread->join_queue), _thread_run, qe);
|
||||
|
||||
/* Schedule the next thread: */
|
||||
_thread_kern_sched_state(PS_JOIN, __FILE__, __LINE__);
|
||||
|
||||
if (_thread_run->interrupted != 0)
|
||||
TAILQ_REMOVE(&(pthread->join_queue), _thread_run, qe);
|
||||
|
||||
_thread_kern_sig_undefer();
|
||||
|
||||
if ((_thread_run->cancelflags & PTHREAD_CANCEL_NEEDED) != 0) {
|
||||
_thread_exit_cleanup();
|
||||
pthread_exit(PTHREAD_CANCELED);
|
||||
}
|
||||
|
||||
/* Check if the thread is not detached: */
|
||||
if ((pthread->attr.flags & PTHREAD_DETACHED) == 0) {
|
||||
/* Check if the return value is required: */
|
||||
@ -93,6 +118,8 @@ pthread_join(pthread_t pthread, void **thread_return)
|
||||
/* Return the thread's return value: */
|
||||
*thread_return = pthread->ret;
|
||||
|
||||
_thread_leave_cancellation_point();
|
||||
|
||||
/* Return the completion status: */
|
||||
return (ret);
|
||||
}
|
||||
|
@ -67,11 +67,10 @@ _thread_kern_sched(ucontext_t * scp)
|
||||
char *fdata;
|
||||
#endif
|
||||
pthread_t pthread, pthread_h = NULL;
|
||||
pthread_t last_thread = NULL;
|
||||
struct itimerval itimer;
|
||||
struct timespec ts, ts1;
|
||||
struct timeval tv, tv1;
|
||||
int i, set_timer = 0;
|
||||
int set_timer = 0;
|
||||
|
||||
/*
|
||||
* Flag the pthread kernel as executing scheduler code
|
||||
@ -109,6 +108,20 @@ __asm__("fnsave %0": :"m"(*fdata));
|
||||
*/
|
||||
_thread_kern_in_sched = 0;
|
||||
|
||||
if (((_thread_run->cancelflags & PTHREAD_AT_CANCEL_POINT) == 0) &&
|
||||
((_thread_run->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) != 0)) {
|
||||
/*
|
||||
* Cancelations override signals.
|
||||
*
|
||||
* Stick a cancellation point at the start of
|
||||
* each async-cancellable thread's resumption.
|
||||
*
|
||||
* We allow threads woken at cancel points to do their
|
||||
* own checks.
|
||||
*/
|
||||
pthread_testcancel();
|
||||
}
|
||||
|
||||
if (_sched_switch_hook != NULL) {
|
||||
/* Run the installed switch hook: */
|
||||
thread_run_switch_hook(_last_user_thread, _thread_run);
|
||||
@ -161,6 +174,7 @@ __asm__("fnsave %0": :"m"(*fdata));
|
||||
*/
|
||||
switch (_thread_run->state) {
|
||||
case PS_DEAD:
|
||||
case PS_STATE_MAX: /* to silence -Wall */
|
||||
/*
|
||||
* Dead threads are not placed in any queue:
|
||||
*/
|
||||
@ -249,6 +263,7 @@ __asm__("fnsave %0": :"m"(*fdata));
|
||||
|
||||
/* Insert into the work queue: */
|
||||
PTHREAD_WORKQ_INSERT(_thread_run);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -627,14 +642,12 @@ _thread_kern_sched_state_unlock(enum pthread_state state,
|
||||
static void
|
||||
_thread_kern_poll(int wait_reqd)
|
||||
{
|
||||
char bufr[128];
|
||||
int count = 0;
|
||||
int i, found;
|
||||
int kern_pipe_added = 0;
|
||||
int nfds = 0;
|
||||
int timeout_ms = 0;
|
||||
struct pthread *pthread, *pthread_next;
|
||||
ssize_t num;
|
||||
struct pthread *pthread;
|
||||
struct timespec ts;
|
||||
struct timeval tv;
|
||||
|
||||
@ -1103,10 +1116,10 @@ thread_run_switch_hook(pthread_t thread_out, pthread_t thread_in)
|
||||
pthread_t tid_in = thread_in;
|
||||
|
||||
if ((tid_out != NULL) &&
|
||||
(tid_out->flags & PTHREAD_FLAGS_PRIVATE != 0))
|
||||
(tid_out->flags & PTHREAD_FLAGS_PRIVATE) != 0)
|
||||
tid_out = NULL;
|
||||
if ((tid_in != NULL) &&
|
||||
(tid_in->flags & PTHREAD_FLAGS_PRIVATE != 0))
|
||||
(tid_in->flags & PTHREAD_FLAGS_PRIVATE) != 0)
|
||||
tid_in = NULL;
|
||||
|
||||
if ((_sched_switch_hook != NULL) && (tid_out != tid_in)) {
|
||||
|
40
lib/libkse/thread/thr_msync.c
Normal file
40
lib/libkse/thread/thr_msync.c
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* David Leonard <d@openbsd.org>, 1999. Public Domain.
|
||||
*
|
||||
* $OpenBSD: uthread_msync.c,v 1.2 1999/06/09 07:16:17 d Exp $
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/mman.h>
|
||||
#ifdef _THREAD_SAFE
|
||||
#include <pthread.h>
|
||||
#include "pthread_private.h"
|
||||
|
||||
int
|
||||
msync(addr, len, flags)
|
||||
void *addr;
|
||||
size_t len;
|
||||
int flags;
|
||||
{
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* XXX This is quite pointless unless we know how to get the
|
||||
* file descriptor associated with the memory, and lock it for
|
||||
* write. The only real use of this wrapper is to guarantee
|
||||
* a cancellation point, as per the standard. sigh.
|
||||
*/
|
||||
|
||||
/* This is a cancellation point: */
|
||||
_thread_enter_cancellation_point();
|
||||
|
||||
ret = _thread_sys_msync(addr, len, flags);
|
||||
|
||||
/* No longer in a cancellation point: */
|
||||
_thread_leave_cancellation_point();
|
||||
|
||||
return (ret);
|
||||
}
|
||||
#endif
|
@ -94,7 +94,8 @@ _mutex_reinit(pthread_mutex_t * mutex)
|
||||
TAILQ_INIT(&(*mutex)->m_queue);
|
||||
(*mutex)->m_owner = NULL;
|
||||
(*mutex)->m_data.m_count = 0;
|
||||
(*mutex)->m_flags = MUTEX_FLAGS_INITED;
|
||||
(*mutex)->m_flags &= MUTEX_FLAGS_PRIVATE;
|
||||
(*mutex)->m_flags |= MUTEX_FLAGS_INITED;
|
||||
(*mutex)->m_refcount = 0;
|
||||
(*mutex)->m_prio = 0;
|
||||
(*mutex)->m_saved_prio = 0;
|
||||
@ -428,6 +429,9 @@ pthread_mutex_lock(pthread_mutex_t * mutex)
|
||||
_MUTEX_INIT_LINK(*mutex);
|
||||
}
|
||||
|
||||
/* Reset the interrupted flag: */
|
||||
_thread_run->interrupted = 0;
|
||||
|
||||
/* Process according to mutex type: */
|
||||
switch ((*mutex)->m_protocol) {
|
||||
/* Default POSIX mutex: */
|
||||
@ -602,6 +606,13 @@ pthread_mutex_lock(pthread_mutex_t * mutex)
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check to see if this thread was interrupted and
|
||||
* is still in the mutex queue of waiting threads:
|
||||
*/
|
||||
if (_thread_run->interrupted != 0)
|
||||
mutex_queue_remove(*mutex, _thread_run);
|
||||
|
||||
/* Unlock the mutex structure: */
|
||||
_SPINUNLOCK(&(*mutex)->lock);
|
||||
|
||||
@ -610,6 +621,11 @@ pthread_mutex_lock(pthread_mutex_t * mutex)
|
||||
* necessary:
|
||||
*/
|
||||
_thread_kern_sig_undefer();
|
||||
|
||||
if ((_thread_run->cancelflags & PTHREAD_CANCEL_NEEDED) != 0) {
|
||||
_thread_exit_cleanup();
|
||||
pthread_exit(PTHREAD_CANCELED);
|
||||
}
|
||||
}
|
||||
|
||||
/* Return the completion status: */
|
||||
@ -1314,6 +1330,18 @@ mutex_rescan_owned (pthread_t pthread, pthread_mutex_t mutex)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
_mutex_unlock_private(pthread_t pthread)
|
||||
{
|
||||
struct pthread_mutex *m, *m_next;
|
||||
|
||||
for (m = TAILQ_FIRST(&pthread->mutexq); m != NULL; m = m_next) {
|
||||
m_next = TAILQ_NEXT(m, m_qe);
|
||||
if ((m->m_flags & MUTEX_FLAGS_PRIVATE) != 0)
|
||||
pthread_mutex_unlock(&m);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Dequeue a waiting thread from the head of a mutex queue in descending
|
||||
* priority order.
|
||||
@ -1323,8 +1351,17 @@ mutex_queue_deq(pthread_mutex_t mutex)
|
||||
{
|
||||
pthread_t pthread;
|
||||
|
||||
if ((pthread = TAILQ_FIRST(&mutex->m_queue)) != NULL)
|
||||
while ((pthread = TAILQ_FIRST(&mutex->m_queue)) != NULL) {
|
||||
TAILQ_REMOVE(&mutex->m_queue, pthread, qe);
|
||||
pthread->flags &= ~PTHREAD_FLAGS_IN_MUTEXQ;
|
||||
|
||||
/*
|
||||
* Only exit the loop if the thread hasn't been
|
||||
* cancelled.
|
||||
*/
|
||||
if (pthread->interrupted == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return(pthread);
|
||||
}
|
||||
@ -1335,7 +1372,10 @@ mutex_queue_deq(pthread_mutex_t mutex)
|
||||
static inline void
|
||||
mutex_queue_remove(pthread_mutex_t mutex, pthread_t pthread)
|
||||
{
|
||||
TAILQ_REMOVE(&mutex->m_queue, pthread, qe);
|
||||
if ((pthread->flags & PTHREAD_FLAGS_IN_MUTEXQ) != 0) {
|
||||
TAILQ_REMOVE(&mutex->m_queue, pthread, qe);
|
||||
pthread->flags &= ~PTHREAD_FLAGS_IN_MUTEXQ;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1359,6 +1399,7 @@ mutex_queue_enq(pthread_mutex_t mutex, pthread_t pthread)
|
||||
tid = TAILQ_NEXT(tid, qe);
|
||||
TAILQ_INSERT_BEFORE(tid, pthread, qe);
|
||||
}
|
||||
pthread->flags |= PTHREAD_FLAGS_IN_MUTEXQ;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -47,6 +47,7 @@ nanosleep(const struct timespec * time_to_sleep,
|
||||
struct timespec remaining_time;
|
||||
struct timeval tv;
|
||||
|
||||
_thread_enter_cancellation_point();
|
||||
/* Check if the time to sleep is legal: */
|
||||
if (time_to_sleep == NULL || time_to_sleep->tv_sec < 0 ||
|
||||
time_to_sleep->tv_nsec < 0 || time_to_sleep->tv_nsec >= 1000000000) {
|
||||
@ -116,6 +117,7 @@ nanosleep(const struct timespec * time_to_sleep,
|
||||
ret = -1;
|
||||
}
|
||||
}
|
||||
_thread_leave_cancellation_point();
|
||||
return (ret);
|
||||
}
|
||||
#endif
|
||||
|
@ -44,10 +44,11 @@
|
||||
int
|
||||
open(const char *path, int flags,...)
|
||||
{
|
||||
int fd;
|
||||
int mode = 0;
|
||||
int status;
|
||||
va_list ap;
|
||||
int fd;
|
||||
int mode = 0;
|
||||
va_list ap;
|
||||
|
||||
_thread_enter_cancellation_point();
|
||||
|
||||
/* Check if the file is being created: */
|
||||
if (flags & O_CREAT) {
|
||||
@ -68,6 +69,8 @@ open(const char *path, int flags,...)
|
||||
fd = -1;
|
||||
}
|
||||
|
||||
_thread_leave_cancellation_point();
|
||||
|
||||
/* Return the file descriptor or -1 on error: */
|
||||
return (fd);
|
||||
}
|
||||
|
@ -253,7 +253,7 @@ struct pthread_mutex {
|
||||
*/
|
||||
#define PTHREAD_MUTEX_STATIC_INITIALIZER \
|
||||
{ PTHREAD_MUTEX_DEFAULT, PTHREAD_PRIO_NONE, TAILQ_INITIALIZER, \
|
||||
NULL, { NULL }, 0, 0, 0, 0, TAILQ_INITIALIZER, \
|
||||
NULL, { NULL }, MUTEX_FLAGS_PRIVATE, 0, 0, 0, TAILQ_INITIALIZER, \
|
||||
_SPINLOCK_INITIALIZER }
|
||||
|
||||
struct pthread_mutex_attr {
|
||||
@ -513,6 +513,15 @@ struct pthread {
|
||||
*/
|
||||
int sig_saved;
|
||||
|
||||
/*
|
||||
* Cancelability flags - the lower 2 bits are used by cancel
|
||||
* definitions in pthread.h
|
||||
*/
|
||||
#define PTHREAD_AT_CANCEL_POINT 0x0004
|
||||
#define PTHREAD_CANCELLING 0x0008
|
||||
#define PTHREAD_CANCEL_NEEDED 0x0010
|
||||
int cancelflags;
|
||||
|
||||
/*
|
||||
* Current signal mask and pending signals.
|
||||
*/
|
||||
@ -610,15 +619,18 @@ struct pthread {
|
||||
*/
|
||||
int yield_on_sig_undefer;
|
||||
|
||||
/* Miscellaneous data. */
|
||||
/* Miscellaneous flags; only set with signals deferred. */
|
||||
int flags;
|
||||
#define PTHREAD_FLAGS_PRIVATE 0x0001
|
||||
#define PTHREAD_EXITING 0x0002
|
||||
#define PTHREAD_FLAGS_IN_CONDQ 0x0004 /* in condition queue using qe link*/
|
||||
#define PTHREAD_FLAGS_IN_WORKQ 0x0008 /* in work queue using qe link */
|
||||
#define PTHREAD_FLAGS_IN_WAITQ 0x0010 /* in waiting queue using pqe link*/
|
||||
#define PTHREAD_FLAGS_IN_PRIOQ 0x0020 /* in priority queue using pqe link*/
|
||||
#define PTHREAD_FLAGS_TRACE 0x0040 /* for debugging purposes */
|
||||
#define PTHREAD_FLAGS_IN_WAITQ 0x0010 /* in waiting queue using pqe link */
|
||||
#define PTHREAD_FLAGS_IN_PRIOQ 0x0020 /* in priority queue using pqe link */
|
||||
#define PTHREAD_FLAGS_IN_MUTEXQ 0x0040 /* in mutex queue using qe link */
|
||||
#define PTHREAD_FLAGS_IN_FILEQ 0x0080 /* in file lock queue using qe link */
|
||||
#define PTHREAD_FLAGS_IN_FDQ 0x0100 /* in fd lock queue using qe link */
|
||||
#define PTHREAD_FLAGS_TRACE 0x0200 /* for debugging purposes */
|
||||
|
||||
/*
|
||||
* Base priority is the user setable and retrievable priority
|
||||
@ -925,6 +937,7 @@ char *__ttyname_r_basic(int, char *, size_t);
|
||||
char *ttyname_r(int, char *, size_t);
|
||||
int _find_dead_thread(pthread_t);
|
||||
int _find_thread(pthread_t);
|
||||
void _funlock_owned(pthread_t);
|
||||
int _thread_create(pthread_t *,const pthread_attr_t *,void *(*start_routine)(void *),void *,pthread_t);
|
||||
int _thread_fd_lock(int, int, struct timespec *);
|
||||
int _thread_fd_lock_debug(int, int, struct timespec *,char *fname,int lineno);
|
||||
@ -932,8 +945,9 @@ void _dispatch_signals(void);
|
||||
void _thread_signal(pthread_t, int);
|
||||
int _mutex_cv_lock(pthread_mutex_t *);
|
||||
int _mutex_cv_unlock(pthread_mutex_t *);
|
||||
void _mutex_notify_priochange(pthread_t);
|
||||
int _mutex_reinit(pthread_mutex_t *);
|
||||
void _mutex_notify_priochange(struct pthread *);
|
||||
void _mutex_unlock_private(pthread_t);
|
||||
int _cond_reinit(pthread_cond_t *);
|
||||
int _pq_alloc(struct pq_queue *, int, int);
|
||||
int _pq_init(struct pq_queue *);
|
||||
@ -948,8 +962,10 @@ void _waitq_setactive(void);
|
||||
void _waitq_clearactive(void);
|
||||
#endif
|
||||
void _thread_exit(char *, int, char *);
|
||||
void _thread_exit_cleanup(void);
|
||||
void _thread_fd_unlock(int, int);
|
||||
void _thread_fd_unlock_debug(int, int, char *, int);
|
||||
void _thread_fd_unlock_owned(pthread_t);
|
||||
void *_thread_cleanup(pthread_t);
|
||||
void _thread_cleanupspecific(void);
|
||||
void _thread_dump_info(void);
|
||||
@ -969,6 +985,9 @@ void _thread_start_sig_handler(void);
|
||||
void _thread_seterrno(pthread_t,int);
|
||||
int _thread_fd_table_init(int fd);
|
||||
pthread_addr_t _thread_gc(pthread_addr_t);
|
||||
void _thread_enter_cancellation_point(void);
|
||||
void _thread_leave_cancellation_point(void);
|
||||
void _thread_cancellation_point(void);
|
||||
|
||||
/* #include <signal.h> */
|
||||
int _thread_sys_sigaction(int, const struct sigaction *, struct sigaction *);
|
||||
@ -1148,6 +1167,8 @@ pid_t _thread_sys_wait4(pid_t, int *, int, struct rusage *);
|
||||
#ifdef _SYS_POLL_H_
|
||||
int _thread_sys_poll(struct pollfd *, unsigned, int);
|
||||
#endif
|
||||
/* #include <sys/mman.h> */
|
||||
int _thread_sys_msync(void *, size_t, int);
|
||||
__END_DECLS
|
||||
|
||||
#endif /* !_PTHREAD_PRIVATE_H */
|
||||
|
@ -47,9 +47,13 @@ read(int fd, void *buf, size_t nbytes)
|
||||
int ret;
|
||||
int type;
|
||||
|
||||
_thread_enter_cancellation_point();
|
||||
|
||||
/* POSIX says to do just this: */
|
||||
if (nbytes == 0)
|
||||
if (nbytes == 0) {
|
||||
_thread_leave_cancellation_point();
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Lock the file descriptor for read: */
|
||||
if ((ret = _FD_LOCK(fd, FD_READ, NULL)) == 0) {
|
||||
@ -61,6 +65,7 @@ read(int fd, void *buf, size_t nbytes)
|
||||
/* File is not open for read: */
|
||||
errno = EBADF;
|
||||
_FD_UNLOCK(fd, FD_READ);
|
||||
_thread_leave_cancellation_point();
|
||||
return (-1);
|
||||
}
|
||||
|
||||
@ -92,6 +97,7 @@ read(int fd, void *buf, size_t nbytes)
|
||||
}
|
||||
_FD_UNLOCK(fd, FD_READ);
|
||||
}
|
||||
_thread_leave_cancellation_point();
|
||||
return (ret);
|
||||
}
|
||||
#endif
|
||||
|
@ -38,7 +38,8 @@
|
||||
#include "pthread_private.h"
|
||||
|
||||
int
|
||||
pthread_setschedparam(pthread_t pthread, int policy, struct sched_param *param)
|
||||
pthread_setschedparam(pthread_t pthread, int policy,
|
||||
const struct sched_param *param)
|
||||
{
|
||||
int old_prio, in_readyq = 0, ret = 0;
|
||||
|
||||
|
@ -47,6 +47,7 @@ sigwait(const sigset_t * set, int *sig)
|
||||
sigset_t tempset, waitset;
|
||||
struct sigaction act;
|
||||
|
||||
_thread_enter_cancellation_point();
|
||||
/*
|
||||
* Specify the thread kernel signal handler.
|
||||
*/
|
||||
@ -85,6 +86,7 @@ sigwait(const sigset_t * set, int *sig)
|
||||
/* Return the signal number to the caller: */
|
||||
*sig = i;
|
||||
|
||||
_thread_leave_cancellation_point();
|
||||
return (0);
|
||||
}
|
||||
|
||||
@ -137,6 +139,7 @@ sigwait(const sigset_t * set, int *sig)
|
||||
}
|
||||
}
|
||||
|
||||
_thread_leave_cancellation_point();
|
||||
/* Return the completion status: */
|
||||
return (ret);
|
||||
}
|
||||
|
@ -42,6 +42,7 @@ wait4(pid_t pid, int *istat, int options, struct rusage * rusage)
|
||||
{
|
||||
pid_t ret;
|
||||
|
||||
_thread_enter_cancellation_point();
|
||||
_thread_kern_sig_defer();
|
||||
|
||||
/* Perform a non-blocking wait4 syscall: */
|
||||
@ -61,6 +62,7 @@ wait4(pid_t pid, int *istat, int options, struct rusage * rusage)
|
||||
}
|
||||
|
||||
_thread_kern_sig_undefer();
|
||||
_thread_leave_cancellation_point();
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
@ -50,9 +50,12 @@ write(int fd, const void *buf, size_t nbytes)
|
||||
ssize_t num = 0;
|
||||
ssize_t ret;
|
||||
|
||||
_thread_enter_cancellation_point();
|
||||
/* POSIX says to do just this: */
|
||||
if (nbytes == 0)
|
||||
if (nbytes == 0) {
|
||||
_thread_leave_cancellation_point();
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Lock the file descriptor for write: */
|
||||
if ((ret = _FD_LOCK(fd, FD_WRITE, NULL)) == 0) {
|
||||
@ -64,7 +67,8 @@ write(int fd, const void *buf, size_t nbytes)
|
||||
/* File is not open for write: */
|
||||
errno = EBADF;
|
||||
_FD_UNLOCK(fd, FD_WRITE);
|
||||
return (-1);
|
||||
_thread_leave_cancellation_point();
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/* Check if file operations are to block */
|
||||
@ -129,6 +133,7 @@ write(int fd, const void *buf, size_t nbytes)
|
||||
}
|
||||
_FD_UNLOCK(fd, FD_RDWR);
|
||||
}
|
||||
_thread_leave_cancellation_point();
|
||||
return (ret);
|
||||
}
|
||||
#endif
|
||||
|
@ -15,6 +15,9 @@ CFLAGS+=-DPTHREAD_KERNEL -D_THREAD_SAFE -I${.CURDIR}/uthread
|
||||
# thread locking.
|
||||
CFLAGS+=-D_LOCK_DEBUG
|
||||
|
||||
# enable extra internal consistancy checks
|
||||
# CFLAGS+=-D_PTHREADS_INVARIANTS
|
||||
|
||||
AINC= -I${.CURDIR}/../libc/${MACHINE_ARCH} -I${.CURDIR}/uthread
|
||||
PRECIOUSLIB= yes
|
||||
|
||||
@ -25,11 +28,12 @@ PRECIOUSLIB= yes
|
||||
HIDDEN_SYSCALLS= accept.o bind.o close.o connect.o dup.o dup2.o \
|
||||
execve.o fchflags.o fchmod.o fchown.o fcntl.o \
|
||||
flock.o fpathconf.o fstat.o fstatfs.o fsync.o getdirentries.o \
|
||||
getpeername.o getsockname.o getsockopt.o ioctl.o listen.o \
|
||||
nanosleep.o nfssvc.o open.o poll.o read.o readv.o recvfrom.o \
|
||||
getlogin.o getpeername.o getsockname.o getsockopt.o ioctl.o listen.o \
|
||||
msync.o nanosleep.o nfssvc.o open.o poll.o read.o readv.o recvfrom.o \
|
||||
recvmsg.o sched_yield.o select.o sendmsg.o sendto.o \
|
||||
setsockopt.o shutdown.o sigaction.o sigaltstack.o \
|
||||
signanosleep.o sigpending.o sigprocmask.o sigsuspend.o socket.o \
|
||||
signanosleep.o sigpending.o sigprocmask.o sigreturn.o sigsetmask.o \
|
||||
sigsuspend.o socket.o \
|
||||
socketpair.o wait4.o write.o writev.o
|
||||
|
||||
.include "${.CURDIR}/../libc/Makefile.inc"
|
||||
|
@ -12,6 +12,7 @@ MAN3+= pthread_cleanup_pop.3 \
|
||||
pthread_cond_signal.3 \
|
||||
pthread_cond_timedwait.3 \
|
||||
pthread_cond_wait.3 \
|
||||
pthread_cancel.3 \
|
||||
pthread_create.3 \
|
||||
pthread_detach.3 \
|
||||
pthread_equal.3 \
|
||||
@ -36,4 +37,8 @@ MAN3+= pthread_cleanup_pop.3 \
|
||||
pthread_rwlockattr_init.3 \
|
||||
pthread_rwlockattr_setpshared.3 \
|
||||
pthread_self.3 \
|
||||
pthread_setspecific.3
|
||||
pthread_setspecific.3 \
|
||||
pthread_testcancel.3
|
||||
|
||||
MLINKS+= pthread_cancel.3 pthread_setcancelstate.3 \
|
||||
pthread_cancel.3 pthread_getcancelstate.3
|
||||
|
70
lib/libpthread/man/pthread_cancel.3
Normal file
70
lib/libpthread/man/pthread_cancel.3
Normal file
@ -0,0 +1,70 @@
|
||||
.\" $FreeBSD$
|
||||
.Dd January 17, 1999
|
||||
.Dt PTHREAD_CANCEL 3
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm pthread_cancel
|
||||
.Nd cancel execution of a thread
|
||||
.Sh SYNOPSIS
|
||||
.Fd #include <pthread.h>
|
||||
.Ft int
|
||||
.Fn pthread_cancel "pthread_t thread"
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Fn pthread_cancel
|
||||
function requests that
|
||||
.Fa thread
|
||||
be canceled. The target thread's cancelability state and type determines
|
||||
when the cancellation takes effect. When the cancellation is acted on,
|
||||
the cancellation cleanup handlers for
|
||||
.Fa thread
|
||||
are called. When the last cancellation cleanup handler returns,
|
||||
the thread-specific data destructor functions will be called for
|
||||
.Fa thread .
|
||||
When the last destructor function returns,
|
||||
.Fa thread
|
||||
will be terminated.
|
||||
.Pp
|
||||
The cancellation processing in the target thread runs asynchronously with
|
||||
respect to the calling thread returning from
|
||||
.Fn pthread_cancel .
|
||||
.Pp
|
||||
A status of
|
||||
.Dv PTHREAD_CANCELED
|
||||
is made available to any threads joining with the target. The symbolic
|
||||
constant
|
||||
.Dv PTHREAD_CANCELED
|
||||
expands to a constant expression of type
|
||||
.Ft "(void *)" ,
|
||||
whose value matches no pointer to an object in memory nor the value
|
||||
.Dv NULL .
|
||||
.Sh RETURN VALUES
|
||||
If successful, the
|
||||
.Fn pthread_cancel
|
||||
functions will return zero. Otherwise an error number will be returned to
|
||||
indicate the error.
|
||||
.Sh ERRORS
|
||||
.Fn pthread_cancel
|
||||
will fail if:
|
||||
.Bl -tag -width Er
|
||||
.It Bq Er ESRCH
|
||||
No thread could be found corresponding to that specified by the given
|
||||
thread ID.
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr pthread_cleanup_pop 3 ,
|
||||
.Xr pthread_cleanup_push 3 ,
|
||||
.Xr pthread_exit 3 ,
|
||||
.Xr pthread_join 3 ,
|
||||
.Xr pthread_setcancelstate 3 ,
|
||||
.Xr pthread_setcanceltype 3 ,
|
||||
.Xr pthread_testcancel 3
|
||||
.Sh STANDARDS
|
||||
.Fn pthread_cancel
|
||||
conforms to ISO/IEC 9945-1 ANSI/IEEE
|
||||
.Pq Dq Tn POSIX
|
||||
Std 1003.1 Second Edition 1996-07-12.
|
||||
.Sh AUTHORS
|
||||
This man page was written by
|
||||
.An David Leonard <d@openbsd.org>
|
||||
for the OpenBSD implementation of pthread_cancel.
|
187
lib/libpthread/man/pthread_testcancel.3
Normal file
187
lib/libpthread/man/pthread_testcancel.3
Normal file
@ -0,0 +1,187 @@
|
||||
.\" $FreeBSD$
|
||||
.Dd January 17, 1999
|
||||
.Dt PTHREAD_TESTCANCEL 3
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm pthread_setcancelstate ,
|
||||
.Nm pthread_setcanceltype ,
|
||||
.Nm pthread_testcancel
|
||||
.Nd set cancelability state
|
||||
.Sh SYNOPSIS
|
||||
.Fd #include <pthread.h>
|
||||
.Ft int
|
||||
.Fn pthread_setcancelstate "int state" "int *oldstate"
|
||||
.Ft int
|
||||
.Fn pthread_setcanceltype "int type" "int *oldtype"
|
||||
.Ft void
|
||||
.Fn pthread_testcancel "void"
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Fn pthread_setcancelstate
|
||||
function atomically both sets the calling thread's cancelability state
|
||||
to the indicated
|
||||
.Fa state
|
||||
and returns the previous cancelability state at the location referenced by
|
||||
.Fa oldstate .
|
||||
Legal values for
|
||||
.Fa state
|
||||
are
|
||||
.Dv PTHREAD_CANCEL_ENABLE
|
||||
and
|
||||
.Dv PTHREAD_CANCEL_DISABLE .
|
||||
.Pp
|
||||
The
|
||||
.Fn pthread_setcanceltype
|
||||
function atomically both sets the calling thread's cancelability type
|
||||
to the indicated
|
||||
.Fa type
|
||||
and returns the previous cancelability type at the location referenced by
|
||||
.Fa oldtype .
|
||||
Legal values for
|
||||
.Fa type
|
||||
are
|
||||
.Dv PTHREAD_CANCEL_DEFERRED
|
||||
and
|
||||
.Dv PTHREAD_CANCEL_ASYNCHRONOUS .
|
||||
.Pp
|
||||
The cancelability state and type of any newly created threads, including the
|
||||
thread in which
|
||||
.Fn main
|
||||
was first invoked, are
|
||||
.Dv PTHREAD_CANCEL_ENABLE
|
||||
and
|
||||
.Dv PTHREAD_CANCEL_DEFERRED
|
||||
respectively.
|
||||
.Pp
|
||||
The
|
||||
.Fn pthread_testcancel
|
||||
function creates a cancellation point in the calling thread. The
|
||||
.Fn pthread_testcancel
|
||||
function has no effect if cancelability is disabled.
|
||||
.Pp
|
||||
.Ss Cancelability States
|
||||
The cancelability state of a thread determines the action taken upon
|
||||
receipt of a cancellation request. The thread may control cancellation in
|
||||
a number of ways.
|
||||
.Pp
|
||||
Each thread maintains its own
|
||||
.Dq cancelability state
|
||||
which may be encoded in two bits:
|
||||
.Bl -hang
|
||||
.It Em Cancelability Enable
|
||||
When cancelability is
|
||||
.Dv PTHREAD_CANCEL_DISABLE ,
|
||||
cancellation requests against the target thread are held pending.
|
||||
.It Em Cancelability Type
|
||||
When cancelability is enabled and the cancelability type is
|
||||
.Dv PTHREAD_CANCEL_ASYNCHRONOUS ,
|
||||
new or pending cancellation requests may be acted upon at any time.
|
||||
When cancelability is enabled and the cancelability type is
|
||||
.Dv PTHREAD_CANCEL_DEFERRED ,
|
||||
cancellation requests are held pending until a cancellation point (see
|
||||
below) is reached. If cancelability is disabled, the setting of the
|
||||
cancelability type has no immediate effect as all cancellation requests
|
||||
are held pending; however, once cancelability is enabled again the new
|
||||
type will be in effect.
|
||||
.El
|
||||
.Ss Cancellation Points
|
||||
Cancellation points will occur when a thread is executing the following
|
||||
functions:
|
||||
.Fn close ,
|
||||
.Fn creat ,
|
||||
.Fn fcntl ,
|
||||
.Fn fsync ,
|
||||
.Fn msync ,
|
||||
.Fn nanosleep ,
|
||||
.Fn open ,
|
||||
.Fn pause ,
|
||||
.Fn pthread_cond_timedwait ,
|
||||
.Fn pthread_cond_wait ,
|
||||
.Fn pthread_join ,
|
||||
.Fn pthread_testcancel ,
|
||||
.Fn read ,
|
||||
.Fn sigwaitinfo ,
|
||||
.Fn sigsuspend ,
|
||||
.Fn sigwait ,
|
||||
.Fn sleep ,
|
||||
.Fn system ,
|
||||
.Fn tcdrain ,
|
||||
.Fn wait ,
|
||||
.Fn waitpid ,
|
||||
.Fn write .
|
||||
.Sh RETURN VALUES
|
||||
If successful, the
|
||||
.Fn pthread_setcancelstate
|
||||
and
|
||||
.Fn pthread_setcanceltype
|
||||
functions will return zero. Otherwise, an error number shall be returned to
|
||||
indicate the error.
|
||||
.Pp
|
||||
The
|
||||
.Fn pthread_setcancelstate
|
||||
and
|
||||
.Fn pthread_setcanceltype
|
||||
functions are used to control the points at which a thread may be
|
||||
asynchronously canceled. For cancellation control to be usable in modular
|
||||
fashion, some rules must be followed.
|
||||
.Pp
|
||||
For purposes of this discussion, consider an object to be a generalization
|
||||
of a procedure. It is a set of procedures and global variables written as
|
||||
a unit and called by clients not known by the object. Objects may depend
|
||||
on other objects.
|
||||
.Pp
|
||||
First, cancelability should only be disabled on entry to an object, never
|
||||
explicitly enabled. On exit from an object, the cancelability state should
|
||||
always be restored to its value on entry to the object.
|
||||
.Pp
|
||||
This follows from a modularity argument: if the client of an object (or the
|
||||
client of an object that uses that object) has disabled cancelability, it is
|
||||
because the client doesn't want to have to worry about how to clean up if the
|
||||
thread is canceled while executing some sequence of actions. If an object
|
||||
is called in such a state and it enables cancelability and a cancellation
|
||||
request is pending for that thread, then the thread will be canceled,
|
||||
contrary to the wish of the client that disabled.
|
||||
.Pp
|
||||
Second, the cancelability type may be explicitly set to either
|
||||
.Em deferred
|
||||
or
|
||||
.Em asynchronous
|
||||
upon entry to an object. But as with the cancelability state, on exit from
|
||||
an object that cancelability type should always be restored to its value on
|
||||
entry to the object.
|
||||
.Pp
|
||||
Finally, only functions that are cancel-safe may be called from a thread that
|
||||
is asynchronously cancelable.
|
||||
.Sh ERRORS
|
||||
The function
|
||||
.Fn pthread_setcancelstate
|
||||
may fail with:
|
||||
.Bl -tag -width Er
|
||||
.It Bq Er EINVAL
|
||||
The specified state is not
|
||||
.Dv PTHREAD_CANCEL_ENABLE
|
||||
or
|
||||
.Dv PTHREAD_CANCEL_DISABLE .
|
||||
.El
|
||||
.Pp
|
||||
The function
|
||||
.Fn pthread_setcanceltype
|
||||
may fail with:
|
||||
.Bl -tag -width Er
|
||||
.It Bq Er EINVAL
|
||||
The specified state is not
|
||||
.Dv PTHREAD_CANCEL_DEFERRED
|
||||
or
|
||||
.Dv PTHREAD_CANCEL_ASYNCHRONOUS .
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr pthread_cancel 3
|
||||
.Sh STANDARDS
|
||||
.Fn pthread_testcancel
|
||||
conforms to ISO/IEC 9945-1 ANSI/IEEE
|
||||
.Pq Dq Tn POSIX
|
||||
Std 1003.1 Second Edition 1996-07-12.
|
||||
.Sh AUTHORS
|
||||
This man page was written by
|
||||
.An David Leonard <d@openbsd.org>
|
||||
for the OpenBSD implementation of pthread_cancel.
|
@ -24,6 +24,7 @@ SRCS+= \
|
||||
uthread_attr_setstacksize.c \
|
||||
uthread_autoinit.cc \
|
||||
uthread_bind.c \
|
||||
uthread_cancel.c \
|
||||
uthread_clean.c \
|
||||
uthread_close.c \
|
||||
uthread_cond.c \
|
||||
@ -37,6 +38,7 @@ SRCS+= \
|
||||
uthread_equal.c \
|
||||
uthread_execve.c \
|
||||
uthread_exit.c \
|
||||
uthread_fchflags.c \
|
||||
uthread_fchmod.c \
|
||||
uthread_fchown.c \
|
||||
uthread_fcntl.c \
|
||||
@ -64,6 +66,7 @@ SRCS+= \
|
||||
uthread_listen.c \
|
||||
uthread_mattr_init.c \
|
||||
uthread_mattr_kind_np.c \
|
||||
uthread_msync.c \
|
||||
uthread_multi_np.c \
|
||||
uthread_mutex.c \
|
||||
uthread_mutex_prioceiling.c \
|
||||
|
@ -37,7 +37,7 @@
|
||||
#include "pthread_private.h"
|
||||
|
||||
int
|
||||
pthread_attr_getinheritsched(pthread_attr_t *attr, int *sched_inherit)
|
||||
pthread_attr_getinheritsched(const pthread_attr_t *attr, int *sched_inherit)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
|
@ -37,7 +37,7 @@
|
||||
#include "pthread_private.h"
|
||||
|
||||
int
|
||||
pthread_attr_getschedparam(pthread_attr_t *attr, struct sched_param *param)
|
||||
pthread_attr_getschedparam(const pthread_attr_t *attr, struct sched_param *param)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
|
@ -37,7 +37,7 @@
|
||||
#include "pthread_private.h"
|
||||
|
||||
int
|
||||
pthread_attr_getschedpolicy(pthread_attr_t *attr, int *policy)
|
||||
pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
|
@ -37,7 +37,7 @@
|
||||
#include "pthread_private.h"
|
||||
|
||||
int
|
||||
pthread_attr_getscope(pthread_attr_t *attr, int *contentionscope)
|
||||
pthread_attr_getscope(const pthread_attr_t *attr, int *contentionscope)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
|
179
lib/libpthread/thread/thr_cancel.c
Normal file
179
lib/libpthread/thread/thr_cancel.c
Normal file
@ -0,0 +1,179 @@
|
||||
/*
|
||||
* David Leonard <d@openbsd.org>, 1999. Public domain.
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <sys/errno.h>
|
||||
#include <pthread.h>
|
||||
#include "pthread_private.h"
|
||||
|
||||
int
|
||||
pthread_cancel(pthread_t pthread)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if ((ret = _find_thread(pthread)) != 0) {
|
||||
/* NOTHING */
|
||||
} else if (pthread->state == PS_DEAD || pthread->state == PS_DEADLOCK) {
|
||||
ret = 0;
|
||||
} else {
|
||||
/* Protect the scheduling queues: */
|
||||
_thread_kern_sig_defer();
|
||||
|
||||
/* Check if we need to kick it back into the run queue: */
|
||||
if ((pthread->cancelflags & PTHREAD_CANCEL_DISABLE) == 0)
|
||||
switch (pthread->state) {
|
||||
case PS_RUNNING:
|
||||
/* No need to resume: */
|
||||
pthread->cancelflags |= PTHREAD_CANCELLING;
|
||||
break;
|
||||
|
||||
case PS_SPINBLOCK:
|
||||
case PS_FDR_WAIT:
|
||||
case PS_FDW_WAIT:
|
||||
case PS_POLL_WAIT:
|
||||
case PS_SELECT_WAIT:
|
||||
/* Remove these threads from the work queue: */
|
||||
if ((pthread->flags & PTHREAD_FLAGS_IN_WORKQ)
|
||||
!= 0)
|
||||
PTHREAD_WORKQ_REMOVE(pthread);
|
||||
/* Fall through: */
|
||||
case PS_SIGTHREAD:
|
||||
case PS_SLEEP_WAIT:
|
||||
case PS_WAIT_WAIT:
|
||||
case PS_SIGSUSPEND:
|
||||
case PS_SIGWAIT:
|
||||
case PS_SUSPENDED:
|
||||
/* Interrupt and resume: */
|
||||
pthread->interrupted = 1;
|
||||
pthread->cancelflags |= PTHREAD_CANCELLING;
|
||||
PTHREAD_NEW_STATE(pthread,PS_RUNNING);
|
||||
break;
|
||||
|
||||
case PS_MUTEX_WAIT:
|
||||
case PS_COND_WAIT:
|
||||
case PS_FDLR_WAIT:
|
||||
case PS_FDLW_WAIT:
|
||||
case PS_FILE_WAIT:
|
||||
case PS_JOIN:
|
||||
/*
|
||||
* Threads in these states may be in queues.
|
||||
* In order to preserve queue integrity, the
|
||||
* cancelled thread must remove itself from the
|
||||
* queue. Mark the thread as interrupted and
|
||||
* needing cancellation, and set the state to
|
||||
* running. When the thread resumes, it will
|
||||
* exit after removing itself from the queue.
|
||||
*/
|
||||
pthread->interrupted = 1;
|
||||
pthread->cancelflags |= PTHREAD_CANCEL_NEEDED;
|
||||
PTHREAD_NEW_STATE(pthread,PS_RUNNING);
|
||||
break;
|
||||
|
||||
case PS_DEAD:
|
||||
case PS_DEADLOCK:
|
||||
case PS_STATE_MAX:
|
||||
/* Ignore - only here to silence -Wall: */
|
||||
break;
|
||||
}
|
||||
/* Unprotect the scheduling queues: */
|
||||
_thread_kern_sig_undefer();
|
||||
|
||||
ret = 0;
|
||||
}
|
||||
return (ret);
|
||||
}
|
||||
|
||||
int
|
||||
pthread_setcancelstate(int state, int *oldstate)
|
||||
{
|
||||
int ostate;
|
||||
int ret;
|
||||
|
||||
ostate = _thread_run->cancelflags & PTHREAD_CANCEL_DISABLE;
|
||||
|
||||
switch (state) {
|
||||
case PTHREAD_CANCEL_ENABLE:
|
||||
if (oldstate != NULL)
|
||||
*oldstate = ostate;
|
||||
_thread_run->cancelflags &= PTHREAD_CANCEL_ENABLE;
|
||||
if ((_thread_run->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) != 0)
|
||||
pthread_testcancel();
|
||||
ret = 0;
|
||||
break;
|
||||
case PTHREAD_CANCEL_DISABLE:
|
||||
if (oldstate != NULL)
|
||||
*oldstate = ostate;
|
||||
_thread_run->cancelflags |= PTHREAD_CANCEL_DISABLE;
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
ret = EINVAL;
|
||||
}
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
int
|
||||
pthread_setcanceltype(int type, int *oldtype)
|
||||
{
|
||||
int otype;
|
||||
int ret;
|
||||
|
||||
otype = _thread_run->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS;
|
||||
switch (type) {
|
||||
case PTHREAD_CANCEL_ASYNCHRONOUS:
|
||||
if (oldtype != NULL)
|
||||
*oldtype = otype;
|
||||
_thread_run->cancelflags |= PTHREAD_CANCEL_ASYNCHRONOUS;
|
||||
pthread_testcancel();
|
||||
ret = 0;
|
||||
break;
|
||||
case PTHREAD_CANCEL_DEFERRED:
|
||||
if (oldtype != NULL)
|
||||
*oldtype = otype;
|
||||
_thread_run->cancelflags &= ~PTHREAD_CANCEL_ASYNCHRONOUS;
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
ret = EINVAL;
|
||||
}
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
void
|
||||
pthread_testcancel(void)
|
||||
{
|
||||
|
||||
if (((_thread_run->cancelflags & PTHREAD_CANCEL_DISABLE) == 0) &&
|
||||
((_thread_run->cancelflags & PTHREAD_CANCELLING) != 0)) {
|
||||
/*
|
||||
* It is possible for this thread to be swapped out
|
||||
* while performing cancellation; do not allow it
|
||||
* to be cancelled again.
|
||||
*/
|
||||
_thread_run->cancelflags &= ~PTHREAD_CANCELLING;
|
||||
_thread_exit_cleanup();
|
||||
pthread_exit(PTHREAD_CANCELED);
|
||||
PANIC("cancel");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
_thread_enter_cancellation_point(void)
|
||||
{
|
||||
|
||||
/* Look for a cancellation before we block: */
|
||||
pthread_testcancel();
|
||||
_thread_run->cancelflags |= PTHREAD_AT_CANCEL_POINT;
|
||||
}
|
||||
|
||||
void
|
||||
_thread_leave_cancellation_point(void)
|
||||
{
|
||||
|
||||
_thread_run->cancelflags &= ~PTHREAD_AT_CANCEL_POINT;
|
||||
/* Look for a cancellation after we unblock: */
|
||||
pthread_testcancel();
|
||||
}
|
@ -45,10 +45,11 @@ close(int fd)
|
||||
{
|
||||
int flags;
|
||||
int ret;
|
||||
int status;
|
||||
struct stat sb;
|
||||
struct fd_table_entry *entry;
|
||||
|
||||
_thread_enter_cancellation_point();
|
||||
|
||||
if ((fd == _thread_kern_pipe[0]) || (fd == _thread_kern_pipe[1])) {
|
||||
/*
|
||||
* Don't allow silly programs to close the kernel pipe.
|
||||
@ -98,6 +99,7 @@ close(int fd)
|
||||
/* Close the file descriptor: */
|
||||
ret = _thread_sys_close(fd);
|
||||
}
|
||||
_thread_leave_cancellation_point();
|
||||
return (ret);
|
||||
}
|
||||
#endif
|
||||
|
@ -157,8 +157,7 @@ pthread_cond_destroy(pthread_cond_t * cond)
|
||||
int
|
||||
pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
|
||||
{
|
||||
int rval = 0;
|
||||
int status;
|
||||
int rval = 0;
|
||||
|
||||
if (cond == NULL)
|
||||
rval = EINVAL;
|
||||
@ -169,6 +168,9 @@ pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
|
||||
*/
|
||||
else if (*cond != NULL ||
|
||||
(rval = pthread_cond_init(cond,NULL)) == 0) {
|
||||
|
||||
_thread_enter_cancellation_point();
|
||||
|
||||
/* Lock the condition variable structure: */
|
||||
_SPINLOCK(&(*cond)->lock);
|
||||
|
||||
@ -193,8 +195,9 @@ pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
|
||||
/* Return invalid argument error: */
|
||||
rval = EINVAL;
|
||||
} else {
|
||||
/* Reset the timeout flag: */
|
||||
/* Reset the timeout and interrupted flags: */
|
||||
_thread_run->timeout = 0;
|
||||
_thread_run->interrupted = 0;
|
||||
|
||||
/*
|
||||
* Queue the running thread for the condition
|
||||
@ -233,7 +236,28 @@ pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
|
||||
_thread_kern_sched_state_unlock(PS_COND_WAIT,
|
||||
&(*cond)->lock, __FILE__, __LINE__);
|
||||
|
||||
/* Lock the mutex: */
|
||||
if (_thread_run->interrupted != 0) {
|
||||
/*
|
||||
* Lock the condition variable
|
||||
* while removing the thread.
|
||||
*/
|
||||
_SPINLOCK(&(*cond)->lock);
|
||||
|
||||
cond_queue_remove(*cond,
|
||||
_thread_run);
|
||||
|
||||
/* Check for no more waiters: */
|
||||
if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
|
||||
(*cond)->c_mutex = NULL;
|
||||
|
||||
_SPINUNLOCK(&(*cond)->lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Note that even though this thread may have
|
||||
* been canceled, POSIX requires that the mutex
|
||||
* be reaquired prior to cancellation.
|
||||
*/
|
||||
rval = _mutex_cv_lock(mutex);
|
||||
}
|
||||
}
|
||||
@ -248,6 +272,13 @@ pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
|
||||
rval = EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((_thread_run->cancelflags & PTHREAD_CANCEL_NEEDED) != 0) {
|
||||
_thread_exit_cleanup();
|
||||
pthread_exit(PTHREAD_CANCELED);
|
||||
}
|
||||
|
||||
_thread_leave_cancellation_point();
|
||||
}
|
||||
|
||||
/* Return the completion status: */
|
||||
@ -258,8 +289,7 @@ int
|
||||
pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
|
||||
const struct timespec * abstime)
|
||||
{
|
||||
int rval = 0;
|
||||
int status;
|
||||
int rval = 0;
|
||||
|
||||
if (cond == NULL || abstime == NULL)
|
||||
rval = EINVAL;
|
||||
@ -276,6 +306,9 @@ pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
|
||||
*/
|
||||
if (*cond != NULL ||
|
||||
(rval = pthread_cond_init(cond,NULL)) == 0) {
|
||||
|
||||
_thread_enter_cancellation_point();
|
||||
|
||||
/* Lock the condition variable structure: */
|
||||
_SPINLOCK(&(*cond)->lock);
|
||||
|
||||
@ -306,8 +339,9 @@ pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
|
||||
_thread_run->wakeup_time.tv_nsec =
|
||||
abstime->tv_nsec;
|
||||
|
||||
/* Reset the timeout flag: */
|
||||
/* Reset the timeout and interrupted flags: */
|
||||
_thread_run->timeout = 0;
|
||||
_thread_run->interrupted = 0;
|
||||
|
||||
/*
|
||||
* Queue the running thread for the condition
|
||||
@ -341,12 +375,16 @@ pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
|
||||
_thread_kern_sched_state_unlock(PS_COND_WAIT,
|
||||
&(*cond)->lock, __FILE__, __LINE__);
|
||||
|
||||
/* Check if the wait timedout: */
|
||||
if (_thread_run->timeout == 0) {
|
||||
/*
|
||||
* Check if the wait timedout or was
|
||||
* interrupted (canceled):
|
||||
*/
|
||||
if ((_thread_run->timeout == 0) &&
|
||||
(_thread_run->interrupted == 0)) {
|
||||
/* Lock the mutex: */
|
||||
rval = _mutex_cv_lock(mutex);
|
||||
}
|
||||
else {
|
||||
|
||||
} else {
|
||||
/* Lock the condition variable structure: */
|
||||
_SPINLOCK(&(*cond)->lock);
|
||||
|
||||
@ -369,8 +407,12 @@ pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
|
||||
rval = ETIMEDOUT;
|
||||
|
||||
/*
|
||||
* Lock the mutex and ignore
|
||||
* any errors:
|
||||
* Lock the mutex and ignore any
|
||||
* errors. Note that even though
|
||||
* this thread may have been
|
||||
* canceled, POSIX requires that
|
||||
* the mutex be reaquired prior
|
||||
* to cancellation.
|
||||
*/
|
||||
(void)_mutex_cv_lock(mutex);
|
||||
}
|
||||
@ -388,6 +430,12 @@ pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
|
||||
break;
|
||||
}
|
||||
|
||||
if ((_thread_run->cancelflags & PTHREAD_CANCEL_NEEDED) != 0) {
|
||||
_thread_exit_cleanup();
|
||||
pthread_exit(PTHREAD_CANCELED);
|
||||
}
|
||||
|
||||
_thread_leave_cancellation_point();
|
||||
}
|
||||
|
||||
/* Return the completion status: */
|
||||
@ -416,16 +464,7 @@ pthread_cond_signal(pthread_cond_t * cond)
|
||||
switch ((*cond)->c_type) {
|
||||
/* Fast condition variable: */
|
||||
case COND_TYPE_FAST:
|
||||
/*
|
||||
* Enter a loop to dequeue threads from the condition
|
||||
* queue until we find one that hasn't previously
|
||||
* timed out.
|
||||
*/
|
||||
while (((pthread = cond_queue_deq(*cond)) != NULL) &&
|
||||
(pthread->timeout != 0)) {
|
||||
}
|
||||
|
||||
if (pthread != NULL)
|
||||
if ((pthread = cond_queue_deq(*cond)) != NULL)
|
||||
/* Allow the thread to run: */
|
||||
PTHREAD_NEW_STATE(pthread,PS_RUNNING);
|
||||
|
||||
@ -482,12 +521,7 @@ pthread_cond_broadcast(pthread_cond_t * cond)
|
||||
* condition queue:
|
||||
*/
|
||||
while ((pthread = cond_queue_deq(*cond)) != NULL) {
|
||||
/*
|
||||
* The thread is already running if the
|
||||
* timeout flag is set.
|
||||
*/
|
||||
if (pthread->timeout == 0)
|
||||
PTHREAD_NEW_STATE(pthread,PS_RUNNING);
|
||||
PTHREAD_NEW_STATE(pthread,PS_RUNNING);
|
||||
}
|
||||
|
||||
/* There are no more waiting threads: */
|
||||
@ -524,9 +558,17 @@ cond_queue_deq(pthread_cond_t cond)
|
||||
{
|
||||
pthread_t pthread;
|
||||
|
||||
if ((pthread = TAILQ_FIRST(&cond->c_queue)) != NULL) {
|
||||
while ((pthread = TAILQ_FIRST(&cond->c_queue)) != NULL) {
|
||||
TAILQ_REMOVE(&cond->c_queue, pthread, qe);
|
||||
pthread->flags &= ~PTHREAD_FLAGS_IN_CONDQ;
|
||||
if ((pthread->timeout == 0) && (pthread->interrupted == 0))
|
||||
/*
|
||||
* Only exit the loop when we find a thread
|
||||
* that hasn't timed out or been canceled;
|
||||
* those threads are already running and don't
|
||||
* need their run state changed.
|
||||
*/
|
||||
break;
|
||||
}
|
||||
|
||||
return(pthread);
|
||||
|
@ -50,9 +50,7 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr,
|
||||
void *(*start_routine) (void *), void *arg)
|
||||
{
|
||||
int f_gc = 0;
|
||||
int i;
|
||||
int ret = 0;
|
||||
int status;
|
||||
pthread_t gc_thread;
|
||||
pthread_t new_thread;
|
||||
pthread_attr_t pattr;
|
||||
@ -166,6 +164,9 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr,
|
||||
new_thread->start_routine = start_routine;
|
||||
new_thread->arg = arg;
|
||||
|
||||
new_thread->cancelflags = PTHREAD_CANCEL_ENABLE |
|
||||
PTHREAD_CANCEL_DEFERRED;
|
||||
|
||||
/*
|
||||
* Write a magic value to the thread structure
|
||||
* to help identify valid ones:
|
||||
|
@ -34,6 +34,8 @@
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifdef _THREAD_SAFE
|
||||
#include <pthread.h>
|
||||
@ -101,17 +103,45 @@ _thread_exit(char *fname, int lineno, char *string)
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Only called when a thread is cancelled. It may be more useful
|
||||
* to call it from pthread_exit() if other ways of asynchronous or
|
||||
* abnormal thread termination can be found.
|
||||
*/
|
||||
void
|
||||
_thread_exit_cleanup(void)
|
||||
{
|
||||
/*
|
||||
* POSIX states that cancellation/termination of a thread should
|
||||
* not release any visible resources (such as mutexes) and that
|
||||
* it is the applications responsibility. Resources that are
|
||||
* internal to the threads library, including file and fd locks,
|
||||
* are not visible to the application and need to be released.
|
||||
*/
|
||||
/* Unlock all owned fd locks: */
|
||||
_thread_fd_unlock_owned(_thread_run);
|
||||
|
||||
/* Unlock all owned file locks: */
|
||||
_funlock_owned(_thread_run);
|
||||
|
||||
/* Unlock all private mutexes: */
|
||||
_mutex_unlock_private(_thread_run);
|
||||
|
||||
/*
|
||||
* This still isn't quite correct because we don't account
|
||||
* for held spinlocks (see libc/stdlib/malloc.c).
|
||||
*/
|
||||
}
|
||||
|
||||
void
|
||||
pthread_exit(void *status)
|
||||
{
|
||||
int sig;
|
||||
long l;
|
||||
pthread_t pthread;
|
||||
pthread_t pthread;
|
||||
|
||||
/* Check if this thread is already in the process of exiting: */
|
||||
if ((_thread_run->flags & PTHREAD_EXITING) != 0) {
|
||||
char msg[128];
|
||||
snprintf(msg,"Thread %p has called pthread_exit() from a destructor. POSIX 1003.1 1996 s16.2.5.2 does not allow this!",_thread_run);
|
||||
snprintf(msg, sizeof(msg), "Thread %p has called pthread_exit() from a destructor. POSIX 1003.1 1996 s16.2.5.2 does not allow this!",_thread_run);
|
||||
PANIC(msg);
|
||||
}
|
||||
|
||||
@ -134,7 +164,7 @@ pthread_exit(void *status)
|
||||
_thread_cleanupspecific();
|
||||
}
|
||||
|
||||
/* Free thread-specific poll_data structure, if allocated */
|
||||
/* Free thread-specific poll_data structure, if allocated: */
|
||||
if (_thread_run->poll_data.fds != NULL) {
|
||||
free(_thread_run->poll_data.fds);
|
||||
_thread_run->poll_data.fds = NULL;
|
||||
|
@ -47,6 +47,8 @@ fcntl(int fd, int cmd,...)
|
||||
int ret;
|
||||
va_list ap;
|
||||
|
||||
_thread_enter_cancellation_point();
|
||||
|
||||
/* Lock the file descriptor: */
|
||||
if ((ret = _FD_LOCK(fd, FD_RDWR, NULL)) == 0) {
|
||||
/* Initialise the variable argument list: */
|
||||
@ -135,6 +137,7 @@ fcntl(int fd, int cmd,...)
|
||||
/* Unlock the file descriptor: */
|
||||
_FD_UNLOCK(fd, FD_RDWR);
|
||||
}
|
||||
_thread_leave_cancellation_point();
|
||||
|
||||
/* Return the completion status: */
|
||||
return (ret);
|
||||
|
@ -41,10 +41,12 @@ fsync(int fd)
|
||||
{
|
||||
int ret;
|
||||
|
||||
_thread_enter_cancellation_point();
|
||||
if ((ret = _FD_LOCK(fd, FD_RDWR, NULL)) == 0) {
|
||||
ret = _thread_sys_fsync(fd);
|
||||
_FD_UNLOCK(fd, FD_RDWR);
|
||||
}
|
||||
_thread_leave_cancellation_point();
|
||||
return (ret);
|
||||
}
|
||||
#endif
|
||||
|
@ -37,7 +37,8 @@
|
||||
#include "pthread_private.h"
|
||||
|
||||
int
|
||||
pthread_getschedparam(pthread_t pthread, int *policy, struct sched_param *param)
|
||||
pthread_getschedparam(pthread_t pthread, int *policy,
|
||||
struct sched_param *param)
|
||||
{
|
||||
int ret;
|
||||
|
||||
|
@ -42,6 +42,7 @@
|
||||
#include <paths.h>
|
||||
#include <poll.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/ttycom.h>
|
||||
@ -199,6 +200,10 @@ _thread_init(void)
|
||||
*/
|
||||
_thread_initial->magic = PTHREAD_MAGIC;
|
||||
|
||||
/* Set the initial cancel state */
|
||||
_thread_initial->cancelflags = PTHREAD_CANCEL_ENABLE |
|
||||
PTHREAD_CANCEL_DEFERRED;
|
||||
|
||||
/* Default the priority of the initial thread: */
|
||||
_thread_initial->base_priority = PTHREAD_DEFAULT_PRIORITY;
|
||||
_thread_initial->active_priority = PTHREAD_DEFAULT_PRIORITY;
|
||||
|
@ -41,16 +41,22 @@ pthread_join(pthread_t pthread, void **thread_return)
|
||||
{
|
||||
int ret = 0;
|
||||
pthread_t pthread1 = NULL;
|
||||
|
||||
_thread_enter_cancellation_point();
|
||||
|
||||
/* Check if the caller has specified an invalid thread: */
|
||||
if (pthread == NULL || pthread->magic != PTHREAD_MAGIC)
|
||||
if (pthread == NULL || pthread->magic != PTHREAD_MAGIC) {
|
||||
/* Invalid thread: */
|
||||
_thread_leave_cancellation_point();
|
||||
return(EINVAL);
|
||||
}
|
||||
|
||||
/* Check if the caller has specified itself: */
|
||||
if (pthread == _thread_run)
|
||||
if (pthread == _thread_run) {
|
||||
/* Avoid a deadlock condition: */
|
||||
_thread_leave_cancellation_point();
|
||||
return(EDEADLK);
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the thread in the list of active threads or in the
|
||||
@ -71,12 +77,31 @@ pthread_join(pthread_t pthread, void **thread_return)
|
||||
|
||||
/* Check if the thread is not dead: */
|
||||
else if (pthread->state != PS_DEAD) {
|
||||
/* Clear the interrupted flag: */
|
||||
_thread_run->interrupted = 0;
|
||||
|
||||
/*
|
||||
* Protect against being context switched out while
|
||||
* adding this thread to the join queue.
|
||||
*/
|
||||
_thread_kern_sig_defer();
|
||||
|
||||
/* Add the running thread to the join queue: */
|
||||
TAILQ_INSERT_TAIL(&(pthread->join_queue), _thread_run, qe);
|
||||
|
||||
/* Schedule the next thread: */
|
||||
_thread_kern_sched_state(PS_JOIN, __FILE__, __LINE__);
|
||||
|
||||
if (_thread_run->interrupted != 0)
|
||||
TAILQ_REMOVE(&(pthread->join_queue), _thread_run, qe);
|
||||
|
||||
_thread_kern_sig_undefer();
|
||||
|
||||
if ((_thread_run->cancelflags & PTHREAD_CANCEL_NEEDED) != 0) {
|
||||
_thread_exit_cleanup();
|
||||
pthread_exit(PTHREAD_CANCELED);
|
||||
}
|
||||
|
||||
/* Check if the thread is not detached: */
|
||||
if ((pthread->attr.flags & PTHREAD_DETACHED) == 0) {
|
||||
/* Check if the return value is required: */
|
||||
@ -93,6 +118,8 @@ pthread_join(pthread_t pthread, void **thread_return)
|
||||
/* Return the thread's return value: */
|
||||
*thread_return = pthread->ret;
|
||||
|
||||
_thread_leave_cancellation_point();
|
||||
|
||||
/* Return the completion status: */
|
||||
return (ret);
|
||||
}
|
||||
|
@ -67,11 +67,10 @@ _thread_kern_sched(ucontext_t * scp)
|
||||
char *fdata;
|
||||
#endif
|
||||
pthread_t pthread, pthread_h = NULL;
|
||||
pthread_t last_thread = NULL;
|
||||
struct itimerval itimer;
|
||||
struct timespec ts, ts1;
|
||||
struct timeval tv, tv1;
|
||||
int i, set_timer = 0;
|
||||
int set_timer = 0;
|
||||
|
||||
/*
|
||||
* Flag the pthread kernel as executing scheduler code
|
||||
@ -109,6 +108,20 @@ __asm__("fnsave %0": :"m"(*fdata));
|
||||
*/
|
||||
_thread_kern_in_sched = 0;
|
||||
|
||||
if (((_thread_run->cancelflags & PTHREAD_AT_CANCEL_POINT) == 0) &&
|
||||
((_thread_run->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) != 0)) {
|
||||
/*
|
||||
* Cancelations override signals.
|
||||
*
|
||||
* Stick a cancellation point at the start of
|
||||
* each async-cancellable thread's resumption.
|
||||
*
|
||||
* We allow threads woken at cancel points to do their
|
||||
* own checks.
|
||||
*/
|
||||
pthread_testcancel();
|
||||
}
|
||||
|
||||
if (_sched_switch_hook != NULL) {
|
||||
/* Run the installed switch hook: */
|
||||
thread_run_switch_hook(_last_user_thread, _thread_run);
|
||||
@ -161,6 +174,7 @@ __asm__("fnsave %0": :"m"(*fdata));
|
||||
*/
|
||||
switch (_thread_run->state) {
|
||||
case PS_DEAD:
|
||||
case PS_STATE_MAX: /* to silence -Wall */
|
||||
/*
|
||||
* Dead threads are not placed in any queue:
|
||||
*/
|
||||
@ -249,6 +263,7 @@ __asm__("fnsave %0": :"m"(*fdata));
|
||||
|
||||
/* Insert into the work queue: */
|
||||
PTHREAD_WORKQ_INSERT(_thread_run);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -627,14 +642,12 @@ _thread_kern_sched_state_unlock(enum pthread_state state,
|
||||
static void
|
||||
_thread_kern_poll(int wait_reqd)
|
||||
{
|
||||
char bufr[128];
|
||||
int count = 0;
|
||||
int i, found;
|
||||
int kern_pipe_added = 0;
|
||||
int nfds = 0;
|
||||
int timeout_ms = 0;
|
||||
struct pthread *pthread, *pthread_next;
|
||||
ssize_t num;
|
||||
struct pthread *pthread;
|
||||
struct timespec ts;
|
||||
struct timeval tv;
|
||||
|
||||
@ -1103,10 +1116,10 @@ thread_run_switch_hook(pthread_t thread_out, pthread_t thread_in)
|
||||
pthread_t tid_in = thread_in;
|
||||
|
||||
if ((tid_out != NULL) &&
|
||||
(tid_out->flags & PTHREAD_FLAGS_PRIVATE != 0))
|
||||
(tid_out->flags & PTHREAD_FLAGS_PRIVATE) != 0)
|
||||
tid_out = NULL;
|
||||
if ((tid_in != NULL) &&
|
||||
(tid_in->flags & PTHREAD_FLAGS_PRIVATE != 0))
|
||||
(tid_in->flags & PTHREAD_FLAGS_PRIVATE) != 0)
|
||||
tid_in = NULL;
|
||||
|
||||
if ((_sched_switch_hook != NULL) && (tid_out != tid_in)) {
|
||||
|
40
lib/libpthread/thread/thr_msync.c
Normal file
40
lib/libpthread/thread/thr_msync.c
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* David Leonard <d@openbsd.org>, 1999. Public Domain.
|
||||
*
|
||||
* $OpenBSD: uthread_msync.c,v 1.2 1999/06/09 07:16:17 d Exp $
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/mman.h>
|
||||
#ifdef _THREAD_SAFE
|
||||
#include <pthread.h>
|
||||
#include "pthread_private.h"
|
||||
|
||||
int
|
||||
msync(addr, len, flags)
|
||||
void *addr;
|
||||
size_t len;
|
||||
int flags;
|
||||
{
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* XXX This is quite pointless unless we know how to get the
|
||||
* file descriptor associated with the memory, and lock it for
|
||||
* write. The only real use of this wrapper is to guarantee
|
||||
* a cancellation point, as per the standard. sigh.
|
||||
*/
|
||||
|
||||
/* This is a cancellation point: */
|
||||
_thread_enter_cancellation_point();
|
||||
|
||||
ret = _thread_sys_msync(addr, len, flags);
|
||||
|
||||
/* No longer in a cancellation point: */
|
||||
_thread_leave_cancellation_point();
|
||||
|
||||
return (ret);
|
||||
}
|
||||
#endif
|
@ -94,7 +94,8 @@ _mutex_reinit(pthread_mutex_t * mutex)
|
||||
TAILQ_INIT(&(*mutex)->m_queue);
|
||||
(*mutex)->m_owner = NULL;
|
||||
(*mutex)->m_data.m_count = 0;
|
||||
(*mutex)->m_flags = MUTEX_FLAGS_INITED;
|
||||
(*mutex)->m_flags &= MUTEX_FLAGS_PRIVATE;
|
||||
(*mutex)->m_flags |= MUTEX_FLAGS_INITED;
|
||||
(*mutex)->m_refcount = 0;
|
||||
(*mutex)->m_prio = 0;
|
||||
(*mutex)->m_saved_prio = 0;
|
||||
@ -428,6 +429,9 @@ pthread_mutex_lock(pthread_mutex_t * mutex)
|
||||
_MUTEX_INIT_LINK(*mutex);
|
||||
}
|
||||
|
||||
/* Reset the interrupted flag: */
|
||||
_thread_run->interrupted = 0;
|
||||
|
||||
/* Process according to mutex type: */
|
||||
switch ((*mutex)->m_protocol) {
|
||||
/* Default POSIX mutex: */
|
||||
@ -602,6 +606,13 @@ pthread_mutex_lock(pthread_mutex_t * mutex)
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check to see if this thread was interrupted and
|
||||
* is still in the mutex queue of waiting threads:
|
||||
*/
|
||||
if (_thread_run->interrupted != 0)
|
||||
mutex_queue_remove(*mutex, _thread_run);
|
||||
|
||||
/* Unlock the mutex structure: */
|
||||
_SPINUNLOCK(&(*mutex)->lock);
|
||||
|
||||
@ -610,6 +621,11 @@ pthread_mutex_lock(pthread_mutex_t * mutex)
|
||||
* necessary:
|
||||
*/
|
||||
_thread_kern_sig_undefer();
|
||||
|
||||
if ((_thread_run->cancelflags & PTHREAD_CANCEL_NEEDED) != 0) {
|
||||
_thread_exit_cleanup();
|
||||
pthread_exit(PTHREAD_CANCELED);
|
||||
}
|
||||
}
|
||||
|
||||
/* Return the completion status: */
|
||||
@ -1314,6 +1330,18 @@ mutex_rescan_owned (pthread_t pthread, pthread_mutex_t mutex)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
_mutex_unlock_private(pthread_t pthread)
|
||||
{
|
||||
struct pthread_mutex *m, *m_next;
|
||||
|
||||
for (m = TAILQ_FIRST(&pthread->mutexq); m != NULL; m = m_next) {
|
||||
m_next = TAILQ_NEXT(m, m_qe);
|
||||
if ((m->m_flags & MUTEX_FLAGS_PRIVATE) != 0)
|
||||
pthread_mutex_unlock(&m);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Dequeue a waiting thread from the head of a mutex queue in descending
|
||||
* priority order.
|
||||
@ -1323,8 +1351,17 @@ mutex_queue_deq(pthread_mutex_t mutex)
|
||||
{
|
||||
pthread_t pthread;
|
||||
|
||||
if ((pthread = TAILQ_FIRST(&mutex->m_queue)) != NULL)
|
||||
while ((pthread = TAILQ_FIRST(&mutex->m_queue)) != NULL) {
|
||||
TAILQ_REMOVE(&mutex->m_queue, pthread, qe);
|
||||
pthread->flags &= ~PTHREAD_FLAGS_IN_MUTEXQ;
|
||||
|
||||
/*
|
||||
* Only exit the loop if the thread hasn't been
|
||||
* cancelled.
|
||||
*/
|
||||
if (pthread->interrupted == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return(pthread);
|
||||
}
|
||||
@ -1335,7 +1372,10 @@ mutex_queue_deq(pthread_mutex_t mutex)
|
||||
static inline void
|
||||
mutex_queue_remove(pthread_mutex_t mutex, pthread_t pthread)
|
||||
{
|
||||
TAILQ_REMOVE(&mutex->m_queue, pthread, qe);
|
||||
if ((pthread->flags & PTHREAD_FLAGS_IN_MUTEXQ) != 0) {
|
||||
TAILQ_REMOVE(&mutex->m_queue, pthread, qe);
|
||||
pthread->flags &= ~PTHREAD_FLAGS_IN_MUTEXQ;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1359,6 +1399,7 @@ mutex_queue_enq(pthread_mutex_t mutex, pthread_t pthread)
|
||||
tid = TAILQ_NEXT(tid, qe);
|
||||
TAILQ_INSERT_BEFORE(tid, pthread, qe);
|
||||
}
|
||||
pthread->flags |= PTHREAD_FLAGS_IN_MUTEXQ;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -47,6 +47,7 @@ nanosleep(const struct timespec * time_to_sleep,
|
||||
struct timespec remaining_time;
|
||||
struct timeval tv;
|
||||
|
||||
_thread_enter_cancellation_point();
|
||||
/* Check if the time to sleep is legal: */
|
||||
if (time_to_sleep == NULL || time_to_sleep->tv_sec < 0 ||
|
||||
time_to_sleep->tv_nsec < 0 || time_to_sleep->tv_nsec >= 1000000000) {
|
||||
@ -116,6 +117,7 @@ nanosleep(const struct timespec * time_to_sleep,
|
||||
ret = -1;
|
||||
}
|
||||
}
|
||||
_thread_leave_cancellation_point();
|
||||
return (ret);
|
||||
}
|
||||
#endif
|
||||
|
@ -44,10 +44,11 @@
|
||||
int
|
||||
open(const char *path, int flags,...)
|
||||
{
|
||||
int fd;
|
||||
int mode = 0;
|
||||
int status;
|
||||
va_list ap;
|
||||
int fd;
|
||||
int mode = 0;
|
||||
va_list ap;
|
||||
|
||||
_thread_enter_cancellation_point();
|
||||
|
||||
/* Check if the file is being created: */
|
||||
if (flags & O_CREAT) {
|
||||
@ -68,6 +69,8 @@ open(const char *path, int flags,...)
|
||||
fd = -1;
|
||||
}
|
||||
|
||||
_thread_leave_cancellation_point();
|
||||
|
||||
/* Return the file descriptor or -1 on error: */
|
||||
return (fd);
|
||||
}
|
||||
|
@ -253,7 +253,7 @@ struct pthread_mutex {
|
||||
*/
|
||||
#define PTHREAD_MUTEX_STATIC_INITIALIZER \
|
||||
{ PTHREAD_MUTEX_DEFAULT, PTHREAD_PRIO_NONE, TAILQ_INITIALIZER, \
|
||||
NULL, { NULL }, 0, 0, 0, 0, TAILQ_INITIALIZER, \
|
||||
NULL, { NULL }, MUTEX_FLAGS_PRIVATE, 0, 0, 0, TAILQ_INITIALIZER, \
|
||||
_SPINLOCK_INITIALIZER }
|
||||
|
||||
struct pthread_mutex_attr {
|
||||
@ -513,6 +513,15 @@ struct pthread {
|
||||
*/
|
||||
int sig_saved;
|
||||
|
||||
/*
|
||||
* Cancelability flags - the lower 2 bits are used by cancel
|
||||
* definitions in pthread.h
|
||||
*/
|
||||
#define PTHREAD_AT_CANCEL_POINT 0x0004
|
||||
#define PTHREAD_CANCELLING 0x0008
|
||||
#define PTHREAD_CANCEL_NEEDED 0x0010
|
||||
int cancelflags;
|
||||
|
||||
/*
|
||||
* Current signal mask and pending signals.
|
||||
*/
|
||||
@ -610,15 +619,18 @@ struct pthread {
|
||||
*/
|
||||
int yield_on_sig_undefer;
|
||||
|
||||
/* Miscellaneous data. */
|
||||
/* Miscellaneous flags; only set with signals deferred. */
|
||||
int flags;
|
||||
#define PTHREAD_FLAGS_PRIVATE 0x0001
|
||||
#define PTHREAD_EXITING 0x0002
|
||||
#define PTHREAD_FLAGS_IN_CONDQ 0x0004 /* in condition queue using qe link*/
|
||||
#define PTHREAD_FLAGS_IN_WORKQ 0x0008 /* in work queue using qe link */
|
||||
#define PTHREAD_FLAGS_IN_WAITQ 0x0010 /* in waiting queue using pqe link*/
|
||||
#define PTHREAD_FLAGS_IN_PRIOQ 0x0020 /* in priority queue using pqe link*/
|
||||
#define PTHREAD_FLAGS_TRACE 0x0040 /* for debugging purposes */
|
||||
#define PTHREAD_FLAGS_IN_WAITQ 0x0010 /* in waiting queue using pqe link */
|
||||
#define PTHREAD_FLAGS_IN_PRIOQ 0x0020 /* in priority queue using pqe link */
|
||||
#define PTHREAD_FLAGS_IN_MUTEXQ 0x0040 /* in mutex queue using qe link */
|
||||
#define PTHREAD_FLAGS_IN_FILEQ 0x0080 /* in file lock queue using qe link */
|
||||
#define PTHREAD_FLAGS_IN_FDQ 0x0100 /* in fd lock queue using qe link */
|
||||
#define PTHREAD_FLAGS_TRACE 0x0200 /* for debugging purposes */
|
||||
|
||||
/*
|
||||
* Base priority is the user setable and retrievable priority
|
||||
@ -925,6 +937,7 @@ char *__ttyname_r_basic(int, char *, size_t);
|
||||
char *ttyname_r(int, char *, size_t);
|
||||
int _find_dead_thread(pthread_t);
|
||||
int _find_thread(pthread_t);
|
||||
void _funlock_owned(pthread_t);
|
||||
int _thread_create(pthread_t *,const pthread_attr_t *,void *(*start_routine)(void *),void *,pthread_t);
|
||||
int _thread_fd_lock(int, int, struct timespec *);
|
||||
int _thread_fd_lock_debug(int, int, struct timespec *,char *fname,int lineno);
|
||||
@ -932,8 +945,9 @@ void _dispatch_signals(void);
|
||||
void _thread_signal(pthread_t, int);
|
||||
int _mutex_cv_lock(pthread_mutex_t *);
|
||||
int _mutex_cv_unlock(pthread_mutex_t *);
|
||||
void _mutex_notify_priochange(pthread_t);
|
||||
int _mutex_reinit(pthread_mutex_t *);
|
||||
void _mutex_notify_priochange(struct pthread *);
|
||||
void _mutex_unlock_private(pthread_t);
|
||||
int _cond_reinit(pthread_cond_t *);
|
||||
int _pq_alloc(struct pq_queue *, int, int);
|
||||
int _pq_init(struct pq_queue *);
|
||||
@ -948,8 +962,10 @@ void _waitq_setactive(void);
|
||||
void _waitq_clearactive(void);
|
||||
#endif
|
||||
void _thread_exit(char *, int, char *);
|
||||
void _thread_exit_cleanup(void);
|
||||
void _thread_fd_unlock(int, int);
|
||||
void _thread_fd_unlock_debug(int, int, char *, int);
|
||||
void _thread_fd_unlock_owned(pthread_t);
|
||||
void *_thread_cleanup(pthread_t);
|
||||
void _thread_cleanupspecific(void);
|
||||
void _thread_dump_info(void);
|
||||
@ -969,6 +985,9 @@ void _thread_start_sig_handler(void);
|
||||
void _thread_seterrno(pthread_t,int);
|
||||
int _thread_fd_table_init(int fd);
|
||||
pthread_addr_t _thread_gc(pthread_addr_t);
|
||||
void _thread_enter_cancellation_point(void);
|
||||
void _thread_leave_cancellation_point(void);
|
||||
void _thread_cancellation_point(void);
|
||||
|
||||
/* #include <signal.h> */
|
||||
int _thread_sys_sigaction(int, const struct sigaction *, struct sigaction *);
|
||||
@ -1148,6 +1167,8 @@ pid_t _thread_sys_wait4(pid_t, int *, int, struct rusage *);
|
||||
#ifdef _SYS_POLL_H_
|
||||
int _thread_sys_poll(struct pollfd *, unsigned, int);
|
||||
#endif
|
||||
/* #include <sys/mman.h> */
|
||||
int _thread_sys_msync(void *, size_t, int);
|
||||
__END_DECLS
|
||||
|
||||
#endif /* !_PTHREAD_PRIVATE_H */
|
||||
|
@ -47,9 +47,13 @@ read(int fd, void *buf, size_t nbytes)
|
||||
int ret;
|
||||
int type;
|
||||
|
||||
_thread_enter_cancellation_point();
|
||||
|
||||
/* POSIX says to do just this: */
|
||||
if (nbytes == 0)
|
||||
if (nbytes == 0) {
|
||||
_thread_leave_cancellation_point();
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Lock the file descriptor for read: */
|
||||
if ((ret = _FD_LOCK(fd, FD_READ, NULL)) == 0) {
|
||||
@ -61,6 +65,7 @@ read(int fd, void *buf, size_t nbytes)
|
||||
/* File is not open for read: */
|
||||
errno = EBADF;
|
||||
_FD_UNLOCK(fd, FD_READ);
|
||||
_thread_leave_cancellation_point();
|
||||
return (-1);
|
||||
}
|
||||
|
||||
@ -92,6 +97,7 @@ read(int fd, void *buf, size_t nbytes)
|
||||
}
|
||||
_FD_UNLOCK(fd, FD_READ);
|
||||
}
|
||||
_thread_leave_cancellation_point();
|
||||
return (ret);
|
||||
}
|
||||
#endif
|
||||
|
@ -38,7 +38,8 @@
|
||||
#include "pthread_private.h"
|
||||
|
||||
int
|
||||
pthread_setschedparam(pthread_t pthread, int policy, struct sched_param *param)
|
||||
pthread_setschedparam(pthread_t pthread, int policy,
|
||||
const struct sched_param *param)
|
||||
{
|
||||
int old_prio, in_readyq = 0, ret = 0;
|
||||
|
||||
|
@ -47,6 +47,7 @@ sigwait(const sigset_t * set, int *sig)
|
||||
sigset_t tempset, waitset;
|
||||
struct sigaction act;
|
||||
|
||||
_thread_enter_cancellation_point();
|
||||
/*
|
||||
* Specify the thread kernel signal handler.
|
||||
*/
|
||||
@ -85,6 +86,7 @@ sigwait(const sigset_t * set, int *sig)
|
||||
/* Return the signal number to the caller: */
|
||||
*sig = i;
|
||||
|
||||
_thread_leave_cancellation_point();
|
||||
return (0);
|
||||
}
|
||||
|
||||
@ -137,6 +139,7 @@ sigwait(const sigset_t * set, int *sig)
|
||||
}
|
||||
}
|
||||
|
||||
_thread_leave_cancellation_point();
|
||||
/* Return the completion status: */
|
||||
return (ret);
|
||||
}
|
||||
|
@ -42,6 +42,7 @@ wait4(pid_t pid, int *istat, int options, struct rusage * rusage)
|
||||
{
|
||||
pid_t ret;
|
||||
|
||||
_thread_enter_cancellation_point();
|
||||
_thread_kern_sig_defer();
|
||||
|
||||
/* Perform a non-blocking wait4 syscall: */
|
||||
@ -61,6 +62,7 @@ wait4(pid_t pid, int *istat, int options, struct rusage * rusage)
|
||||
}
|
||||
|
||||
_thread_kern_sig_undefer();
|
||||
_thread_leave_cancellation_point();
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
@ -50,9 +50,12 @@ write(int fd, const void *buf, size_t nbytes)
|
||||
ssize_t num = 0;
|
||||
ssize_t ret;
|
||||
|
||||
_thread_enter_cancellation_point();
|
||||
/* POSIX says to do just this: */
|
||||
if (nbytes == 0)
|
||||
if (nbytes == 0) {
|
||||
_thread_leave_cancellation_point();
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Lock the file descriptor for write: */
|
||||
if ((ret = _FD_LOCK(fd, FD_WRITE, NULL)) == 0) {
|
||||
@ -64,7 +67,8 @@ write(int fd, const void *buf, size_t nbytes)
|
||||
/* File is not open for write: */
|
||||
errno = EBADF;
|
||||
_FD_UNLOCK(fd, FD_WRITE);
|
||||
return (-1);
|
||||
_thread_leave_cancellation_point();
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/* Check if file operations are to block */
|
||||
@ -129,6 +133,7 @@ write(int fd, const void *buf, size_t nbytes)
|
||||
}
|
||||
_FD_UNLOCK(fd, FD_RDWR);
|
||||
}
|
||||
_thread_leave_cancellation_point();
|
||||
return (ret);
|
||||
}
|
||||
#endif
|
||||
|
70
share/man/man3/pthread_cancel.3
Normal file
70
share/man/man3/pthread_cancel.3
Normal file
@ -0,0 +1,70 @@
|
||||
.\" $FreeBSD$
|
||||
.Dd January 17, 1999
|
||||
.Dt PTHREAD_CANCEL 3
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm pthread_cancel
|
||||
.Nd cancel execution of a thread
|
||||
.Sh SYNOPSIS
|
||||
.Fd #include <pthread.h>
|
||||
.Ft int
|
||||
.Fn pthread_cancel "pthread_t thread"
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Fn pthread_cancel
|
||||
function requests that
|
||||
.Fa thread
|
||||
be canceled. The target thread's cancelability state and type determines
|
||||
when the cancellation takes effect. When the cancellation is acted on,
|
||||
the cancellation cleanup handlers for
|
||||
.Fa thread
|
||||
are called. When the last cancellation cleanup handler returns,
|
||||
the thread-specific data destructor functions will be called for
|
||||
.Fa thread .
|
||||
When the last destructor function returns,
|
||||
.Fa thread
|
||||
will be terminated.
|
||||
.Pp
|
||||
The cancellation processing in the target thread runs asynchronously with
|
||||
respect to the calling thread returning from
|
||||
.Fn pthread_cancel .
|
||||
.Pp
|
||||
A status of
|
||||
.Dv PTHREAD_CANCELED
|
||||
is made available to any threads joining with the target. The symbolic
|
||||
constant
|
||||
.Dv PTHREAD_CANCELED
|
||||
expands to a constant expression of type
|
||||
.Ft "(void *)" ,
|
||||
whose value matches no pointer to an object in memory nor the value
|
||||
.Dv NULL .
|
||||
.Sh RETURN VALUES
|
||||
If successful, the
|
||||
.Fn pthread_cancel
|
||||
functions will return zero. Otherwise an error number will be returned to
|
||||
indicate the error.
|
||||
.Sh ERRORS
|
||||
.Fn pthread_cancel
|
||||
will fail if:
|
||||
.Bl -tag -width Er
|
||||
.It Bq Er ESRCH
|
||||
No thread could be found corresponding to that specified by the given
|
||||
thread ID.
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr pthread_cleanup_pop 3 ,
|
||||
.Xr pthread_cleanup_push 3 ,
|
||||
.Xr pthread_exit 3 ,
|
||||
.Xr pthread_join 3 ,
|
||||
.Xr pthread_setcancelstate 3 ,
|
||||
.Xr pthread_setcanceltype 3 ,
|
||||
.Xr pthread_testcancel 3
|
||||
.Sh STANDARDS
|
||||
.Fn pthread_cancel
|
||||
conforms to ISO/IEC 9945-1 ANSI/IEEE
|
||||
.Pq Dq Tn POSIX
|
||||
Std 1003.1 Second Edition 1996-07-12.
|
||||
.Sh AUTHORS
|
||||
This man page was written by
|
||||
.An David Leonard <d@openbsd.org>
|
||||
for the OpenBSD implementation of pthread_cancel.
|
187
share/man/man3/pthread_testcancel.3
Normal file
187
share/man/man3/pthread_testcancel.3
Normal file
@ -0,0 +1,187 @@
|
||||
.\" $FreeBSD$
|
||||
.Dd January 17, 1999
|
||||
.Dt PTHREAD_TESTCANCEL 3
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm pthread_setcancelstate ,
|
||||
.Nm pthread_setcanceltype ,
|
||||
.Nm pthread_testcancel
|
||||
.Nd set cancelability state
|
||||
.Sh SYNOPSIS
|
||||
.Fd #include <pthread.h>
|
||||
.Ft int
|
||||
.Fn pthread_setcancelstate "int state" "int *oldstate"
|
||||
.Ft int
|
||||
.Fn pthread_setcanceltype "int type" "int *oldtype"
|
||||
.Ft void
|
||||
.Fn pthread_testcancel "void"
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Fn pthread_setcancelstate
|
||||
function atomically both sets the calling thread's cancelability state
|
||||
to the indicated
|
||||
.Fa state
|
||||
and returns the previous cancelability state at the location referenced by
|
||||
.Fa oldstate .
|
||||
Legal values for
|
||||
.Fa state
|
||||
are
|
||||
.Dv PTHREAD_CANCEL_ENABLE
|
||||
and
|
||||
.Dv PTHREAD_CANCEL_DISABLE .
|
||||
.Pp
|
||||
The
|
||||
.Fn pthread_setcanceltype
|
||||
function atomically both sets the calling thread's cancelability type
|
||||
to the indicated
|
||||
.Fa type
|
||||
and returns the previous cancelability type at the location referenced by
|
||||
.Fa oldtype .
|
||||
Legal values for
|
||||
.Fa type
|
||||
are
|
||||
.Dv PTHREAD_CANCEL_DEFERRED
|
||||
and
|
||||
.Dv PTHREAD_CANCEL_ASYNCHRONOUS .
|
||||
.Pp
|
||||
The cancelability state and type of any newly created threads, including the
|
||||
thread in which
|
||||
.Fn main
|
||||
was first invoked, are
|
||||
.Dv PTHREAD_CANCEL_ENABLE
|
||||
and
|
||||
.Dv PTHREAD_CANCEL_DEFERRED
|
||||
respectively.
|
||||
.Pp
|
||||
The
|
||||
.Fn pthread_testcancel
|
||||
function creates a cancellation point in the calling thread. The
|
||||
.Fn pthread_testcancel
|
||||
function has no effect if cancelability is disabled.
|
||||
.Pp
|
||||
.Ss Cancelability States
|
||||
The cancelability state of a thread determines the action taken upon
|
||||
receipt of a cancellation request. The thread may control cancellation in
|
||||
a number of ways.
|
||||
.Pp
|
||||
Each thread maintains its own
|
||||
.Dq cancelability state
|
||||
which may be encoded in two bits:
|
||||
.Bl -hang
|
||||
.It Em Cancelability Enable
|
||||
When cancelability is
|
||||
.Dv PTHREAD_CANCEL_DISABLE ,
|
||||
cancellation requests against the target thread are held pending.
|
||||
.It Em Cancelability Type
|
||||
When cancelability is enabled and the cancelability type is
|
||||
.Dv PTHREAD_CANCEL_ASYNCHRONOUS ,
|
||||
new or pending cancellation requests may be acted upon at any time.
|
||||
When cancelability is enabled and the cancelability type is
|
||||
.Dv PTHREAD_CANCEL_DEFERRED ,
|
||||
cancellation requests are held pending until a cancellation point (see
|
||||
below) is reached. If cancelability is disabled, the setting of the
|
||||
cancelability type has no immediate effect as all cancellation requests
|
||||
are held pending; however, once cancelability is enabled again the new
|
||||
type will be in effect.
|
||||
.El
|
||||
.Ss Cancellation Points
|
||||
Cancellation points will occur when a thread is executing the following
|
||||
functions:
|
||||
.Fn close ,
|
||||
.Fn creat ,
|
||||
.Fn fcntl ,
|
||||
.Fn fsync ,
|
||||
.Fn msync ,
|
||||
.Fn nanosleep ,
|
||||
.Fn open ,
|
||||
.Fn pause ,
|
||||
.Fn pthread_cond_timedwait ,
|
||||
.Fn pthread_cond_wait ,
|
||||
.Fn pthread_join ,
|
||||
.Fn pthread_testcancel ,
|
||||
.Fn read ,
|
||||
.Fn sigwaitinfo ,
|
||||
.Fn sigsuspend ,
|
||||
.Fn sigwait ,
|
||||
.Fn sleep ,
|
||||
.Fn system ,
|
||||
.Fn tcdrain ,
|
||||
.Fn wait ,
|
||||
.Fn waitpid ,
|
||||
.Fn write .
|
||||
.Sh RETURN VALUES
|
||||
If successful, the
|
||||
.Fn pthread_setcancelstate
|
||||
and
|
||||
.Fn pthread_setcanceltype
|
||||
functions will return zero. Otherwise, an error number shall be returned to
|
||||
indicate the error.
|
||||
.Pp
|
||||
The
|
||||
.Fn pthread_setcancelstate
|
||||
and
|
||||
.Fn pthread_setcanceltype
|
||||
functions are used to control the points at which a thread may be
|
||||
asynchronously canceled. For cancellation control to be usable in modular
|
||||
fashion, some rules must be followed.
|
||||
.Pp
|
||||
For purposes of this discussion, consider an object to be a generalization
|
||||
of a procedure. It is a set of procedures and global variables written as
|
||||
a unit and called by clients not known by the object. Objects may depend
|
||||
on other objects.
|
||||
.Pp
|
||||
First, cancelability should only be disabled on entry to an object, never
|
||||
explicitly enabled. On exit from an object, the cancelability state should
|
||||
always be restored to its value on entry to the object.
|
||||
.Pp
|
||||
This follows from a modularity argument: if the client of an object (or the
|
||||
client of an object that uses that object) has disabled cancelability, it is
|
||||
because the client doesn't want to have to worry about how to clean up if the
|
||||
thread is canceled while executing some sequence of actions. If an object
|
||||
is called in such a state and it enables cancelability and a cancellation
|
||||
request is pending for that thread, then the thread will be canceled,
|
||||
contrary to the wish of the client that disabled.
|
||||
.Pp
|
||||
Second, the cancelability type may be explicitly set to either
|
||||
.Em deferred
|
||||
or
|
||||
.Em asynchronous
|
||||
upon entry to an object. But as with the cancelability state, on exit from
|
||||
an object that cancelability type should always be restored to its value on
|
||||
entry to the object.
|
||||
.Pp
|
||||
Finally, only functions that are cancel-safe may be called from a thread that
|
||||
is asynchronously cancelable.
|
||||
.Sh ERRORS
|
||||
The function
|
||||
.Fn pthread_setcancelstate
|
||||
may fail with:
|
||||
.Bl -tag -width Er
|
||||
.It Bq Er EINVAL
|
||||
The specified state is not
|
||||
.Dv PTHREAD_CANCEL_ENABLE
|
||||
or
|
||||
.Dv PTHREAD_CANCEL_DISABLE .
|
||||
.El
|
||||
.Pp
|
||||
The function
|
||||
.Fn pthread_setcanceltype
|
||||
may fail with:
|
||||
.Bl -tag -width Er
|
||||
.It Bq Er EINVAL
|
||||
The specified state is not
|
||||
.Dv PTHREAD_CANCEL_DEFERRED
|
||||
or
|
||||
.Dv PTHREAD_CANCEL_ASYNCHRONOUS .
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr pthread_cancel 3
|
||||
.Sh STANDARDS
|
||||
.Fn pthread_testcancel
|
||||
conforms to ISO/IEC 9945-1 ANSI/IEEE
|
||||
.Pq Dq Tn POSIX
|
||||
Std 1003.1 Second Edition 1996-07-12.
|
||||
.Sh AUTHORS
|
||||
This man page was written by
|
||||
.An David Leonard <d@openbsd.org>
|
||||
for the OpenBSD implementation of pthread_cancel.
|
Loading…
Reference in New Issue
Block a user