When restarting RPC, fetch a new reply port.
[kopensolaris-gnu/glibc.git] / hurd / intr-msg.c
1 /* Replacement for mach_msg used in interruptible Hurd RPCs.
2 Copyright (C) 1995, 1996 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public License as
7 published by the Free Software Foundation; either version 2 of the
8 License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public
16 License along with the GNU C Library; see the file COPYING.LIB.  If
17 not, write to the Free Software Foundation, Inc., 675 Mass Ave,
18 Cambridge, MA 02139, USA.  */
19
20 #include <mach.h>
21 #include <mach/mig_errors.h>
22 #include <mach/mig_support.h>
23 #include <hurd/signal.h>
24
25 #include "intr-msg.h"
26
27
28 error_t
29 _hurd_intr_rpc_mach_msg (mach_msg_header_t *msg,
30                          mach_msg_option_t option,
31                          mach_msg_size_t send_size,
32                          mach_msg_size_t rcv_size,
33                          mach_port_t rcv_name,
34                          mach_msg_timeout_t timeout,
35                          mach_port_t notify)
36 {
37   struct hurd_sigstate *ss = _hurd_self_sigstate ();
38   error_t err;
39   /* Notice now if the user requested a timeout.  OPTION may have the bit
40      added by interruption semantics, and we must distinguish.  */
41   int user_timeout = option & MACH_RCV_TIMEOUT;
42
43   /* Tell the signal thread that we are doing an interruptible RPC on
44      this port.  If we get a signal and should return EINTR, the signal
45      thread will set this variable to MACH_PORT_NULL.  The RPC might
46      return EINTR when some other thread gets a signal, in which case we
47      want to restart our call.  */
48   ss->intr_port = msg->msgh_remote_port;
49
50   /* A signal may arrive here, after intr_port is set, but before
51      the mach_msg system call.  The signal handler might do an
52      interruptible RPC, and clobber intr_port; then it would not be
53      set properly when we actually did send the RPC, and a later
54      signal wouldn't interrupt that RPC.  So,
55      _hurd_setup_sighandler saves intr_port in the sigcontext, and
56      sigreturn restores it.  */
57
58  message:
59
60   if (ss->cancel)
61     {
62       ss->cancel = 0;
63       return EINTR;
64     }
65
66   err = INTR_MSG_TRAP (msg, option, send_size,
67                        rcv_size, rcv_name, timeout, notify);
68
69   switch (err)
70     {
71     case MACH_RCV_TIMED_OUT:
72       if (user_timeout)
73         /* The real user RPC timed out.  */
74         break;
75       else
76         /* The operation was supposedly interrupted, but still has
77            not returned.  Declare it interrupted.  */
78         goto interrupted;
79
80     case MACH_SEND_INTERRUPTED: /* RPC didn't get out.  */
81     case EINTR:                 /* Server not cooperating with interrupt.  */
82       if (ss->intr_port != MACH_PORT_NULL)
83         /* If this signal was for us and it should interrupt calls, the
84            signal thread will have cleared SS->intr_port.
85            Since it's not cleared, the signal was for another thread,
86            or SA_RESTART is set.  Restart the interrupted call.  */
87         {
88         restart:
89           if (rcv_name != MACH_PORT_NULL)
90             /* Make sure we have a valid reply port.  The one we were using
91                may have been destroyed by interruption.  */
92             msg->msgh_local_port = rcv_name = __mig_get_reply_port ();
93           goto message;
94         }
95       /* FALLTHROUGH */
96
97     case MACH_RCV_PORT_DIED:
98       /* Server didn't respond to interrupt_operation,
99          so the signal thread destroyed the reply port.  */
100       /* FALLTHROUGH */
101
102     interrupted:
103       err = EINTR;
104
105       /* The EINTR return indicates cancellation, so clear the flag.  */
106       ss->cancel = 0;
107       break;
108
109     case MACH_RCV_INTERRUPTED:  /* RPC sent; no reply.  */
110       option &= ~MACH_SEND_MSG; /* Don't send again.  */
111       if (ss->intr_port == MACH_PORT_NULL)
112         {
113           /* This signal or cancellation was for us.  We need to wait for
114              the reply, but not hang forever.  */
115           option |= MACH_RCV_TIMEOUT;
116           /* Never decrease the user's timeout.  */
117           if (!user_timeout || timeout > _hurd_interrupted_rpc_timeout)
118             timeout = _hurd_interrupted_rpc_timeout;
119         }
120       goto message;             /* Retry the receive.  */
121
122     case MACH_MSG_SUCCESS:
123       if (option & MACH_RCV_MSG)
124         {
125           /* We got a reply.  Was it EINTR?  */
126           mig_reply_header_t *const reply = (void *) msg;
127           const union
128             {
129               mach_msg_type_t t;
130               int i;
131             } check =
132               { t: {
133                 MACH_MSG_TYPE_INTEGER_32,
134                 32,
135                 1,
136                 TRUE,
137                 FALSE,
138                 FALSE,
139                 0
140               } };
141           if (msg->msgh_size == sizeof *reply &&
142               !(msg->msgh_bits & MACH_MSGH_BITS_COMPLEX) &&
143               *(int *) &reply->RetCodeType == check.i &&
144               reply->RetCode == EINTR)
145             {
146               /* It is indeed EINTR.  Is the interrupt for us?  */
147               if (ss->intr_port != MACH_PORT_NULL)
148                 /* Nope; repeat the RPC.
149                    XXX Resources moved? */
150                 goto restart;
151               else
152                 /* The EINTR return indicates cancellation, so clear the
153                    flag.  */
154                 ss->cancel = 0;
155             }
156         }
157       break;
158
159     default:                    /* Quiet -Wswitch-enum.  */
160     }
161
162   ss->intr_port = MACH_PORT_NULL;
163
164   return err;
165 }