summaryrefslogtreecommitdiff
path: root/ipc/ipc_pset.c
diff options
context:
space:
mode:
Diffstat (limited to 'ipc/ipc_pset.c')
-rw-r--r--ipc/ipc_pset.c349
1 files changed, 349 insertions, 0 deletions
diff --git a/ipc/ipc_pset.c b/ipc/ipc_pset.c
new file mode 100644
index 0000000..57705d6
--- /dev/null
+++ b/ipc/ipc_pset.c
@@ -0,0 +1,349 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989Carnegie Mellon University.
+ * Copyright (c) 1993,1994 The University of Utah and
+ * the Computer Systems Laboratory (CSL).
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON, THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF
+ * THIS SOFTWARE IN ITS "AS IS" CONDITION, AND DISCLAIM ANY LIABILITY
+ * OF ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF
+ * THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ */
+/*
+ * File: ipc/ipc_pset.c
+ * Author: Rich Draves
+ * Date: 1989
+ *
+ * Functions to manipulate IPC port sets.
+ */
+
+#include <mach/port.h>
+#include <mach/kern_return.h>
+#include <mach/message.h>
+#include <ipc/ipc_mqueue.h>
+#include <ipc/ipc_object.h>
+#include <ipc/ipc_pset.h>
+#include <ipc/ipc_right.h>
+#include <ipc/ipc_space.h>
+
+
+/*
+ * Routine: ipc_pset_alloc
+ * Purpose:
+ * Allocate a port set.
+ * Conditions:
+ * Nothing locked. If successful, the port set is returned
+ * locked. (The caller doesn't have a reference.)
+ * Returns:
+ * KERN_SUCCESS The port set is allocated.
+ * KERN_INVALID_TASK The space is dead.
+ * KERN_NO_SPACE No room for an entry in the space.
+ * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
+ */
+
+kern_return_t
+ipc_pset_alloc(
+ ipc_space_t space,
+ mach_port_t *namep,
+ ipc_pset_t *psetp)
+{
+ ipc_pset_t pset;
+ mach_port_t name;
+ kern_return_t kr;
+
+ kr = ipc_object_alloc(space, IOT_PORT_SET,
+ MACH_PORT_TYPE_PORT_SET, 0,
+ &name, (ipc_object_t *) &pset);
+ if (kr != KERN_SUCCESS)
+ return kr;
+ /* pset is locked */
+
+ ipc_target_init(&pset->ips_target, name);
+
+ *namep = name;
+ *psetp = pset;
+ return KERN_SUCCESS;
+}
+
+/*
+ * Routine: ipc_pset_alloc_name
+ * Purpose:
+ * Allocate a port set, with a specific name.
+ * Conditions:
+ * Nothing locked. If successful, the port set is returned
+ * locked. (The caller doesn't have a reference.)
+ * Returns:
+ * KERN_SUCCESS The port set is allocated.
+ * KERN_INVALID_TASK The space is dead.
+ * KERN_NAME_EXISTS The name already denotes a right.
+ * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
+ */
+
+kern_return_t
+ipc_pset_alloc_name(
+ ipc_space_t space,
+ mach_port_t name,
+ ipc_pset_t *psetp)
+{
+ ipc_pset_t pset;
+ kern_return_t kr;
+
+
+ kr = ipc_object_alloc_name(space, IOT_PORT_SET,
+ MACH_PORT_TYPE_PORT_SET, 0,
+ name, (ipc_object_t *) &pset);
+ if (kr != KERN_SUCCESS)
+ return kr;
+ /* pset is locked */
+
+ ipc_target_init(&pset->ips_target, name);
+
+ *psetp = pset;
+ return KERN_SUCCESS;
+}
+
+/*
+ * Routine: ipc_pset_add
+ * Purpose:
+ * Puts a port into a port set.
+ * The port set gains a reference.
+ * Conditions:
+ * Both port and port set are locked and active.
+ * The port isn't already in a set.
+ * The owner of the port set is also receiver for the port.
+ */
+
+void
+ipc_pset_add(
+ ipc_pset_t pset,
+ ipc_port_t port)
+{
+ assert(ips_active(pset));
+ assert(ip_active(port));
+ assert(port->ip_pset == IPS_NULL);
+
+ port->ip_pset = pset;
+ port->ip_cur_target = &pset->ips_target;
+ ips_reference(pset);
+
+ imq_lock(&port->ip_messages);
+ imq_lock(&pset->ips_messages);
+
+ /* move messages from port's queue to the port set's queue */
+
+ ipc_mqueue_move(&pset->ips_messages, &port->ip_messages, port);
+ imq_unlock(&pset->ips_messages);
+ assert(ipc_kmsg_queue_empty(&port->ip_messages.imq_messages));
+
+ /* wake up threads waiting to receive from the port */
+
+ ipc_mqueue_changed(&port->ip_messages, MACH_RCV_PORT_CHANGED);
+ assert(ipc_thread_queue_empty(&port->ip_messages.imq_threads));
+ imq_unlock(&port->ip_messages);
+}
+
+/*
+ * Routine: ipc_pset_remove
+ * Purpose:
+ * Removes a port from a port set.
+ * The port set loses a reference.
+ * Conditions:
+ * Both port and port set are locked.
+ * The port must be active.
+ */
+
+void
+ipc_pset_remove(
+ ipc_pset_t pset,
+ ipc_port_t port)
+{
+ assert(ip_active(port));
+ assert(port->ip_pset == pset);
+
+ port->ip_pset = IPS_NULL;
+ port->ip_cur_target = &port->ip_target;
+ ips_release(pset);
+
+ imq_lock(&port->ip_messages);
+ imq_lock(&pset->ips_messages);
+
+ /* move messages from port set's queue to the port's queue */
+
+ ipc_mqueue_move(&port->ip_messages, &pset->ips_messages, port);
+
+ imq_unlock(&pset->ips_messages);
+ imq_unlock(&port->ip_messages);
+}
+
+/*
+ * Routine: ipc_pset_move
+ * Purpose:
+ * If nset is IPS_NULL, removes port
+ * from the port set it is in. Otherwise, adds
+ * port to nset, removing it from any set
+ * it might already be in.
+ * Conditions:
+ * The space is read-locked.
+ * Returns:
+ * KERN_SUCCESS Moved the port.
+ * KERN_NOT_IN_SET nset is null and port isn't in a set.
+ */
+
+kern_return_t
+ipc_pset_move(
+ ipc_space_t space,
+ ipc_port_t port,
+ ipc_pset_t nset)
+{
+ ipc_pset_t oset;
+
+ /*
+ * While we've got the space locked, it holds refs for
+ * the port and nset (because of the entries). Also,
+ * they must be alive. While we've got port locked, it
+ * holds a ref for oset, which might not be alive.
+ */
+
+ ip_lock(port);
+ assert(ip_active(port));
+
+ oset = port->ip_pset;
+
+ if (oset == nset) {
+ /* the port is already in the new set: a noop */
+
+ is_read_unlock(space);
+ } else if (oset == IPS_NULL) {
+ /* just add port to the new set */
+
+ ips_lock(nset);
+ assert(ips_active(nset));
+ is_read_unlock(space);
+
+ ipc_pset_add(nset, port);
+
+ ips_unlock(nset);
+ } else if (nset == IPS_NULL) {
+ /* just remove port from the old set */
+
+ is_read_unlock(space);
+ ips_lock(oset);
+
+ ipc_pset_remove(oset, port);
+
+ if (ips_active(oset))
+ ips_unlock(oset);
+ else {
+ ips_check_unlock(oset);
+ oset = IPS_NULL; /* trigger KERN_NOT_IN_SET */
+ }
+ } else {
+ /* atomically move port from oset to nset */
+
+ if (oset < nset) {
+ ips_lock(oset);
+ ips_lock(nset);
+ } else {
+ ips_lock(nset);
+ ips_lock(oset);
+ }
+
+ is_read_unlock(space);
+ assert(ips_active(nset));
+
+ ipc_pset_remove(oset, port);
+ ipc_pset_add(nset, port);
+
+ ips_unlock(nset);
+ ips_check_unlock(oset); /* KERN_NOT_IN_SET not a possibility */
+ }
+
+ ip_unlock(port);
+
+ return (((nset == IPS_NULL) && (oset == IPS_NULL)) ?
+ KERN_NOT_IN_SET : KERN_SUCCESS);
+}
+
+/*
+ * Routine: ipc_pset_destroy
+ * Purpose:
+ * Destroys a port_set.
+ *
+ * Doesn't remove members from the port set;
+ * that happens lazily. As members are removed,
+ * their messages are removed from the queue.
+ * Conditions:
+ * The port_set is locked and alive.
+ * The caller has a reference, which is consumed.
+ * Afterwards, the port_set is unlocked and dead.
+ */
+
+void
+ipc_pset_destroy(
+ ipc_pset_t pset)
+{
+ assert(ips_active(pset));
+
+ pset->ips_object.io_bits &= ~IO_BITS_ACTIVE;
+
+ imq_lock(&pset->ips_messages);
+ ipc_mqueue_changed(&pset->ips_messages, MACH_RCV_PORT_DIED);
+ imq_unlock(&pset->ips_messages);
+
+ /* Common destruction for the IPC target. */
+ ipc_target_terminate(&pset->ips_target);
+
+ ips_release(pset); /* consume the ref our caller gave us */
+ ips_check_unlock(pset);
+}
+
+#include <mach_kdb.h>
+
+
+#if MACH_KDB
+#define printf kdbprintf
+
+/*
+ * Routine: ipc_pset_print
+ * Purpose:
+ * Pretty-print a port set for kdb.
+ */
+
+void
+ipc_pset_print(
+ ipc_pset_t pset)
+{
+ extern int indent;
+
+ printf("pset 0x%x\n", pset);
+
+ indent += 2;
+
+ ipc_object_print(&pset->ips_object);
+ iprintf("local_name = 0x%x\n", pset->ips_local_name);
+ iprintf("kmsgs = 0x%x", pset->ips_messages.imq_messages.ikmq_base);
+ printf(",rcvrs = 0x%x\n", pset->ips_messages.imq_threads.ithq_base);
+
+ indent -=2;
+}
+
+#endif MACH_KDB