summaryrefslogtreecommitdiff
path: root/hurd
diff options
context:
space:
mode:
authorRoland McGrath <roland@gnu.org>1995-08-14 22:49:23 +0000
committerRoland McGrath <roland@gnu.org>1995-08-14 22:49:23 +0000
commit54da5be39c868b55c234c23bb38eb42babc084d7 (patch)
tree64fe7cd0dfeaef3a8b6f469f3a7a83a3a974067a /hurd
parent047f282dd7e83f24abce284dcc9d2d78cab42f9b (diff)
Mon Aug 14 16:51:13 1995 Roland McGrath <roland@churchy.gnu.ai.mit.edu>
* hurd/thread-cancel.c: New file. * sysdeps/mach/hurd/i386/trampoline.c (_hurd_setup_sighandler): In rpc_wait case, frob mach_msg args to set timeout on receive. (_hurdsig_rcv_interrupted_p): Function removed. * sysdeps/mach/hurd/alpha/trampoline.c: Likewise. * sysdeps/mach/hurd/hppa/trampoline.c: Likewise. * sysdeps/mach/hurd/mips/trampoline.c: Likewise. * hurd/intr-msg.c: New file. * hurd/hurd/signal.h (struct hurd_sigstate): New member `cancel'. (_hurdsig_rcv_interrupted_p): Declaration removed. (HURD_EINTR_RPC): Macro removed. (_hurd_longjmp_thread_state, _hurd_interrupted_rpc_timeout): Declare these. * hurd/intr-rpc.h: New file. * hurd/intr-rpc.defs: Just import intr-rpc.h. * hurd/hurdsig.c (_hurd_interrupted_rpc_timeout): New variable. (interrupted_reply_port_location): Take new flag arg; only catch faults if it's set. (abort_rpcs): Rename to _hurdsig_abort_rpcs; take same new flag arg. No longer use _hurdsig_rcv_interrupted_p; instead compare PC to &_hurd_intr_rpc_msg_in_trap. If before it, mutate state to simulate MACH_SEND_INTERRUPTED return; on it, interrupt the operation. All callers changed. * hurd/hurd.h (hurd_thread_cancel, hurd_check_cancel): Declare these. * hurd/Makefile (distribute): Remove intr-rpc.awk. (sig): Add thread-cancel. (transform-user-stub, transform-user-stub-output): Variables removed. * sysdeps/mach/hurd/dl-sysdep.c: Change all RPCs from `__hurd_intr_rpc_*' to `__*'. (_hurd_intr_rpc_mach_msg): New function. (_hurd_thread_sigstate): Function removed. * sysdeps/mach/hurd/ioctl.c: Use _hurd_intr_rpc_mach_msg function, instead of __mach_msg inside HURD_EINTR_RPC macro. * sysdeps/generic/morecore.c [__GNU_LIBRARY__]: Declare `__sbrk' to take ptrdiff_t arg. * sysdeps/mach/hurd/fork.c: Remove _hurd_longjmp_thread_state decl. * sysdeps/mach/hurd/kill.c (kill_pid): Don't make `inline'. * libc-symbols.h [GCC >= 2.7] (strong_alias, weak_symbol, weak_alias): Use `extern' storage class.
Diffstat (limited to 'hurd')
-rw-r--r--hurd/Makefile20
-rw-r--r--hurd/hurd.h10
-rw-r--r--hurd/hurd/signal.h62
-rw-r--r--hurd/hurdsig.c181
-rw-r--r--hurd/intr-msg.c151
-rw-r--r--hurd/intr-rpc.defs12
-rw-r--r--hurd/intr-rpc.h25
-rw-r--r--hurd/thread-cancel.c86
8 files changed, 385 insertions, 162 deletions
diff --git a/hurd/Makefile b/hurd/Makefile
index 5e1622d87d..58bea5f1c0 100644
--- a/hurd/Makefile
+++ b/hurd/Makefile
@@ -28,7 +28,7 @@ headers = hurd.h $(interface-headers) \
$(addprefix hurd/,fd.h id.h port.h signal.h userlink.h \
resource.h threadvar.h)
-distribute := hurdstartup.h hurdfault.h intr-rpc.awk intr-rpc.defs STATUS
+distribute := hurdstartup.h hurdfault.h intr-rpc.defs STATUS
# The RPC interfaces go in a separate library.
interface-library := libhurduser
@@ -53,7 +53,7 @@ routines = hurdstartup hurdinit \
$(sig) $(dtable) hurdinline port-cleanup
sig = hurdsig hurdfault faultexc siginfo hurd-raise preempt-sig \
trampoline longjmp-ts catch-exc exc2signal hurdkill sigunwind \
- thread-self
+ thread-self thread-cancel
dtable = dtable port2fd new-fd alloc-fd intern-fd \
getdport openport \
fd-close fd-read fd-write hurdioctl ctty-input ctty-output
@@ -88,25 +88,13 @@ include ../mach/Machrules
include ../Rules
# intr-rpc.defs defines the INTR_INTERFACE macro to make the generated RPC
-# stubs send-interruptible, and to prefix them with `hurd_intr_rpc_'.
+# stubs import <hurd/signal.h> and #define __mach_msg to
+# _hurd_intr_rpc_mach_msg.
user-MIGFLAGS += -imacros intr-rpc.defs
-# Run each generated user stub through intr-rpc.awk, which will detect
-# stubs __hurd_intr_rpc_% and generate the user-callable function for the
-# stub: this is a wrapper which calls __hurd_intr_rpc_% inside
-# HURD_EINTR_RPC.
-define transform-user-stub
-gawk -v call=$${call} -f $(word 2,$^) \
- $(objpfx)tmp_$${call}.c > $(objpfx)tmpi_$${call}.c; \
-rm -f $(objpfx)tmp_$${call}.c;
-endef
-transform-user-stub-output = tmpi
-
$(foreach if,$(user-interfaces),$($(if)-calls:%=$(objpfx)RPC_%.o))): \
hurd/signal.h
-$(user-interfaces:%=$(objpfx)%.ustamp): intr-rpc.awk
-
$(objpfx)fault%.c $(objpfx)fault%.h: $(mach-srcdir)/mach/%.defs
$(MIG) $(MIGFLAGS) -prefix _hurdsig_fault_ \
-server $(@:.h=.c) -sheader $(@:.c=.h) \
diff --git a/hurd/hurd.h b/hurd/hurd.h
index e0ba62eede..1b4db17efe 100644
--- a/hurd/hurd.h
+++ b/hurd/hurd.h
@@ -286,6 +286,16 @@ extern task_t __pid2task (pid_t pid), pid2task (pid_t pid);
extern thread_t hurd_thread_self (void);
+/* Cancel pending operations on THREAD. If it is doing an interruptible RPC,
+ that RPC will now return EINTR; otherwise, the "cancelled" flag will be
+ set, causing the next `hurd_check_cancel' call to return nonzero or the
+ next interruptible RPC to return EINTR (whichever is called first). */
+extern error_t hurd_thread_cancel (thread_t thread);
+
+/* Test and clear the calling thread's "cancelled" flag. */
+extern int hurd_check_cancel (void);
+
+
/* Return the io server port for file descriptor FD.
This adds a Mach user reference to the returned port.
On error, sets `errno' and returns MACH_PORT_NULL. */
diff --git a/hurd/hurd/signal.h b/hurd/hurd/signal.h
index d28d1c6c11..a032313e4e 100644
--- a/hurd/hurd/signal.h
+++ b/hurd/hurd/signal.h
@@ -84,6 +84,8 @@ struct hurd_sigstate
<hurd/userlink.h> for details. This member is only used by the
thread itself, and always inside a critical section. */
struct hurd_userlink *active_resources;
+
+ volatile int cancel; /* Flag set by hurd_thread_cancel. */
};
/* Linked list of states of all threads whose state has been asked for. */
@@ -253,20 +255,14 @@ _hurd_setup_sighandler (struct hurd_sigstate *ss, __sighandler_t handler,
extern void _hurd_msgport_receive (void);
-/* STATE describes a thread that had intr_port set (meaning it was inside
- HURD_EINTR_RPC), after it has been thread_abort'd. It it looks to have
- just completed a mach_msg_trap system call that returned
- MACH_RCV_INTERRUPTED, return nonzero and set *PORT to the receive right
- being waited on. */
-
-extern int _hurdsig_rcv_interrupted_p (struct machine_thread_all_state *state,
- mach_port_t *port);
-
/* Set up STATE with a thread state that, when resumed, is
like `longjmp (_hurd_sigthread_fault_env, 1)'. */
extern void _hurd_initialize_fault_recovery_state (void *state);
+/* Set up STATE to do the equivalent of `longjmp (ENV, VAL);'. */
+
+extern void _hurd_longjmp_thread_state (void *state, jmp_buf env, int value);
/* Function run for SIGINFO when its action is SIG_DFL and the current
process is the session leader. */
@@ -274,50 +270,10 @@ extern void _hurd_initialize_fault_recovery_state (void *state);
extern void _hurd_siginfo_handler (int);
-/* Perform interruptible RPC CALL on PORT.
- The call should use
- The args in CALL should be constant or local variable refs.
- They may be evaluated many times, and must not change.
- PORT must not be deallocated before this RPC is finished. */
-#define HURD_EINTR_RPC(port, call) \
- ({ \
- __label__ __do_call; /* Give this label block scope. */ \
- error_t __err; \
- struct hurd_sigstate *__ss = _hurd_self_sigstate (); \
- __do_call: \
- /* Tell the signal thread that we are doing an interruptible RPC on \
- this port. If we get a signal and should return EINTR, the signal \
- thread will set this variable to MACH_PORT_NULL. The RPC might \
- return EINTR when some other thread gets a signal, in which case we \
- want to restart our call. */ \
- __ss->intr_port = (port); \
- /* A signal may arrive here, after intr_port is set, but before the \
- mach_msg system call. The signal handler might do an interruptible \
- RPC, and clobber intr_port; then it would not be set properly when \
- we actually did send the RPC, and a later signal wouldn't interrupt \
- that RPC. So, _hurd_setup_sighandler saves intr_port in the \
- sigcontext, and sigreturn restores it. */ \
- switch (__err = (call)) \
- { \
- case EINTR: /* RPC went out and was interrupted. */ \
- case MACH_SEND_INTERRUPTED: /* RPC didn't get out. */ \
- if (__ss->intr_port != MACH_PORT_NULL) \
- /* If this signal was for us and it should interrupt calls, the \
- signal thread will have cleared SS->intr_port. Since it's not \
- cleared, the signal was for another thread, or SA_RESTART is \
- set. Restart the interrupted call. */ \
- goto __do_call; \
- /* FALLTHROUGH */ \
- case MACH_RCV_PORT_DIED: \
- /* Server didn't respond to interrupt_operation, \
- so the signal thread destroyed the reply port. */ \
- __err = EINTR; \
- break; \
- default: /* Quiet -Wswitch-enum. */ \
- } \
- __ss->intr_port = MACH_PORT_NULL; \
- __err; \
- }) \
+/* Milliseconds to wait for an interruptible RPC to return after
+ `interrupt_operation'. */
+
+extern mach_msg_timeout_t _hurd_interrupted_rpc_timeout;
/* Mask of signals that cannot be caught, blocked, or ignored. */
diff --git a/hurd/hurdsig.c b/hurd/hurdsig.c
index 9414c059e3..7dd2b8c2fc 100644
--- a/hurd/hurdsig.c
+++ b/hurd/hurdsig.c
@@ -41,6 +41,9 @@ thread_t _hurd_sigthread;
/* Linked-list of per-thread signal state. */
struct hurd_sigstate *_hurd_sigstates;
+
+/* Timeout for RPC's after interrupt_operation. */
+mach_msg_timeout_t _hurd_interrupted_rpc_timeout = 3000;
static void
default_sigaction (struct sigaction actions[NSIG])
@@ -76,9 +79,6 @@ _hurd_thread_sigstate (thread_t thread)
__sigemptyset (&ss->pending);
memset (&ss->sigaltstack, 0, sizeof (ss->sigaltstack));
ss->suspended = 0;
-#ifdef noteven
- __condition_init (&ss->arrived);
-#endif
ss->intr_port = MACH_PORT_NULL;
ss->context = NULL;
@@ -225,16 +225,17 @@ abort_thread (struct hurd_sigstate *ss, struct machine_thread_all_state *state,
}
/* Find the location of the MiG reply port cell in use by the thread whose
- state is described by THREAD_STATE. Make sure that this location can be
- set without faulting, or else return NULL. */
+ state is described by THREAD_STATE. If SIGTHREAD is nonzero, make sure
+ that this location can be set without faulting, or else return NULL. */
static mach_port_t *
-interrupted_reply_port_location (struct machine_thread_all_state *thread_state)
+interrupted_reply_port_location (struct machine_thread_all_state *thread_state,
+ int sigthread)
{
mach_port_t *portloc = (mach_port_t *) __hurd_threadvar_location_from_sp
(_HURD_THREADVAR_MIG_REPLY, (void *) thread_state->basic.SP);
- if (_hurdsig_catch_fault (SIGSEGV))
+ if (sigthread && _hurdsig_catch_fault (SIGSEGV))
{
assert (_hurdsig_fault_sigcode == (long int) portloc);
/* Faulted trying to read the stack. */
@@ -244,7 +245,8 @@ interrupted_reply_port_location (struct machine_thread_all_state *thread_state)
/* Fault now if this pointer is bogus. */
*(volatile mach_port_t *) portloc = *portloc;
- _hurdsig_end_catch_fault ();
+ if (sigthread)
+ _hurdsig_end_catch_fault ();
return portloc;
}
@@ -266,12 +268,14 @@ interrupted_reply_port_location (struct machine_thread_all_state *thread_state)
be applied back to the thread if it might ever run again, else zero. */
static mach_port_t
-abort_rpcs (struct hurd_sigstate *ss, int signo,
- struct machine_thread_all_state *state, int *state_change,
- mach_port_t *reply_port, mach_msg_type_name_t reply_port_type,
- int untraced)
+_hurdsig_abort_rpcs (struct hurd_sigstate *ss, int signo, int sigthread,
+ struct machine_thread_all_state *state, int *state_change,
+ mach_port_t *reply_port,
+ mach_msg_type_name_t reply_port_type,
+ int untraced)
{
- mach_port_t msging_port;
+ extern const void _hurd_intr_rpc_msg_do_trap, _hurd_intr_rpc_msg_in_trap;
+ mach_port_t rcv_port = MACH_PORT_NULL;
mach_port_t intr_port;
*state_change = 0;
@@ -285,71 +289,69 @@ abort_rpcs (struct hurd_sigstate *ss, int signo,
receive completes immediately or aborts. */
abort_thread (ss, state, reply_port, reply_port_type, untraced);
- if (_hurdsig_rcv_interrupted_p (state, &msging_port))
+ if (state->basic.PC < (natural_t) &_hurd_intr_rpc_msg_in_trap)
{
- error_t err;
-
- /* The RPC request message was sent and the thread was waiting for
- the reply message; now the message receive has been aborted, so
- the mach_msg_call will return MACH_RCV_INTERRUPTED. We must tell
- the server to interrupt the pending operation. The thread must
- wait for the reply message before running the signal handler (to
- guarantee that the operation has finished being interrupted), so
- our nonzero return tells the trampoline code to finish the message
- receive operation before running the handler. */
-
- err = __interrupt_operation (intr_port);
-
- if (err)
- {
- mach_port_t *reply;
-
- /* The interrupt didn't work.
- Destroy the receive right the thread is blocked on. */
- __mach_port_destroy (__mach_task_self (), msging_port);
-
- /* The system call return value register now contains
- MACH_RCV_INTERRUPTED; when mach_msg resumes, it will retry the
- call. Since we have just destroyed the receive right, the
- retry will fail with MACH_RCV_INVALID_NAME. Instead, just
- change the return value here to EINTR so mach_msg will not
- retry and the EINTR error code will propagate up. */
- state->basic.SYSRETURN = EINTR;
- *state_change = 1;
-
- /* If that was the thread's MiG reply port (which I think should
- always be the case), clear the reply port cell so it won't be
- reused. */
- reply = interrupted_reply_port_location (state);
- if (reply != NULL && *reply == msging_port)
- *reply = MACH_PORT_NULL;
- }
-
- /* All threads whose RPCs were interrupted by the interrupt_operation
- call above will retry their RPCs unless we clear SS->intr_port.
- So we clear it for the thread taking a signal when SA_RESTART is
- clear, so that its call returns EINTR. */
- if (!(ss->actions[signo].sa_flags & SA_RESTART))
- ss->intr_port = MACH_PORT_NULL;
-
- return err ? MACH_PORT_NULL : msging_port;
+ /* The thread is about to do the RPC, but hasn't yet entered
+ mach_msg. Mutate the thread's state so it knows not to try
+ the RPC. */
+ MACHINE_THREAD_STATE_SET_PC (&state->basic,
+ &_hurd_intr_rpc_msg_in_trap);
+ state->basic.SYSRETURN = MACH_SEND_INTERRUPTED;
+ *state_change = 1;
}
+ else if (state->basic.PC == (natural_t) &_hurd_intr_rpc_msg_in_trap &&
+ /* The thread was blocked in the system call. After thread_abort,
+ the return value register indicates what state the RPC was in
+ when interrupted. */
+ state->basic.SYSRETURN == MACH_RCV_INTERRUPTED)
+ {
+ /* The RPC request message was sent and the thread was waiting for
+ the reply message; now the message receive has been aborted, so
+ the mach_msg call will return MACH_RCV_INTERRUPTED. We must tell
+ the server to interrupt the pending operation. The thread must
+ wait for the reply message before running the signal handler (to
+ guarantee that the operation has finished being interrupted), so
+ our nonzero return tells the trampoline code to finish the message
+ receive operation before running the handler. */
+
+ mach_port_t *reply = interrupted_reply_port_location (state,
+ sigthread);
+ error_t err = __interrupt_operation (intr_port);
+
+ if (err)
+ {
+ if (reply)
+ {
+ /* The interrupt didn't work.
+ Destroy the receive right the thread is blocked on. */
+ __mach_port_destroy (__mach_task_self (), *reply);
+ *reply = MACH_PORT_NULL;
+ }
- /* One of the following is true:
-
- 1. The RPC has not yet been sent. The thread will start its operation
- after the signal has been handled.
-
- 2. The RPC has finished, but not yet cleared SS->intr_port.
- The thread will clear SS->intr_port after running the handler.
-
- 3. The RPC request message was being sent was aborted. The mach_msg
- system call will return MACH_SEND_INTERRUPTED, and HURD_EINTR_RPC will
- notice the interruption (either retrying the RPC or returning EINTR). */
+ /* The system call return value register now contains
+ MACH_RCV_INTERRUPTED; when mach_msg resumes, it will retry the
+ call. Since we have just destroyed the receive right, the
+ retry will fail with MACH_RCV_INVALID_NAME. Instead, just
+ change the return value here to EINTR so mach_msg will not
+ retry and the EINTR error code will propagate up. */
+ state->basic.SYSRETURN = EINTR;
+ *state_change = 1;
+ }
+ else if (reply)
+ rcv_port = *reply;
+
+ /* All threads whose RPCs were interrupted by the interrupt_operation
+ call above will retry their RPCs unless we clear SS->intr_port.
+ So we clear it for the thread taking a signal when SA_RESTART is
+ clear, so that its call returns EINTR. */
+ if (! signo || !(ss->actions[signo].sa_flags & SA_RESTART))
+ ss->intr_port = MACH_PORT_NULL;
+ }
- return MACH_PORT_NULL;
+ return rcv_port;
}
+
/* Abort the RPCs being run by all threads but this one;
all other threads should be suspended. If LIVE is nonzero, those
threads may run again, so they should be adjusted as necessary to be
@@ -387,8 +389,9 @@ abort_all_rpcs (int signo, struct machine_thread_all_state *state, int live)
/* Abort any operation in progress with interrupt_operation.
Record the reply port the thread is waiting on.
We will wait for all the replies below. */
- reply_ports[nthreads++] = abort_rpcs (ss, signo, state, &state_changed,
- NULL, 0, 0);
+ reply_ports[nthreads++] = _hurdsig_abort_rpcs (ss, signo, 1,
+ state, &state_changed,
+ NULL, 0, 0);
if (state_changed && live)
/* Aborting the RPC needed to change this thread's state,
and it might ever run again. So write back its state. */
@@ -403,11 +406,18 @@ abort_all_rpcs (int signo, struct machine_thread_all_state *state, int live)
{
error_t err;
mach_msg_header_t head;
- err = __mach_msg (&head, MACH_RCV_MSG, 0, sizeof head,
+ err = __mach_msg (&head, MACH_RCV_MSG|MACH_RCV_TIMEOUT, 0, sizeof head,
reply_ports[nthreads],
- MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
- if (err != MACH_RCV_TOO_LARGE)
- assert_perror (err);
+ _hurd_interrupted_rpc_timeout, MACH_PORT_NULL);
+ switch (err)
+ {
+ case MACH_RCV_TIMED_OUT:
+ case MACH_RCV_TOO_LARGE:
+ break;
+
+ default:
+ assert_perror (err);
+ }
}
}
@@ -745,7 +755,7 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss,
if (! machine_get_basic_state (ss->thread, &thread_state))
goto sigbomb;
- loc = interrupted_reply_port_location (&thread_state);
+ loc = interrupted_reply_port_location (&thread_state, 1);
if (loc && *loc != MACH_PORT_NULL)
/* This is the reply port for the context which called
sigreturn. Since we are abandoning that context entirely
@@ -759,11 +769,11 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss,
}
else
{
- wait_for_reply = (abort_rpcs (ss, signo,
- &thread_state, &state_changed,
- &reply_port, reply_port_type,
- untraced)
- != MACH_PORT_NULL);
+ wait_for_reply
+ = (_hurdsig_abort_rpcs (ss, signo, 1,
+ &thread_state, &state_changed,
+ &reply_port, reply_port_type, untraced)
+ != MACH_PORT_NULL);
if (ss->critical_section)
{
@@ -790,7 +800,8 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss,
{
/* Fetch the thread variable for the MiG reply port,
and set it to MACH_PORT_NULL. */
- mach_port_t *loc = interrupted_reply_port_location (&thread_state);
+ mach_port_t *loc = interrupted_reply_port_location (&thread_state,
+ 1);
if (loc)
{
scp->sc_reply_port = *loc;
diff --git a/hurd/intr-msg.c b/hurd/intr-msg.c
new file mode 100644
index 0000000000..cdcd8d51ea
--- /dev/null
+++ b/hurd/intr-msg.c
@@ -0,0 +1,151 @@
+/* Replacement for mach_msg used in interruptible Hurd RPCs.
+Copyright (C) 1995 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+#include <mach.h>
+#include <mach/mig_errors.h>
+#include <hurd/signal.h>
+
+error_t
+_hurd_intr_rpc_mach_msg (mach_msg_header_t *msg,
+ mach_msg_option_t option,
+ mach_msg_size_t send_size,
+ mach_msg_size_t rcv_size,
+ mach_port_t rcv_name,
+ mach_msg_timeout_t timeout,
+ mach_port_t notify)
+{
+ struct hurd_sigstate *ss = _hurd_self_sigstate ();
+ error_t err;
+
+ /* Tell the signal thread that we are doing an interruptible RPC on
+ this port. If we get a signal and should return EINTR, the signal
+ thread will set this variable to MACH_PORT_NULL. The RPC might
+ return EINTR when some other thread gets a signal, in which case we
+ want to restart our call. */
+ ss->intr_port = msg->msgh_remote_port;
+
+ /* A signal may arrive here, after intr_port is set, but before
+ the mach_msg system call. The signal handler might do an
+ interruptible RPC, and clobber intr_port; then it would not be
+ set properly when we actually did send the RPC, and a later
+ signal wouldn't interrupt that RPC. So,
+ _hurd_setup_sighandler saves intr_port in the sigcontext, and
+ sigreturn restores it. */
+
+ message:
+
+ if (ss->cancel)
+ {
+ err = EINTR;
+ ss->cancel = 0;
+ }
+ else
+ /* err = intr_msg_trap (msg, option, send_size,
+ rcv_size, rcv_name, timeout, notify);
+ */
+ asm (".globl _hurd_intr_rpc_msg_do_trap\n"
+ ".globl _hurd_intr_rpc_msg_in_trap\n"
+ " movl %%esp, %%ecx\n"
+ " leal %1, %%esp\n"
+ " movl $-25, %%eax\n"
+ "_hurd_intr_rpc_msg_do_trap: lcall $7, $0 # status in %0\n"
+ "_hurd_intr_rpc_msg_in_trap: movl %%ecx, %%esp"
+ : "=a" (err) : "m" ((&msg)[1]) : "%ecx");
+
+ switch (err)
+ {
+ case MACH_SEND_INTERRUPTED: /* RPC didn't get out. */
+ if (ss->intr_port != MACH_PORT_NULL)
+ /* If this signal was for us and it should interrupt calls, the
+ signal thread will have cleared SS->intr_port.
+ Since it's not cleared, the signal was for another thread,
+ or SA_RESTART is set. Restart the interrupted call. */
+ goto message;
+ /* FALLTHROUGH */
+
+ case MACH_RCV_PORT_DIED:
+ /* Server didn't respond to interrupt_operation,
+ so the signal thread destroyed the reply port. */
+ /* FALLTHROUGH */
+
+ case MACH_RCV_TIMED_OUT:
+ /* The operation was supposedly interrupted, but still has
+ not returned. Declare it interrupted. */
+
+ err = EINTR;
+
+ /* The EINTR return indicates cancellation, so clear the flag. */
+ ss->cancel = 0;
+ break;
+
+ case MACH_RCV_INTERRUPTED: /* RPC sent; no reply. */
+ option &= ~MACH_SEND_MSG; /* Don't send again. */
+ if (ss->intr_port == MACH_PORT_NULL)
+ {
+ /* This signal or cancellation was for us. We need to wait for
+ the reply, but not hang forever. */
+ option |= MACH_RCV_TIMEOUT;
+ timeout = _hurd_interrupted_rpc_timeout;
+ }
+ goto message; /* Retry the receive. */
+
+ case MACH_MSG_SUCCESS:
+ if (option & MACH_RCV_MSG)
+ {
+ /* We got a reply. Was it EINTR? */
+ mig_reply_header_t *const reply = (void *) msg;
+ const union
+ {
+ mach_msg_type_t t;
+ int i;
+ } check =
+ { t: {
+ MACH_MSG_TYPE_INTEGER_32,
+ 32,
+ 1,
+ TRUE,
+ FALSE,
+ FALSE,
+ 0
+ } };
+ if (msg->msgh_size == sizeof *reply &&
+ !(msg->msgh_bits & MACH_MSGH_BITS_COMPLEX) &&
+ *(int *) &reply->RetCodeType == check.i &&
+ reply->RetCode == EINTR)
+ {
+ /* It is indeed EINTR. Is the interrupt for us? */
+ if (ss->intr_port != MACH_PORT_NULL)
+ /* Nope; repeat the RPC.
+ XXX Resources moved? */
+ goto message;
+ else
+ /* The EINTR return indicates cancellation, so clear the
+ flag. */
+ ss->cancel = 0;
+ }
+ }
+ break;
+
+ default: /* Quiet -Wswitch-enum. */
+ }
+
+ ss->intr_port = MACH_PORT_NULL;
+
+ return err;
+}
diff --git a/hurd/intr-rpc.defs b/hurd/intr-rpc.defs
index a2e7b060c9..7ed12bee62 100644
--- a/hurd/intr-rpc.defs
+++ b/hurd/intr-rpc.defs
@@ -1,5 +1,5 @@
/* Special MiG definitions for interruptible RPC stubs.
-Copyright (C) 1994 Free Software Foundation, Inc.
+Copyright (C) 1994, 1995 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
@@ -17,11 +17,7 @@ License along with the GNU C Library; see the file COPYING.LIB. If
not, write to the Free Software Foundation, Inc., 675 Mass Ave,
Cambridge, MA 02139, USA. */
-/* Set the MiG options for an interruptible RPC interface.
- We rename each MiG-generated function to hurd_intr_rpc_CALL and
- give it the option to return on an interrupted message send. */
-
-#define INTR_INTERFACE \
-msgoption MACH_SEND_INTERRUPT;\
-userprefix hurd_intr_rpc_;
+/* Cause user stubs for interruptible RPCs to import a special header to
+ modify their behavior. */
+#define INTR_INTERFACE uimport "intr-rpc.h";
diff --git a/hurd/intr-rpc.h b/hurd/intr-rpc.h
new file mode 100644
index 0000000000..169d1c7430
--- /dev/null
+++ b/hurd/intr-rpc.h
@@ -0,0 +1,25 @@
+/* Special MiG definitions for interruptible RPC stubs.
+Copyright (C) 1995 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+/* This file is imported by the MiG-generated user stubs for interruptible
+ RPCs. We modify them to use our own function in place of mach_msg. */
+
+#include <hurd/signal.h>
+
+#define __mach_msg _hurd_intr_rpc_mach_msg
diff --git a/hurd/thread-cancel.c b/hurd/thread-cancel.c
new file mode 100644
index 0000000000..db527c3935
--- /dev/null
+++ b/hurd/thread-cancel.c
@@ -0,0 +1,86 @@
+/* Thread cancellation support.
+Copyright (C) 1995 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+#include <hurd/signal.h>
+#include <hurd/interrupt.h>
+#include <assert.h>
+#include <thread_state.h>
+
+
+/* See hurdsig.c. */
+extern mach_port_t _hurdsig_abort_rpcs (struct hurd_sigstate *ss,
+ int signo, int sigthread,
+ struct machine_thread_all_state *,
+ int *state_change,
+ mach_port_t *reply_port,
+ mach_msg_type_name_t reply_port_type,
+ int untraced);
+
+error_t
+hurd_thread_cancel (thread_t thread)
+{
+ struct hurd_sigstate *ss = _hurd_thread_sigstate (thread);
+ struct machine_thread_all_state state;
+ int state_change;
+ error_t err;
+
+ if (! ss)
+ return EINVAL;
+
+ __spin_lock (&ss->lock);
+ assert (! ss->critical_section);
+ ss->critical_section = 1;
+ err = __thread_suspend (thread);
+ __spin_unlock (&ss->lock);
+
+ if (! err)
+ {
+ /* Set the flag telling the thread its operation is being cancelled. */
+ ss->cancel = 1;
+
+ /* Interrupt any interruptible RPC now in progress. */
+ state.set = 0;
+ _hurdsig_abort_rpcs (ss, 0, 0, &state, &state_change, NULL, 0, 0);
+ if (state_change)
+ err = __thread_set_state (thread, MACHINE_THREAD_STATE_FLAVOR,
+ (natural_t *) &state.basic,
+ MACHINE_THREAD_STATE_COUNT);
+
+ __thread_resume (thread);
+ }
+
+ _hurd_critical_section_unlock (ss);
+ return err;
+}
+
+
+int
+hurd_check_cancel (void)
+{
+ struct hurd_sigstate *ss = _hurd_self_sigstate ();
+ int cancel;
+
+ __spin_lock (&ss->lock);
+ assert (! ss->critical_section);
+ cancel = ss->cancel;
+ ss->cancel = 0;
+ __spin_unlock (&ss->lock);
+
+ return cancel;
+}