summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Leonard <d@cvs.openbsd.org>1999-01-17 23:57:29 +0000
committerDavid Leonard <d@cvs.openbsd.org>1999-01-17 23:57:29 +0000
commit5ca1f71120ac2bc81b65a6cef298606b92242903 (patch)
tree82d901bf8097936d39c68cdb89cc598d1e1fccd6
parent209640f64878cc32de9cf45178c9a0e979525bdd (diff)
pthread_cancel() and cancellation points
-rw-r--r--lib/libc_r/TEST/test_cancel.c174
-rw-r--r--lib/libc_r/man/pthread_cancel.365
-rw-r--r--lib/libc_r/man/pthread_testcancel.3182
-rw-r--r--lib/libc_r/uthread/uthread_cancel.c164
-rw-r--r--lib/libc_r/uthread/uthread_close.c4
-rw-r--r--lib/libc_r/uthread/uthread_cond.c8
-rw-r--r--lib/libc_r/uthread/uthread_create.c7
-rw-r--r--lib/libc_r/uthread/uthread_fcntl.c5
-rw-r--r--lib/libc_r/uthread/uthread_fsync.c4
-rw-r--r--lib/libc_r/uthread/uthread_init.c4
-rw-r--r--lib/libc_r/uthread/uthread_join.c14
-rw-r--r--lib/libc_r/uthread/uthread_nanosleep.c4
-rw-r--r--lib/libc_r/uthread/uthread_open.c6
-rw-r--r--lib/libc_r/uthread/uthread_read.c10
-rw-r--r--lib/libc_r/uthread/uthread_sigwait.c6
-rw-r--r--lib/libc_r/uthread/uthread_wait4.c4
-rw-r--r--lib/libc_r/uthread/uthread_write.c10
-rw-r--r--lib/libpthread/man/pthread_cancel.365
-rw-r--r--lib/libpthread/man/pthread_testcancel.3182
-rw-r--r--lib/libpthread/uthread/uthread_cancel.c164
-rw-r--r--lib/libpthread/uthread/uthread_close.c4
-rw-r--r--lib/libpthread/uthread/uthread_cond.c8
-rw-r--r--lib/libpthread/uthread/uthread_create.c7
-rw-r--r--lib/libpthread/uthread/uthread_fcntl.c5
-rw-r--r--lib/libpthread/uthread/uthread_fsync.c4
-rw-r--r--lib/libpthread/uthread/uthread_init.c4
-rw-r--r--lib/libpthread/uthread/uthread_join.c14
-rw-r--r--lib/libpthread/uthread/uthread_nanosleep.c4
-rw-r--r--lib/libpthread/uthread/uthread_open.c6
-rw-r--r--lib/libpthread/uthread/uthread_read.c10
-rw-r--r--lib/libpthread/uthread/uthread_sigwait.c6
-rw-r--r--lib/libpthread/uthread/uthread_wait4.c4
-rw-r--r--lib/libpthread/uthread/uthread_write.c10
33 files changed, 1130 insertions, 38 deletions
diff --git a/lib/libc_r/TEST/test_cancel.c b/lib/libc_r/TEST/test_cancel.c
new file mode 100644
index 00000000000..74ecaf317d9
--- /dev/null
+++ b/lib/libc_r/TEST/test_cancel.c
@@ -0,0 +1,174 @@
+/* $OpenBSD: test_cancel.c,v 1.1 1999/01/17 23:57:26 d Exp $ */
+/* David Leonard <d@openbsd.org>, 1999. Public Domain. */
+
+#include <pthread.h>
+#include <pthread_np.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include "test.h"
+
+static pthread_cond_t cond;
+static pthread_mutex_t mutex;
+static struct timespec expiretime;
+
+static int pv_state = 0;
+void p() {
+ CHECKr(pthread_mutex_lock(&mutex));
+ if (pv_state <= 0) {
+ CHECKr(pthread_cond_timedwait(&cond, &mutex, &expiretime));
+ }
+ pv_state--;
+ CHECKr(pthread_mutex_unlock(&mutex));
+}
+
+void v() {
+ int needsignal;
+
+ CHECKr(pthread_mutex_lock(&mutex));
+ pv_state++;
+ needsignal = (pv_state == 1);
+ if (needsignal)
+ CHECKr(pthread_cond_signal(&cond));
+ CHECKr(pthread_mutex_unlock(&mutex));
+}
+
+void
+c1handler(void *fd)
+{
+ CHECKe(close((int)fd));
+ v();
+}
+
+void *
+child1fn(arg)
+ void *arg;
+{
+ int fd;
+ char buf[1024];
+ int len;
+
+ pthread_set_name_np(pthread_self(), "c1");
+ CHECKr(pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL));
+ /* something that will block */
+ CHECKe(fd = open("/dev/tty", O_RDONLY));
+ pthread_cleanup_push(c1handler, (void *)fd);
+ v();
+ while (1) {
+ CHECKe(len = read(fd, &buf, sizeof buf));
+ printf("child 1 read %d bytes\n", len);
+ }
+ PANIC("child 1");
+}
+
+static int c2_in_test = 0;
+
+void
+c2handler(void *arg)
+{
+ ASSERT(c2_in_test);
+ v();
+}
+
+void *
+child2fn(arg)
+ void *arg;
+{
+ pthread_set_name_np(pthread_self(), "c2");
+
+ CHECKr(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL));
+ pthread_cleanup_push(c2handler, NULL);
+ v();
+
+ while (1) {
+ struct timespec now;
+ struct timespec end;
+
+ /*
+ * XXX Be careful not to call any cancellation points
+ * until pthread_testcancel()
+ */
+
+ CHECKe(clock_gettime(CLOCK_REALTIME, &end));
+ end.tv_sec ++;
+
+ while (1) {
+ CHECKe(clock_gettime(CLOCK_REALTIME, &now));
+ if (timespeccmp(&now, &end, >=))
+ break;
+ pthread_yield();
+ }
+
+ /* XXX write() contains a cancellation point */
+ /* printf("child 2 testing for cancel\n"); */
+
+ c2_in_test = 1;
+ pthread_testcancel();
+ printf("you should see this message exactly once\n");
+ c2_in_test = 0;
+ }
+ PANIC("child 2");
+}
+
+static int c3_cancel_survived;
+
+void
+c3handler(void *arg)
+{
+ printf("(fyi, cancellation of self %s instantaneous)\n",
+ (c3_cancel_survived ? "was not" : "was"));
+ v();
+}
+
+void *
+child3fn(arg)
+ void *arg;
+{
+ pthread_set_name_np(pthread_self(), "c3");
+ pthread_cleanup_push(c3handler, NULL);
+
+ /* Cancel myself */
+ CHECKr(pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL));
+ c3_cancel_survived = 0;
+ pthread_cancel(pthread_self());
+ c3_cancel_survived = 1;
+ pthread_testcancel();
+
+ PANIC("child 3");
+}
+
+int
+main()
+{
+ pthread_t child1, child2, child3;
+
+ /* Set up our control flow */
+ CHECKr(pthread_mutex_init(&mutex, NULL));
+ CHECKr(pthread_cond_init(&cond, NULL));
+ CHECKe(clock_gettime(CLOCK_REALTIME, &expiretime));
+ expiretime.tv_sec += 5; /* this test shouldn't run over 5 seconds */
+
+ CHECKr(pthread_create(&child1, NULL, child1fn, NULL));
+ CHECKr(pthread_create(&child2, NULL, child2fn, NULL));
+ p();
+ p();
+
+ CHECKr(pthread_cancel(child1));
+ p();
+
+ /* Give thread 2 a change to go through its deferred loop once */
+ sleep(2);
+ CHECKr(pthread_cancel(child2));
+ p();
+
+ /* Child 3 cancels itself */
+ CHECKr(pthread_create(&child3, NULL, child3fn, NULL));
+ p();
+
+ /* Make sure they're all gone */
+ CHECKr(pthread_join(child3, NULL));
+ CHECKr(pthread_join(child2, NULL));
+ CHECKr(pthread_join(child1, NULL));
+
+ exit(0);
+}
diff --git a/lib/libc_r/man/pthread_cancel.3 b/lib/libc_r/man/pthread_cancel.3
new file mode 100644
index 00000000000..8522098a34e
--- /dev/null
+++ b/lib/libc_r/man/pthread_cancel.3
@@ -0,0 +1,65 @@
+.Dd January 17, 1999
+.Dt PTHREAD_CANCEL 3
+.Os BSD 4
+.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_testcancel 3 ,
+.Xr pthread_setcancelstate 3 ,
+.Xr pthread_setcanceltype 3 ,
+.Xr pthread_cleanup_pop 3 ,
+.Xr pthread_cleanup_push 3 ,
+.Xr pthread_exit 3 ,
+.Xr pthread_join 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.
diff --git a/lib/libc_r/man/pthread_testcancel.3 b/lib/libc_r/man/pthread_testcancel.3
new file mode 100644
index 00000000000..1cd02e8fe83
--- /dev/null
+++ b/lib/libc_r/man/pthread_testcancel.3
@@ -0,0 +1,182 @@
+.Dd January 17, 1999
+.Dt PTHREAD_TESTCANCEL 3
+.Os BSD 4
+.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.
diff --git a/lib/libc_r/uthread/uthread_cancel.c b/lib/libc_r/uthread/uthread_cancel.c
new file mode 100644
index 00000000000..f3a205d1e9e
--- /dev/null
+++ b/lib/libc_r/uthread/uthread_cancel.c
@@ -0,0 +1,164 @@
+/*
+ * David Leonard <d@openbsd.org>, 1999. Public domain.
+ */
+#include <sys/errno.h>
+#include <pthread.h>
+#include "pthread_private.h"
+
+int
+pthread_cancel(pthread)
+ pthread_t pthread;
+{
+ int ret;
+
+ if ((ret = _find_thread(pthread))) {
+ } else if (pthread->state == PS_DEAD)
+ ret = 0;
+ else {
+ /* Set the threads's I've-been-cancelled flag: */
+ pthread->flags |= PTHREAD_CANCELLING;
+ /* Check if we need to kick it back into the run queue: */
+ if ((pthread->cancelstate == PTHREAD_CANCEL_ENABLE) &&
+ ((pthread->canceltype == PTHREAD_CANCEL_ASYNCHRONOUS) ||
+ (pthread->flags & PTHREAD_AT_CANCEL_POINT)))
+ switch (pthread->state) {
+ case PS_RUNNING:
+ /* No need to resume: */
+ break;
+ case PS_WAIT_WAIT:
+ case PS_FDR_WAIT:
+ case PS_FDW_WAIT:
+ case PS_SLEEP_WAIT:
+ case PS_SELECT_WAIT:
+ case PS_SIGSUSPEND:
+ /* Interrupt and resume: */
+ pthread->interrupted = 1;
+ 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_SIGWAIT:
+ case PS_JOIN:
+ case PS_SUSPENDED:
+ case PS_SIGTHREAD:
+ /* Simply wake: */
+ /* XXX may be incorrect */
+ PTHREAD_NEW_STATE(pthread,PS_RUNNING);
+ break;
+ case PS_DEAD:
+ case PS_STATE_MAX:
+ /* Ignore */
+ break;
+ }
+ ret = 0;
+ }
+ return (ret);
+}
+
+int
+pthread_setcancelstate(state, oldstate)
+ int state;
+ int *oldstate;
+{
+ int ostate;
+ int ret;
+
+ ostate = _thread_run->cancelstate;
+
+ switch (state) {
+ case PTHREAD_CANCEL_ENABLE:
+ if (oldstate)
+ *oldstate = ostate;
+ _thread_run->cancelstate = PTHREAD_CANCEL_ENABLE;
+ if (_thread_run->canceltype == PTHREAD_CANCEL_ASYNCHRONOUS)
+ _thread_cancellation_point();
+ ret = 0;
+ break;
+ case PTHREAD_CANCEL_DISABLE:
+ if (oldstate)
+ *oldstate = ostate;
+ _thread_run->cancelstate = PTHREAD_CANCEL_DISABLE;
+ ret = 0;
+ break;
+ default:
+ ret = EINVAL;
+ }
+
+ return (ret);
+}
+
+
+int
+pthread_setcanceltype(type, oldtype)
+ int type;
+ int *oldtype;
+{
+ int otype;
+ int ret;
+
+ otype = _thread_run->canceltype;
+ switch (type) {
+ case PTHREAD_CANCEL_ASYNCHRONOUS:
+ if (oldtype)
+ *oldtype = otype;
+ _thread_run->canceltype = PTHREAD_CANCEL_ASYNCHRONOUS;
+ _thread_cancellation_point();
+ ret = 0;
+ break;
+ case PTHREAD_CANCEL_DEFERRED:
+ if (oldtype)
+ *oldtype = otype;
+ _thread_run->canceltype = PTHREAD_CANCEL_DEFERRED;
+ ret = 0;
+ break;
+ default:
+ ret = EINVAL;
+ }
+
+ return (ret);
+}
+
+void
+pthread_testcancel()
+{
+
+ _thread_cancellation_point();
+}
+
+void
+_thread_enter_cancellation_point()
+{
+
+ /* Look for a cancellation before we block: */
+ _thread_cancellation_point();
+ _thread_run->flags |= PTHREAD_AT_CANCEL_POINT;
+}
+
+void
+_thread_leave_cancellation_point()
+{
+
+ _thread_run->flags &=~ PTHREAD_AT_CANCEL_POINT;
+ /* Look for a cancellation after we unblock: */
+ _thread_cancellation_point();
+}
+
+/*
+ * Must only be called when in asynchronous cancel mode, or
+ * from pthread_testcancel().
+ */
+void
+_thread_cancellation_point()
+{
+
+ if ((_thread_run->cancelstate == PTHREAD_CANCEL_ENABLE) &&
+ ((_thread_run->flags & (PTHREAD_CANCELLING|PTHREAD_EXITING)) ==
+ PTHREAD_CANCELLING)) {
+ _thread_run->flags &=~ PTHREAD_CANCELLING;
+ pthread_exit(PTHREAD_CANCELED);
+ PANIC("cancel");
+ }
+}
diff --git a/lib/libc_r/uthread/uthread_close.c b/lib/libc_r/uthread/uthread_close.c
index 7d95fbb3196..bc94d162b2a 100644
--- a/lib/libc_r/uthread/uthread_close.c
+++ b/lib/libc_r/uthread/uthread_close.c
@@ -29,7 +29,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $OpenBSD: uthread_close.c,v 1.3 1999/01/06 05:29:22 d Exp $
+ * $OpenBSD: uthread_close.c,v 1.4 1999/01/17 23:57:27 d Exp $
*/
#include <stdlib.h>
#include <unistd.h>
@@ -46,6 +46,7 @@ close(int fd)
int ret;
struct stat sb;
+ _thread_enter_cancellation_point();
/* Lock the file descriptor while the file is closed: */
if ((ret = _FD_LOCK(fd, FD_RDWR, NULL)) == 0) {
/* Get file descriptor status. */
@@ -84,6 +85,7 @@ close(int fd)
free(_thread_fd_table[fd]);
_thread_fd_table[fd] = NULL;
}
+ _thread_leave_cancellation_point();
return (ret);
}
#endif
diff --git a/lib/libc_r/uthread/uthread_cond.c b/lib/libc_r/uthread/uthread_cond.c
index b2ef3df1ac5..525f86049b0 100644
--- a/lib/libc_r/uthread/uthread_cond.c
+++ b/lib/libc_r/uthread/uthread_cond.c
@@ -29,7 +29,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $OpenBSD: uthread_cond.c,v 1.4 1999/01/06 05:29:22 d Exp $
+ * $OpenBSD: uthread_cond.c,v 1.5 1999/01/17 23:57:27 d Exp $
*/
#include <stdlib.h>
#include <errno.h>
@@ -137,6 +137,8 @@ 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);
@@ -185,6 +187,7 @@ pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
break;
}
+ _thread_leave_cancellation_point();
}
/* Return the completion status: */
@@ -206,6 +209,7 @@ pthread_cond_timedwait(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);
@@ -263,7 +267,7 @@ pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
rval = EINVAL;
break;
}
-
+ _thread_leave_cancellation_point();
}
/* Return the completion status: */
diff --git a/lib/libc_r/uthread/uthread_create.c b/lib/libc_r/uthread/uthread_create.c
index 178c35424b8..1ed287d2b1b 100644
--- a/lib/libc_r/uthread/uthread_create.c
+++ b/lib/libc_r/uthread/uthread_create.c
@@ -29,7 +29,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $OpenBSD: uthread_create.c,v 1.5 1999/01/06 05:29:22 d Exp $
+ * $OpenBSD: uthread_create.c,v 1.6 1999/01/17 23:57:27 d Exp $
*/
#include <errno.h>
#include <stdlib.h>
@@ -93,6 +93,9 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr,
timerclear(&new_thread->ru_stime);
_SPINUNLOCK(&new_thread->lock);
+ new_thread->cancelstate = PTHREAD_CANCEL_ENABLE;
+ new_thread->canceltype = PTHREAD_CANCEL_DEFERRED;
+
/*
* Write a magic value to the thread structure
* to help identify valid ones:
@@ -109,7 +112,7 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr,
new_thread->sigmask = _thread_run->sigmask;
/* Initialise the jump buffer: */
- setjmp(new_thread->saved_jmp_buf);
+ _thread_machdep_setjmp(new_thread->saved_jmp_buf);
/*
* Set up new stack frame so that it looks like it
diff --git a/lib/libc_r/uthread/uthread_fcntl.c b/lib/libc_r/uthread/uthread_fcntl.c
index e7e11a2e218..1a0a3e9548a 100644
--- a/lib/libc_r/uthread/uthread_fcntl.c
+++ b/lib/libc_r/uthread/uthread_fcntl.c
@@ -29,7 +29,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $OpenBSD: uthread_fcntl.c,v 1.3 1999/01/06 05:29:23 d Exp $
+ * $OpenBSD: uthread_fcntl.c,v 1.4 1999/01/17 23:57:27 d Exp $
*/
#include <stdarg.h>
#include <unistd.h>
@@ -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);
diff --git a/lib/libc_r/uthread/uthread_fsync.c b/lib/libc_r/uthread/uthread_fsync.c
index 4c953443e95..9db34f5564c 100644
--- a/lib/libc_r/uthread/uthread_fsync.c
+++ b/lib/libc_r/uthread/uthread_fsync.c
@@ -29,7 +29,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $OpenBSD: uthread_fsync.c,v 1.2 1999/01/06 05:29:24 d Exp $
+ * $OpenBSD: uthread_fsync.c,v 1.3 1999/01/17 23:57:27 d Exp $
*/
#include <unistd.h>
#ifdef _THREAD_SAFE
@@ -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
diff --git a/lib/libc_r/uthread/uthread_init.c b/lib/libc_r/uthread/uthread_init.c
index ad56b9a670c..0cd5bd376c4 100644
--- a/lib/libc_r/uthread/uthread_init.c
+++ b/lib/libc_r/uthread/uthread_init.c
@@ -29,7 +29,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $OpenBSD: uthread_init.c,v 1.7 1999/01/10 23:11:33 d Exp $
+ * $OpenBSD: uthread_init.c,v 1.8 1999/01/17 23:57:27 d Exp $
*/
#include <errno.h>
@@ -224,6 +224,8 @@ _thread_init(void)
_thread_initial->nxt = NULL;
_thread_initial->flags = 0;
_thread_initial->error = 0;
+ _thread_initial->cancelstate = PTHREAD_CANCEL_ENABLE;
+ _thread_initial->canceltype = PTHREAD_CANCEL_DEFERRED;
_thread_initial->magic = PTHREAD_MAGIC;
pthread_set_name_np(_thread_initial, "init");
_SPINUNLOCK(&_thread_initial->lock);
diff --git a/lib/libc_r/uthread/uthread_join.c b/lib/libc_r/uthread/uthread_join.c
index 9602ec52a25..52baee31c33 100644
--- a/lib/libc_r/uthread/uthread_join.c
+++ b/lib/libc_r/uthread/uthread_join.c
@@ -29,7 +29,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $OpenBSD: uthread_join.c,v 1.2 1999/01/06 05:29:24 d Exp $
+ * $OpenBSD: uthread_join.c,v 1.3 1999/01/17 23:57:27 d Exp $
*/
#include <errno.h>
#ifdef _THREAD_SAFE
@@ -42,15 +42,21 @@ 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
@@ -93,6 +99,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);
}
diff --git a/lib/libc_r/uthread/uthread_nanosleep.c b/lib/libc_r/uthread/uthread_nanosleep.c
index 450b505bf12..0f5c4c9c2ab 100644
--- a/lib/libc_r/uthread/uthread_nanosleep.c
+++ b/lib/libc_r/uthread/uthread_nanosleep.c
@@ -29,7 +29,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $OpenBSD: uthread_nanosleep.c,v 1.2 1999/01/06 05:29:25 d Exp $
+ * $OpenBSD: uthread_nanosleep.c,v 1.3 1999/01/17 23:57:27 d Exp $
*/
#include <stdio.h>
#include <errno.h>
@@ -48,6 +48,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_nsec < 0 || time_to_sleep->tv_nsec > 1000000000 || time_to_sleep->tv_sec < 0) {
/* Return an EINVAL error : */
@@ -95,6 +96,7 @@ nanosleep(const struct timespec * time_to_sleep,
ret = -1;
}
}
+ _thread_leave_cancellation_point();
return (ret);
}
#endif
diff --git a/lib/libc_r/uthread/uthread_open.c b/lib/libc_r/uthread/uthread_open.c
index 52d0a6b06cf..9c03daa4f47 100644
--- a/lib/libc_r/uthread/uthread_open.c
+++ b/lib/libc_r/uthread/uthread_open.c
@@ -30,7 +30,7 @@
* SUCH DAMAGE.
*
* $FreeBSD: uthread_open.c,v 1.4 1998/04/29 09:59:07 jb Exp $
- * $OpenBSD: uthread_open.c,v 1.2 1998/12/23 22:49:46 d Exp $
+ * $OpenBSD: uthread_open.c,v 1.3 1999/01/17 23:57:27 d Exp $
*
*/
#include <stdarg.h>
@@ -49,6 +49,8 @@ open(const char *path, int flags,...)
int mode = 0;
va_list ap;
+ _thread_enter_cancellation_point();
+
/* Check if the file is being created: */
if (flags & O_CREAT) {
/* Get the creation mode: */
@@ -68,6 +70,8 @@ open(const char *path, int flags,...)
fd = -1;
}
+ _thread_leave_cancellation_point();
+
/* Return the file descriptor or -1 on error: */
return (fd);
}
diff --git a/lib/libc_r/uthread/uthread_read.c b/lib/libc_r/uthread/uthread_read.c
index 37b0c9067bb..22d1d03f091 100644
--- a/lib/libc_r/uthread/uthread_read.c
+++ b/lib/libc_r/uthread/uthread_read.c
@@ -30,7 +30,7 @@
* SUCH DAMAGE.
*
* $FreeBSD: uthread_read.c,v 1.6 1998/06/10 22:28:43 jb Exp $
- * $OpenBSD: uthread_read.c,v 1.2 1998/12/23 22:49:46 d Exp $
+ * $OpenBSD: uthread_read.c,v 1.3 1999/01/17 23:57:27 d Exp $
*
*/
#include <sys/types.h>
@@ -48,9 +48,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) {
@@ -62,6 +66,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);
}
@@ -93,6 +98,7 @@ read(int fd, void *buf, size_t nbytes)
}
_FD_UNLOCK(fd, FD_READ);
}
+ _thread_leave_cancellation_point();
return (ret);
}
#endif
diff --git a/lib/libc_r/uthread/uthread_sigwait.c b/lib/libc_r/uthread/uthread_sigwait.c
index 8432e879646..3593b72853f 100644
--- a/lib/libc_r/uthread/uthread_sigwait.c
+++ b/lib/libc_r/uthread/uthread_sigwait.c
@@ -29,7 +29,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $OpenBSD: uthread_sigwait.c,v 1.3 1999/01/06 05:29:28 d Exp $
+ * $OpenBSD: uthread_sigwait.c,v 1.4 1999/01/17 23:57:27 d Exp $
*/
#include <signal.h>
#include <errno.h>
@@ -45,6 +45,7 @@ sigwait(const sigset_t * set, int *sig)
sigset_t tempset;
struct sigaction act;
+ _thread_enter_cancellation_point();
/*
* Specify the thread kernel signal handler.
*/
@@ -75,6 +76,7 @@ sigwait(const sigset_t * set, int *sig)
/* Return the signal number to the caller: */
*sig = i;
+ _thread_leave_cancellation_point();
return (0);
}
@@ -127,6 +129,8 @@ sigwait(const sigset_t * set, int *sig)
}
}
+ _thread_leave_cancellation_point();
+
/* Return the completion status: */
return (ret);
}
diff --git a/lib/libc_r/uthread/uthread_wait4.c b/lib/libc_r/uthread/uthread_wait4.c
index 5017fe5c1ac..a4f44e1d405 100644
--- a/lib/libc_r/uthread/uthread_wait4.c
+++ b/lib/libc_r/uthread/uthread_wait4.c
@@ -29,7 +29,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $OpenBSD: uthread_wait4.c,v 1.2 1999/01/06 05:29:29 d Exp $
+ * $OpenBSD: uthread_wait4.c,v 1.3 1999/01/17 23:57:28 d Exp $
*/
#include <errno.h>
#include <sys/wait.h>
@@ -42,6 +42,7 @@ wait4(pid_t pid, int *istat, int options, struct rusage * rusage)
{
pid_t ret;
+ _thread_enter_cancellation_point();
/* Perform a non-blocking wait4 syscall: */
while ((ret = _thread_sys_wait4(pid, istat, options | WNOHANG, rusage)) == 0 && (options & WNOHANG) == 0) {
/* Reset the interrupted operation flag: */
@@ -57,6 +58,7 @@ wait4(pid_t pid, int *istat, int options, struct rusage * rusage)
break;
}
}
+ _thread_leave_cancellation_point();
return (ret);
}
#endif
diff --git a/lib/libc_r/uthread/uthread_write.c b/lib/libc_r/uthread/uthread_write.c
index ba8585ec8b2..b7c974ee289 100644
--- a/lib/libc_r/uthread/uthread_write.c
+++ b/lib/libc_r/uthread/uthread_write.c
@@ -30,7 +30,7 @@
* SUCH DAMAGE.
*
* $FreeBSD: uthread_write.c,v 1.10 1998/09/07 21:55:01 alex Exp $
- * $OpenBSD: uthread_write.c,v 1.2 1998/12/23 22:49:47 d Exp $
+ * $OpenBSD: uthread_write.c,v 1.3 1999/01/17 23:57:28 d Exp $
*
*/
#include <sys/types.h>
@@ -51,9 +51,13 @@ 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) {
@@ -65,6 +69,7 @@ write(int fd, const void *buf, size_t nbytes)
/* File is not open for write: */
errno = EBADF;
_FD_UNLOCK(fd, FD_WRITE);
+ _thread_leave_cancellation_point();
return (-1);
}
@@ -130,6 +135,7 @@ write(int fd, const void *buf, size_t nbytes)
}
_FD_UNLOCK(fd, FD_RDWR);
}
+ _thread_leave_cancellation_point();
return (ret);
}
#endif
diff --git a/lib/libpthread/man/pthread_cancel.3 b/lib/libpthread/man/pthread_cancel.3
new file mode 100644
index 00000000000..8522098a34e
--- /dev/null
+++ b/lib/libpthread/man/pthread_cancel.3
@@ -0,0 +1,65 @@
+.Dd January 17, 1999
+.Dt PTHREAD_CANCEL 3
+.Os BSD 4
+.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_testcancel 3 ,
+.Xr pthread_setcancelstate 3 ,
+.Xr pthread_setcanceltype 3 ,
+.Xr pthread_cleanup_pop 3 ,
+.Xr pthread_cleanup_push 3 ,
+.Xr pthread_exit 3 ,
+.Xr pthread_join 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.
diff --git a/lib/libpthread/man/pthread_testcancel.3 b/lib/libpthread/man/pthread_testcancel.3
new file mode 100644
index 00000000000..1cd02e8fe83
--- /dev/null
+++ b/lib/libpthread/man/pthread_testcancel.3
@@ -0,0 +1,182 @@
+.Dd January 17, 1999
+.Dt PTHREAD_TESTCANCEL 3
+.Os BSD 4
+.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.
diff --git a/lib/libpthread/uthread/uthread_cancel.c b/lib/libpthread/uthread/uthread_cancel.c
new file mode 100644
index 00000000000..f3a205d1e9e
--- /dev/null
+++ b/lib/libpthread/uthread/uthread_cancel.c
@@ -0,0 +1,164 @@
+/*
+ * David Leonard <d@openbsd.org>, 1999. Public domain.
+ */
+#include <sys/errno.h>
+#include <pthread.h>
+#include "pthread_private.h"
+
+int
+pthread_cancel(pthread)
+ pthread_t pthread;
+{
+ int ret;
+
+ if ((ret = _find_thread(pthread))) {
+ } else if (pthread->state == PS_DEAD)
+ ret = 0;
+ else {
+ /* Set the threads's I've-been-cancelled flag: */
+ pthread->flags |= PTHREAD_CANCELLING;
+ /* Check if we need to kick it back into the run queue: */
+ if ((pthread->cancelstate == PTHREAD_CANCEL_ENABLE) &&
+ ((pthread->canceltype == PTHREAD_CANCEL_ASYNCHRONOUS) ||
+ (pthread->flags & PTHREAD_AT_CANCEL_POINT)))
+ switch (pthread->state) {
+ case PS_RUNNING:
+ /* No need to resume: */
+ break;
+ case PS_WAIT_WAIT:
+ case PS_FDR_WAIT:
+ case PS_FDW_WAIT:
+ case PS_SLEEP_WAIT:
+ case PS_SELECT_WAIT:
+ case PS_SIGSUSPEND:
+ /* Interrupt and resume: */
+ pthread->interrupted = 1;
+ 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_SIGWAIT:
+ case PS_JOIN:
+ case PS_SUSPENDED:
+ case PS_SIGTHREAD:
+ /* Simply wake: */
+ /* XXX may be incorrect */
+ PTHREAD_NEW_STATE(pthread,PS_RUNNING);
+ break;
+ case PS_DEAD:
+ case PS_STATE_MAX:
+ /* Ignore */
+ break;
+ }
+ ret = 0;
+ }
+ return (ret);
+}
+
+int
+pthread_setcancelstate(state, oldstate)
+ int state;
+ int *oldstate;
+{
+ int ostate;
+ int ret;
+
+ ostate = _thread_run->cancelstate;
+
+ switch (state) {
+ case PTHREAD_CANCEL_ENABLE:
+ if (oldstate)
+ *oldstate = ostate;
+ _thread_run->cancelstate = PTHREAD_CANCEL_ENABLE;
+ if (_thread_run->canceltype == PTHREAD_CANCEL_ASYNCHRONOUS)
+ _thread_cancellation_point();
+ ret = 0;
+ break;
+ case PTHREAD_CANCEL_DISABLE:
+ if (oldstate)
+ *oldstate = ostate;
+ _thread_run->cancelstate = PTHREAD_CANCEL_DISABLE;
+ ret = 0;
+ break;
+ default:
+ ret = EINVAL;
+ }
+
+ return (ret);
+}
+
+
+int
+pthread_setcanceltype(type, oldtype)
+ int type;
+ int *oldtype;
+{
+ int otype;
+ int ret;
+
+ otype = _thread_run->canceltype;
+ switch (type) {
+ case PTHREAD_CANCEL_ASYNCHRONOUS:
+ if (oldtype)
+ *oldtype = otype;
+ _thread_run->canceltype = PTHREAD_CANCEL_ASYNCHRONOUS;
+ _thread_cancellation_point();
+ ret = 0;
+ break;
+ case PTHREAD_CANCEL_DEFERRED:
+ if (oldtype)
+ *oldtype = otype;
+ _thread_run->canceltype = PTHREAD_CANCEL_DEFERRED;
+ ret = 0;
+ break;
+ default:
+ ret = EINVAL;
+ }
+
+ return (ret);
+}
+
+void
+pthread_testcancel()
+{
+
+ _thread_cancellation_point();
+}
+
+void
+_thread_enter_cancellation_point()
+{
+
+ /* Look for a cancellation before we block: */
+ _thread_cancellation_point();
+ _thread_run->flags |= PTHREAD_AT_CANCEL_POINT;
+}
+
+void
+_thread_leave_cancellation_point()
+{
+
+ _thread_run->flags &=~ PTHREAD_AT_CANCEL_POINT;
+ /* Look for a cancellation after we unblock: */
+ _thread_cancellation_point();
+}
+
+/*
+ * Must only be called when in asynchronous cancel mode, or
+ * from pthread_testcancel().
+ */
+void
+_thread_cancellation_point()
+{
+
+ if ((_thread_run->cancelstate == PTHREAD_CANCEL_ENABLE) &&
+ ((_thread_run->flags & (PTHREAD_CANCELLING|PTHREAD_EXITING)) ==
+ PTHREAD_CANCELLING)) {
+ _thread_run->flags &=~ PTHREAD_CANCELLING;
+ pthread_exit(PTHREAD_CANCELED);
+ PANIC("cancel");
+ }
+}
diff --git a/lib/libpthread/uthread/uthread_close.c b/lib/libpthread/uthread/uthread_close.c
index 7d95fbb3196..bc94d162b2a 100644
--- a/lib/libpthread/uthread/uthread_close.c
+++ b/lib/libpthread/uthread/uthread_close.c
@@ -29,7 +29,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $OpenBSD: uthread_close.c,v 1.3 1999/01/06 05:29:22 d Exp $
+ * $OpenBSD: uthread_close.c,v 1.4 1999/01/17 23:57:27 d Exp $
*/
#include <stdlib.h>
#include <unistd.h>
@@ -46,6 +46,7 @@ close(int fd)
int ret;
struct stat sb;
+ _thread_enter_cancellation_point();
/* Lock the file descriptor while the file is closed: */
if ((ret = _FD_LOCK(fd, FD_RDWR, NULL)) == 0) {
/* Get file descriptor status. */
@@ -84,6 +85,7 @@ close(int fd)
free(_thread_fd_table[fd]);
_thread_fd_table[fd] = NULL;
}
+ _thread_leave_cancellation_point();
return (ret);
}
#endif
diff --git a/lib/libpthread/uthread/uthread_cond.c b/lib/libpthread/uthread/uthread_cond.c
index b2ef3df1ac5..525f86049b0 100644
--- a/lib/libpthread/uthread/uthread_cond.c
+++ b/lib/libpthread/uthread/uthread_cond.c
@@ -29,7 +29,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $OpenBSD: uthread_cond.c,v 1.4 1999/01/06 05:29:22 d Exp $
+ * $OpenBSD: uthread_cond.c,v 1.5 1999/01/17 23:57:27 d Exp $
*/
#include <stdlib.h>
#include <errno.h>
@@ -137,6 +137,8 @@ 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);
@@ -185,6 +187,7 @@ pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
break;
}
+ _thread_leave_cancellation_point();
}
/* Return the completion status: */
@@ -206,6 +209,7 @@ pthread_cond_timedwait(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);
@@ -263,7 +267,7 @@ pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
rval = EINVAL;
break;
}
-
+ _thread_leave_cancellation_point();
}
/* Return the completion status: */
diff --git a/lib/libpthread/uthread/uthread_create.c b/lib/libpthread/uthread/uthread_create.c
index 178c35424b8..1ed287d2b1b 100644
--- a/lib/libpthread/uthread/uthread_create.c
+++ b/lib/libpthread/uthread/uthread_create.c
@@ -29,7 +29,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $OpenBSD: uthread_create.c,v 1.5 1999/01/06 05:29:22 d Exp $
+ * $OpenBSD: uthread_create.c,v 1.6 1999/01/17 23:57:27 d Exp $
*/
#include <errno.h>
#include <stdlib.h>
@@ -93,6 +93,9 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr,
timerclear(&new_thread->ru_stime);
_SPINUNLOCK(&new_thread->lock);
+ new_thread->cancelstate = PTHREAD_CANCEL_ENABLE;
+ new_thread->canceltype = PTHREAD_CANCEL_DEFERRED;
+
/*
* Write a magic value to the thread structure
* to help identify valid ones:
@@ -109,7 +112,7 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr,
new_thread->sigmask = _thread_run->sigmask;
/* Initialise the jump buffer: */
- setjmp(new_thread->saved_jmp_buf);
+ _thread_machdep_setjmp(new_thread->saved_jmp_buf);
/*
* Set up new stack frame so that it looks like it
diff --git a/lib/libpthread/uthread/uthread_fcntl.c b/lib/libpthread/uthread/uthread_fcntl.c
index e7e11a2e218..1a0a3e9548a 100644
--- a/lib/libpthread/uthread/uthread_fcntl.c
+++ b/lib/libpthread/uthread/uthread_fcntl.c
@@ -29,7 +29,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $OpenBSD: uthread_fcntl.c,v 1.3 1999/01/06 05:29:23 d Exp $
+ * $OpenBSD: uthread_fcntl.c,v 1.4 1999/01/17 23:57:27 d Exp $
*/
#include <stdarg.h>
#include <unistd.h>
@@ -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);
diff --git a/lib/libpthread/uthread/uthread_fsync.c b/lib/libpthread/uthread/uthread_fsync.c
index 4c953443e95..9db34f5564c 100644
--- a/lib/libpthread/uthread/uthread_fsync.c
+++ b/lib/libpthread/uthread/uthread_fsync.c
@@ -29,7 +29,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $OpenBSD: uthread_fsync.c,v 1.2 1999/01/06 05:29:24 d Exp $
+ * $OpenBSD: uthread_fsync.c,v 1.3 1999/01/17 23:57:27 d Exp $
*/
#include <unistd.h>
#ifdef _THREAD_SAFE
@@ -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
diff --git a/lib/libpthread/uthread/uthread_init.c b/lib/libpthread/uthread/uthread_init.c
index ad56b9a670c..0cd5bd376c4 100644
--- a/lib/libpthread/uthread/uthread_init.c
+++ b/lib/libpthread/uthread/uthread_init.c
@@ -29,7 +29,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $OpenBSD: uthread_init.c,v 1.7 1999/01/10 23:11:33 d Exp $
+ * $OpenBSD: uthread_init.c,v 1.8 1999/01/17 23:57:27 d Exp $
*/
#include <errno.h>
@@ -224,6 +224,8 @@ _thread_init(void)
_thread_initial->nxt = NULL;
_thread_initial->flags = 0;
_thread_initial->error = 0;
+ _thread_initial->cancelstate = PTHREAD_CANCEL_ENABLE;
+ _thread_initial->canceltype = PTHREAD_CANCEL_DEFERRED;
_thread_initial->magic = PTHREAD_MAGIC;
pthread_set_name_np(_thread_initial, "init");
_SPINUNLOCK(&_thread_initial->lock);
diff --git a/lib/libpthread/uthread/uthread_join.c b/lib/libpthread/uthread/uthread_join.c
index 9602ec52a25..52baee31c33 100644
--- a/lib/libpthread/uthread/uthread_join.c
+++ b/lib/libpthread/uthread/uthread_join.c
@@ -29,7 +29,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $OpenBSD: uthread_join.c,v 1.2 1999/01/06 05:29:24 d Exp $
+ * $OpenBSD: uthread_join.c,v 1.3 1999/01/17 23:57:27 d Exp $
*/
#include <errno.h>
#ifdef _THREAD_SAFE
@@ -42,15 +42,21 @@ 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
@@ -93,6 +99,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);
}
diff --git a/lib/libpthread/uthread/uthread_nanosleep.c b/lib/libpthread/uthread/uthread_nanosleep.c
index 450b505bf12..0f5c4c9c2ab 100644
--- a/lib/libpthread/uthread/uthread_nanosleep.c
+++ b/lib/libpthread/uthread/uthread_nanosleep.c
@@ -29,7 +29,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $OpenBSD: uthread_nanosleep.c,v 1.2 1999/01/06 05:29:25 d Exp $
+ * $OpenBSD: uthread_nanosleep.c,v 1.3 1999/01/17 23:57:27 d Exp $
*/
#include <stdio.h>
#include <errno.h>
@@ -48,6 +48,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_nsec < 0 || time_to_sleep->tv_nsec > 1000000000 || time_to_sleep->tv_sec < 0) {
/* Return an EINVAL error : */
@@ -95,6 +96,7 @@ nanosleep(const struct timespec * time_to_sleep,
ret = -1;
}
}
+ _thread_leave_cancellation_point();
return (ret);
}
#endif
diff --git a/lib/libpthread/uthread/uthread_open.c b/lib/libpthread/uthread/uthread_open.c
index 52d0a6b06cf..9c03daa4f47 100644
--- a/lib/libpthread/uthread/uthread_open.c
+++ b/lib/libpthread/uthread/uthread_open.c
@@ -30,7 +30,7 @@
* SUCH DAMAGE.
*
* $FreeBSD: uthread_open.c,v 1.4 1998/04/29 09:59:07 jb Exp $
- * $OpenBSD: uthread_open.c,v 1.2 1998/12/23 22:49:46 d Exp $
+ * $OpenBSD: uthread_open.c,v 1.3 1999/01/17 23:57:27 d Exp $
*
*/
#include <stdarg.h>
@@ -49,6 +49,8 @@ open(const char *path, int flags,...)
int mode = 0;
va_list ap;
+ _thread_enter_cancellation_point();
+
/* Check if the file is being created: */
if (flags & O_CREAT) {
/* Get the creation mode: */
@@ -68,6 +70,8 @@ open(const char *path, int flags,...)
fd = -1;
}
+ _thread_leave_cancellation_point();
+
/* Return the file descriptor or -1 on error: */
return (fd);
}
diff --git a/lib/libpthread/uthread/uthread_read.c b/lib/libpthread/uthread/uthread_read.c
index 37b0c9067bb..22d1d03f091 100644
--- a/lib/libpthread/uthread/uthread_read.c
+++ b/lib/libpthread/uthread/uthread_read.c
@@ -30,7 +30,7 @@
* SUCH DAMAGE.
*
* $FreeBSD: uthread_read.c,v 1.6 1998/06/10 22:28:43 jb Exp $
- * $OpenBSD: uthread_read.c,v 1.2 1998/12/23 22:49:46 d Exp $
+ * $OpenBSD: uthread_read.c,v 1.3 1999/01/17 23:57:27 d Exp $
*
*/
#include <sys/types.h>
@@ -48,9 +48,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) {
@@ -62,6 +66,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);
}
@@ -93,6 +98,7 @@ read(int fd, void *buf, size_t nbytes)
}
_FD_UNLOCK(fd, FD_READ);
}
+ _thread_leave_cancellation_point();
return (ret);
}
#endif
diff --git a/lib/libpthread/uthread/uthread_sigwait.c b/lib/libpthread/uthread/uthread_sigwait.c
index 8432e879646..3593b72853f 100644
--- a/lib/libpthread/uthread/uthread_sigwait.c
+++ b/lib/libpthread/uthread/uthread_sigwait.c
@@ -29,7 +29,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $OpenBSD: uthread_sigwait.c,v 1.3 1999/01/06 05:29:28 d Exp $
+ * $OpenBSD: uthread_sigwait.c,v 1.4 1999/01/17 23:57:27 d Exp $
*/
#include <signal.h>
#include <errno.h>
@@ -45,6 +45,7 @@ sigwait(const sigset_t * set, int *sig)
sigset_t tempset;
struct sigaction act;
+ _thread_enter_cancellation_point();
/*
* Specify the thread kernel signal handler.
*/
@@ -75,6 +76,7 @@ sigwait(const sigset_t * set, int *sig)
/* Return the signal number to the caller: */
*sig = i;
+ _thread_leave_cancellation_point();
return (0);
}
@@ -127,6 +129,8 @@ sigwait(const sigset_t * set, int *sig)
}
}
+ _thread_leave_cancellation_point();
+
/* Return the completion status: */
return (ret);
}
diff --git a/lib/libpthread/uthread/uthread_wait4.c b/lib/libpthread/uthread/uthread_wait4.c
index 5017fe5c1ac..a4f44e1d405 100644
--- a/lib/libpthread/uthread/uthread_wait4.c
+++ b/lib/libpthread/uthread/uthread_wait4.c
@@ -29,7 +29,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $OpenBSD: uthread_wait4.c,v 1.2 1999/01/06 05:29:29 d Exp $
+ * $OpenBSD: uthread_wait4.c,v 1.3 1999/01/17 23:57:28 d Exp $
*/
#include <errno.h>
#include <sys/wait.h>
@@ -42,6 +42,7 @@ wait4(pid_t pid, int *istat, int options, struct rusage * rusage)
{
pid_t ret;
+ _thread_enter_cancellation_point();
/* Perform a non-blocking wait4 syscall: */
while ((ret = _thread_sys_wait4(pid, istat, options | WNOHANG, rusage)) == 0 && (options & WNOHANG) == 0) {
/* Reset the interrupted operation flag: */
@@ -57,6 +58,7 @@ wait4(pid_t pid, int *istat, int options, struct rusage * rusage)
break;
}
}
+ _thread_leave_cancellation_point();
return (ret);
}
#endif
diff --git a/lib/libpthread/uthread/uthread_write.c b/lib/libpthread/uthread/uthread_write.c
index ba8585ec8b2..b7c974ee289 100644
--- a/lib/libpthread/uthread/uthread_write.c
+++ b/lib/libpthread/uthread/uthread_write.c
@@ -30,7 +30,7 @@
* SUCH DAMAGE.
*
* $FreeBSD: uthread_write.c,v 1.10 1998/09/07 21:55:01 alex Exp $
- * $OpenBSD: uthread_write.c,v 1.2 1998/12/23 22:49:47 d Exp $
+ * $OpenBSD: uthread_write.c,v 1.3 1999/01/17 23:57:28 d Exp $
*
*/
#include <sys/types.h>
@@ -51,9 +51,13 @@ 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) {
@@ -65,6 +69,7 @@ write(int fd, const void *buf, size_t nbytes)
/* File is not open for write: */
errno = EBADF;
_FD_UNLOCK(fd, FD_WRITE);
+ _thread_leave_cancellation_point();
return (-1);
}
@@ -130,6 +135,7 @@ write(int fd, const void *buf, size_t nbytes)
}
_FD_UNLOCK(fd, FD_RDWR);
}
+ _thread_leave_cancellation_point();
return (ret);
}
#endif