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