If the user passed the MACH_RCV_TIMEOUT option, distinguish
[kopensolaris-gnu/glibc.git] / hurd / intr-msg.c
1 /* Replacement for mach_msg used in interruptible Hurd RPCs.
2 Copyright (C) 1995 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 <hurd/signal.h>
23
24 #include "intr-msg.h"
25
26
27 error_t
28 _hurd_intr_rpc_mach_msg (mach_msg_header_t *msg,
29                          mach_msg_option_t option,
30                          mach_msg_size_t send_size,
31                          mach_msg_size_t rcv_size,
32                          mach_port_t rcv_name,
33                          mach_msg_timeout_t timeout,
34                          mach_port_t notify)
35 {
36   struct hurd_sigstate *ss = _hurd_self_sigstate ();
37   error_t err;
38   /* Notice now if the user requested a timeout.  OPTION may have the bit
39      added by interruption semantics, and we must distinguish.  */
40   int user_timeout = option & MACH_RCV_TIMEOUT;
41
42   /* Tell the signal thread that we are doing an interruptible RPC on
43      this port.  If we get a signal and should return EINTR, the signal
44      thread will set this variable to MACH_PORT_NULL.  The RPC might
45      return EINTR when some other thread gets a signal, in which case we
46      want to restart our call.  */
47   ss->intr_port = msg->msgh_remote_port;
48   
49   /* A signal may arrive here, after intr_port is set, but before
50      the mach_msg system call.  The signal handler might do an
51      interruptible RPC, and clobber intr_port; then it would not be
52      set properly when we actually did send the RPC, and a later
53      signal wouldn't interrupt that RPC.  So,
54      _hurd_setup_sighandler saves intr_port in the sigcontext, and
55      sigreturn restores it.  */
56
57  message:
58
59   if (ss->cancel)
60     {
61       err = EINTR;
62       ss->cancel = 0;
63     }
64   else 
65     err = INTR_MSG_TRAP (msg, option, send_size,
66                          rcv_size, rcv_name, timeout, notify);
67
68   switch (err)
69     {
70     case MACH_RCV_TIMED_OUT:
71       if (user_timeout)
72         /* The real user RPC timed out.  */
73         break;
74       else
75         /* The operation was supposedly interrupted, but still has
76            not returned.  Declare it interrupted.  */
77         goto interrupted;
78
79     case MACH_SEND_INTERRUPTED: /* RPC didn't get out.  */
80       if (ss->intr_port != MACH_PORT_NULL)
81         /* If this signal was for us and it should interrupt calls, the
82            signal thread will have cleared SS->intr_port.
83            Since it's not cleared, the signal was for another thread,
84            or SA_RESTART is set.  Restart the interrupted call.  */
85         goto message;
86       /* FALLTHROUGH */
87
88     case MACH_RCV_PORT_DIED:
89       /* Server didn't respond to interrupt_operation,
90          so the signal thread destroyed the reply port.  */
91       /* FALLTHROUGH */
92
93     interrupted:
94       err = EINTR;
95
96       /* The EINTR return indicates cancellation, so clear the flag.  */
97       ss->cancel = 0;
98       break;
99
100     case MACH_RCV_INTERRUPTED:  /* RPC sent; no reply.  */
101       option &= ~MACH_SEND_MSG; /* Don't send again.  */
102       if (ss->intr_port == MACH_PORT_NULL)
103         {
104           /* This signal or cancellation was for us.  We need to wait for
105              the reply, but not hang forever.  */
106           option |= MACH_RCV_TIMEOUT;
107           /* Never decrease the user's timeout.  */
108           if (!user_timeout || timeout > _hurd_interrupted_rpc_timeout)
109             timeout = _hurd_interrupted_rpc_timeout;
110         }
111       goto message;             /* Retry the receive.  */
112
113     case MACH_MSG_SUCCESS:
114       if (option & MACH_RCV_MSG)
115         {
116           /* We got a reply.  Was it EINTR?  */
117           mig_reply_header_t *const reply = (void *) msg;
118           const union
119             {
120               mach_msg_type_t t;
121               int i;
122             } check =
123               { t: {
124                 MACH_MSG_TYPE_INTEGER_32,
125                 32,
126                 1,
127                 TRUE,
128                 FALSE,
129                 FALSE,
130                 0
131               } };
132           if (msg->msgh_size == sizeof *reply &&
133               !(msg->msgh_bits & MACH_MSGH_BITS_COMPLEX) &&
134               *(int *) &reply->RetCodeType == check.i &&
135               reply->RetCode == EINTR)
136             {
137               /* It is indeed EINTR.  Is the interrupt for us?  */
138               if (ss->intr_port != MACH_PORT_NULL)
139                 /* Nope; repeat the RPC.
140                    XXX Resources moved? */
141                 goto message;
142               else
143                 /* The EINTR return indicates cancellation, so clear the
144                    flag.  */
145                 ss->cancel = 0;
146             }
147         }
148       break;
149
150     default:                    /* Quiet -Wswitch-enum.  */
151     }
152
153   ss->intr_port = MACH_PORT_NULL;
154
155   return err;
156 }