An interrupted RPC call can return EINTR whilst the RPC is still in
progress on the server. Some RPC calls have permanent consequences
(eg. a write() to append data to a file) but a caller seeing EINTR
should expect that no state has changed. The signal thread now stores
the server's reply (which it already waited for) as the interrupted
thread's reply.
---
hurd/hurdsig.c | 67 +++++++++++++++++++++++++++++---------------------
1 file changed, 39 insertions(+), 28 deletions(-)
diff --git a/hurd/hurdsig.c b/hurd/hurdsig.c
index cb3e04ec0d..79501bebc5 100644
--- a/hurd/hurdsig.c
+++ b/hurd/hurdsig.c
@@ -452,7 +452,7 @@ _hurdsig_abort_rpcs (struct hurd_sigstate *ss, int signo,
int sigthread,
/* The interrupt didn't work.
Destroy the receive right the thread is blocked on, and
replace it with a dead name to keep the name from reuse until
- the therad is done with it. To do this atomically, first
+ the thread is done with it. To do this atomically, first
insert a send right, and then destroy the receive right,
turning the send right into a dead name. */
err = __mach_port_insert_right (__mach_task_self (),
@@ -465,12 +465,15 @@ _hurdsig_abort_rpcs (struct hurd_sigstate *ss, int signo,
int sigthread,
}
/* 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;
+ MACH_RCV_INTERRUPTED; when mach_msg resumes, it will
+ retry the call. Since we have just destroyed the receive
+ right, the retry would fail with MACH_RCV_INVALID_NAME.
+ Instead, just change the return value here so mach_msg
+ will not retry. We cannot return EINTR because we do not
+ know what state the server is in and we cannot wait for
+ its reply. EIEIO instead indicates a fatal result for the
+ operation. */
+ state->basic.SYSRETURN = EIEIO;
*state_change = 1;
}
else if (reply)
@@ -479,7 +482,8 @@ _hurdsig_abort_rpcs (struct hurd_sigstate *ss, int signo,
int sigthread,
/* 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. */
+ that its call returns the server's response to the RPC (which includes
+ the possibility of EINTR). */
if (! signo || !(_hurd_sigstate_actions (ss) [signo].sa_flags &
SA_RESTART))
ss->intr_port = MACH_PORT_NULL;
}
@@ -528,17 +532,8 @@ abort_all_rpcs (int signo, struct machine_thread_all_state
*state, int live)
reply_ports[nthreads] = _hurdsig_abort_rpcs (ss, signo, 1,
state, &state_changed,
NULL);
- if (live)
+ if (live && state_changed)
{
- if (reply_ports[nthreads] != MACH_PORT_NULL)
- {
- /* We will wait for the reply to this RPC below, so the
- thread must issue a new RPC rather than waiting for the
- reply to the one it sent. */
- state->basic.SYSRETURN = EINTR;
- state_changed = 1;
- }
- if (state_changed)
/* Aborting the RPC needed to change this thread's state,
and it might ever run again. So write back its state. */
__thread_set_state (ss->thread, MACHINE_THREAD_STATE_FLAVOR,
@@ -547,24 +542,40 @@ abort_all_rpcs (int signo, struct
machine_thread_all_state *state, int live)
}
}
+ /* All threads (except this one) are suspended, so _hurd_sigstates
+ cannot have changed and therefore indexes into reply_ports still
+ match the _hurd_sigstates sequence. */
+ nthreads = 0;
+
/* Wait for replies from all the successfully interrupted RPCs. */
- while (nthreads-- > 0)
+ for (ss = _hurd_sigstates; ss != NULL; ss = ss->next, nthreads += 1)
if (reply_ports[nthreads] != MACH_PORT_NULL)
{
+ machine_get_basic_state (ss->thread, state);
+
+ /* We use the message header/rcv_size supplied to the thread
+ that initiated the RPC so that the reply is available to it
+ if it resumes. */
error_t err;
- mach_msg_header_t head;
- err = __mach_msg (&head, MACH_RCV_MSG|MACH_RCV_TIMEOUT, 0, sizeof head,
+ mach_msg_header_t *head = (mach_msg_header_t *) state->basic.rdi;
+ mach_msg_size_t rcv_size = (mach_msg_size_t) state->basic.r10;
+
+ assert (head != NULL && rcv_size >= sizeof(mach_msg_header_t));
+
+ err = __mach_msg (head, MACH_RCV_MSG|MACH_RCV_TIMEOUT, 0, rcv_size,
reply_ports[nthreads],
_hurd_interrupted_rpc_timeout, MACH_PORT_NULL);
- switch (err)
+ if (live)
{
- case MACH_RCV_TIMED_OUT:
- case MACH_RCV_TOO_LARGE:
- break;
-
- default:
- assert_perror (err);
+ /* The RPC might have any result including success. The
+ suspended thread must deal with the outcome. */
+ state->basic.SYSRETURN = err;
+ __thread_set_state (ss->thread, MACHINE_THREAD_STATE_FLAVOR,
+ (natural_t *) &state->basic,
+ MACHINE_THREAD_STATE_COUNT);
}
+ else if (err != MACH_RCV_TIMED_OUT)
+ assert_perror (err);
}
}
--
2.47.3