summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libports/dead-name.c28
-rw-r--r--libports/interrupt-notified-rpcs.c114
-rw-r--r--libports/interrupt-on-notify.c169
3 files changed, 311 insertions, 0 deletions
diff --git a/libports/dead-name.c b/libports/dead-name.c
new file mode 100644
index 00000000..07705563
--- /dev/null
+++ b/libports/dead-name.c
@@ -0,0 +1,28 @@
+/* Handle various ports internal uses of dead-name notification
+
+ 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"
+#include <mach/notify.h>
+
+void
+ports_dead_name (void *notify, mach_port_t dead_name)
+{
+ ports_interrupt_notified_rpcs (notify, dead_name, MACH_NOTIFY_DEAD_NAME);
+}
diff --git a/libports/interrupt-notified-rpcs.c b/libports/interrupt-notified-rpcs.c
new file mode 100644
index 00000000..a88e398a
--- /dev/null
+++ b/libports/interrupt-notified-rpcs.c
@@ -0,0 +1,114 @@
+/* Handle interruping rpcs because of notification
+
+ 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"
+
+/* A linked list of ports for which notification has been requested. */
+struct ports_notify *_ports_notifications = 0;
+
+/* Free lists for notify structures. */
+struct ports_notify *_ports_free_ports_notifies = 0;
+struct rpc_notify *_ports_free_rpc_notifies = 0;
+
+/* Interrupt any rpcs on OBJECT that have requested such. */
+void
+ports_interrupt_notified_rpcs (void *object,
+ mach_port_t port, mach_msg_id_t what)
+{
+ struct ports_notify *np;
+
+ mutex_lock (&_ports_lock);
+ for (np = _ports_notifications; np; np = np->next)
+ if (np->port == port && np->what == what)
+ {
+ struct rpc_notify *req;
+ for (req = np->reqs; req; req = req->next_req)
+ if (req->pending)
+ {
+ req->pending--;
+ hurd_thread_cancel (req->rpc->thread);
+ }
+ break;
+ }
+ mutex_unlock (&_ports_lock);
+}
+
+static void
+remove_req (struct rpc_notify *req)
+{
+ struct ports_notify *np = req->notify;
+
+ /* Take REQ out of the list of notified rpcs. */
+ if (req->next_req)
+ req->next_req->prev_req_p = req->prev_req_p;
+ *req->prev_req_p = req->next_req;
+
+ if (np->reqs == 0)
+ /* Now NP has no more reqests, so we can free it too. */
+ {
+ /* Take NP out of the active list... */
+ if (np->next)
+ np->next->prevp = np->prevp;
+ *np->prevp = np->next;
+
+ /* And put it on the free list. */
+ np->next = _ports_free_ports_notifies;
+ _ports_free_ports_notifies = np;
+
+ if (np->pending)
+ /* And cancel the associated notification. */
+ {
+ mach_port_t old;
+ error_t err =
+ mach_port_request_notification (mach_task_self (), np->port,
+ np->what, 0, MACH_PORT_NULL,
+ MACH_MSG_TYPE_MAKE_SEND_ONCE,
+ &old);
+ if (! err && old != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), old);
+ }
+ }
+}
+
+/* Remove RPC from the list of notified rpcs, cancelling any pending
+ notifications. _PORTS_LOCK should be held. */
+void
+_ports_remove_notified_rpc (struct rpc_info *rpc)
+{
+ struct rpc_notify *req = rpc->notifies;
+
+ if (req)
+ /* Cancel RPC's notify requests. */
+ {
+ struct rpc_notify *last = req;
+
+ while (last->next)
+ {
+ remove_req (last);
+ last = last->next;
+ }
+ remove_req (last);
+
+ /* Put the whole chain on the free list. */
+ last->next = _ports_free_rpc_notifies;
+ _ports_free_rpc_notifies = req;
+ }
+}
+
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;
+}