summaryrefslogtreecommitdiff
path: root/libports/interrupt-on-notify.c
diff options
context:
space:
mode:
authorMiles Bader <miles@gnu.org>1995-12-27 21:46:29 +0000
committerMiles Bader <miles@gnu.org>1995-12-27 21:46:29 +0000
commitb815fe0a4b061cf4e83be6ec8a37adde2a9ccf4e (patch)
treee42747e18adbdea4ed70c6a21b9fa106d2e11cc7 /libports/interrupt-on-notify.c
parent8e58b40a959ef8b93c75cc7ddb8bc08abafd1a50 (diff)
Initial revision
Diffstat (limited to 'libports/interrupt-on-notify.c')
-rw-r--r--libports/interrupt-on-notify.c169
1 files changed, 169 insertions, 0 deletions
diff --git a/libports/interrupt-on-notify.c b/libports/interrupt-on-notify.c
new file mode 100644
index 00000000..79bd140c
--- /dev/null
+++ b/libports/interrupt-on-notify.c
@@ -0,0 +1,169 @@
+/* Mark an rpc to be interrupted when a port dies
+
+ Copyright (C) 1995 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ports.h"
+
+/* Arrange for hurd_cancel to be called on RPC's thread if OBJECT gets notified
+ that any of the things in COND have happened to PORT. RPC should be an
+ rpc on OBJECT. */
+error_t
+ports_interrupt_rpc_on_notification (void *object,
+ struct rpc_info *rpc,
+ mach_port_t port, mach_msg_id_t what)
+{
+ int req_notify;
+ struct ports_notify *pn;
+ struct rpc_notify *new_req, *req;
+ struct port_info *pi = object;
+
+ mutex_lock (&_ports_lock);
+
+ new_req = _ports_free_rpc_notifies;
+ if (new_req)
+ _ports_free_rpc_notifies = new_req->next;
+ else
+ /* No free notify structs, allocate one; it's expected that 99% of the
+ time we'll add a new structure, so we malloc while we don't have the
+ lock, and free it if we're wrong. */
+ {
+ mutex_unlock (&_ports_lock); /* Don't hold the lock during malloc. */
+ new_req = malloc (sizeof (struct rpc_notify));
+ if (! new_req)
+ return ENOMEM;
+ mutex_lock (&_ports_lock);
+ }
+
+ /* Find any existing entry for PORT/WHAT. */
+ for (pn = _ports_notifications; pn; pn = pn->next)
+ if (pn->port == port && pn->what == what)
+ break;
+
+ if (! pn)
+ /* A notification on a new port. */
+ {
+ pn = _ports_free_ports_notifies;
+
+ if (pn)
+ _ports_free_ports_notifies = pn->next;
+ else
+ {
+ pn = malloc (sizeof (struct ports_notify));
+ if (! pn)
+ /* sigh. Free what we've alloced and return. */
+ {
+ new_req->next = _ports_free_rpc_notifies;
+ _ports_free_rpc_notifies = new_req;
+ mutex_unlock (&_ports_lock);
+ return ENOMEM;
+ }
+ }
+
+ pn->reqs = 0;
+ pn->port = port;
+ pn->what = what;
+ pn->pending = 0;
+ mutex_init (&pn->lock);
+
+ pn->next = _ports_notifications;
+ pn->prevp = &_ports_notifications;
+ _ports_notifications->prevp = &pn->next;
+ _ports_notifications = pn;
+ }
+
+ for (req = rpc->notifies; req; req = req->next)
+ if (req->notify == pn)
+ break;
+
+ if (req)
+ free (new_req);
+ else
+ {
+ req = new_req;
+
+ req->rpc = rpc;
+ req->notify = pn;
+ req->pending = 0;
+
+ req->next_req = pn->reqs;
+ req->prev_req_p = &pn->reqs;
+ pn->reqs->prev_req_p = &req->next_req;
+ pn->reqs = req;
+
+ req->next = rpc->notifies;
+ rpc->notifies = req;
+ }
+
+ /* Make sure that this request results in an interrupt. */
+ req->pending++;
+
+ /* Find out whether we should request a new notification (after we release
+ _PORTS_LOCK) -- PN may be new, or left over after a previous
+ notification (in which case our new request is likely to trigger an
+ immediate notification). */
+ req_notify = !pn->pending;
+ if (req_notify)
+ mutex_lock (&pn->lock);
+
+ mutex_unlock (&_ports_lock);
+
+ if (req_notify)
+ {
+ mach_port_t old;
+ error_t err =
+ mach_port_request_notification (mach_task_self (), port,
+ what, 1, pi->port_right,
+ MACH_MSG_TYPE_MAKE_SEND_ONCE, &old);
+
+ if (! err && old != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), old);
+
+ pn->pending = 1;
+ mutex_unlock (&pn->lock);
+
+ return err;
+ }
+ else
+ return 0;
+}
+
+/* Arrange for hurd_cancel to be called on the current thread, which should
+ be an rpc on OBJECT, if PORT gets notified with the condition WHAT. */
+error_t
+ports_interrupt_self_on_notification (void *object,
+ mach_port_t port, mach_msg_id_t what)
+{
+ struct rpc_info *rpc;
+ struct port_info *pi = object;
+ thread_t thread = hurd_thread_self ();
+
+ mutex_lock (&_ports_lock);
+ for (rpc = pi->current_rpcs; rpc; rpc = rpc->next)
+ if (rpc->thread == thread)
+ break;
+ mutex_unlock (&_ports_lock);
+
+ if (rpc)
+ /* We don't have to worry about RPC going away after we dropped the lock
+ because we're that thread, and we're still here. */
+ return ports_interrupt_rpc_on_notification (object, rpc, port, what);
+ else
+ /* This thread isn't in an rpc! */
+ return EIEIO;
+}