diff options
Diffstat (limited to 'libports')
-rw-r--r-- | libports/dead-name.c | 28 | ||||
-rw-r--r-- | libports/interrupt-notified-rpcs.c | 114 | ||||
-rw-r--r-- | libports/interrupt-on-notify.c | 169 |
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; +} |