Update.
[kopensolaris-gnu/glibc.git] / hurd / intr-msg.c
1 /* Replacement for mach_msg used in interruptible Hurd RPCs.
2    Copyright (C) 95, 96, 97, 98 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 not,
17    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18    Boston, MA 02111-1307, 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   error_t err;
38   struct hurd_sigstate *ss;
39   int user_timeout;
40
41   if (_hurd_msgport_thread == MACH_PORT_NULL)
42     {
43       /* The signal thread is not set up yet, so we cannot do the
44          normal signal magic.  Do a simple uninterruptible RPC instead.  */
45       return __mach_msg (msg, option, send_size, rcv_size, rcv_name,
46                          timeout, notify);
47     }
48
49   ss = _hurd_self_sigstate ();
50
51   /* Notice now if the user requested a timeout.  OPTION may have the bit
52      added by interruption semantics, and we must distinguish.  */
53   user_timeout = option & MACH_RCV_TIMEOUT;
54
55   /* Tell the signal thread that we are doing an interruptible RPC on
56      this port.  If we get a signal and should return EINTR, the signal
57      thread will set this variable to MACH_PORT_NULL.  The RPC might
58      return EINTR when some other thread gets a signal, in which case we
59      want to restart our call.  */
60   ss->intr_port = msg->msgh_remote_port;
61
62   /* A signal may arrive here, after intr_port is set, but before
63      the mach_msg system call.  The signal handler might do an
64      interruptible RPC, and clobber intr_port; then it would not be
65      set properly when we actually did send the RPC, and a later
66      signal wouldn't interrupt that RPC.  So,
67      _hurd_setup_sighandler saves intr_port in the sigcontext, and
68      sigreturn restores it.  */
69
70  message:
71
72   if (ss->cancel)
73     {
74       /* We have been cancelled.  Don't do an RPC at all.  */
75       ss->intr_port = MACH_PORT_NULL;
76       ss->cancel = 0;
77       return EINTR;
78     }
79
80   err = INTR_MSG_TRAP (msg, option, send_size,
81                        rcv_size, rcv_name, timeout, notify);
82
83   switch (err)
84     {
85     case MACH_RCV_TIMED_OUT:
86       if (user_timeout)
87         /* The real user RPC timed out.  */
88         break;
89       else
90         /* The operation was supposedly interrupted, but still has
91            not returned.  Declare it interrupted.  */
92         goto interrupted;
93
94     case MACH_SEND_INTERRUPTED: /* RPC didn't get out.  */
95     case EINTR:                 /* Server not cooperating with interrupt.  */
96       if (ss->intr_port != MACH_PORT_NULL)
97         /* If this signal was for us and it should interrupt calls, the
98            signal thread will have cleared SS->intr_port.
99            Since it's not cleared, the signal was for another thread,
100            or SA_RESTART is set.  Restart the interrupted call.  */
101         {
102         restart:
103           if (rcv_name != MACH_PORT_NULL)
104             /* Make sure we have a valid reply port.  The one we were using
105                may have been destroyed by interruption.  */
106             msg->msgh_local_port = rcv_name = __mig_get_reply_port ();
107           goto message;
108         }
109       /* FALLTHROUGH */
110
111     case MACH_RCV_PORT_DIED:
112       /* Server didn't respond to interrupt_operation,
113          so the signal thread destroyed the reply port.  */
114       /* FALLTHROUGH */
115
116     interrupted:
117       err = EINTR;
118
119       /* The EINTR return indicates cancellation, so clear the flag.  */
120       ss->cancel = 0;
121       break;
122
123     case MACH_RCV_INTERRUPTED:  /* RPC sent; no reply.  */
124       option &= ~MACH_SEND_MSG; /* Don't send again.  */
125       if (ss->intr_port == MACH_PORT_NULL)
126         {
127           /* This signal or cancellation was for us.  We need to wait for
128              the reply, but not hang forever.  */
129           option |= MACH_RCV_TIMEOUT;
130           /* Never decrease the user's timeout.  */
131           if (!user_timeout || timeout > _hurd_interrupted_rpc_timeout)
132             timeout = _hurd_interrupted_rpc_timeout;
133         }
134       goto message;             /* Retry the receive.  */
135
136     case MACH_MSG_SUCCESS:
137       if (option & MACH_RCV_MSG)
138         {
139           /* We got a reply.  Was it EINTR?  */
140           mig_reply_header_t *const reply = (void *) msg;
141           const union
142             {
143               mach_msg_type_t t;
144               int i;
145             } check =
146               { t: {
147                 MACH_MSG_TYPE_INTEGER_T,
148                 MACH_MSG_TYPE_INTEGER_T,
149                 1,
150                 TRUE,
151                 FALSE,
152                 FALSE,
153                 0
154               } };
155           if (msg->msgh_size == sizeof *reply &&
156               !(msg->msgh_bits & MACH_MSGH_BITS_COMPLEX) &&
157               *(int *) &reply->RetCodeType == check.i &&
158               reply->RetCode == EINTR)
159             {
160               /* It is indeed EINTR.  Is the interrupt for us?  */
161               if (ss->intr_port != MACH_PORT_NULL)
162                 /* Nope; repeat the RPC.
163                    XXX Resources moved? */
164                 goto restart;
165               else
166                 /* The EINTR return indicates cancellation, so clear the
167                    flag.  */
168                 ss->cancel = 0;
169             }
170         }
171       break;
172
173     default:                    /* Quiet -Wswitch-enum.  */
174     }
175
176   ss->intr_port = MACH_PORT_NULL;
177
178   return err;
179 }