/* Ports library for server construction
   Copyright (C) 1993, 1994, 1995, 1996 Free Software Foundation, Inc.
   Written by Michael I. Bushnell.

   This file is part of the GNU Hurd.

   The GNU Hurd 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.

   The GNU Hurd 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. */

#ifndef _HURD_PORTS_
#define _HURD_PORTS_

#include <mach.h>
#include <stdlib.h>
#include <hurd.h>
#include <mach/notify.h>

/* These are global values for common flags used in the various structures.
   Not all of these are meaningful in all flag fields.  */
#define PORTS_INHIBITED		0x0100 /* block RPC's */
#define PORTS_BLOCKED		0x0200 /* if INHIBITED, someone is blocked */
#define PORTS_INHIBIT_WAIT	0x0400 /* someone wants to start inhibit */
#define PORTS_NO_ALLOC		0x0800 /* block allocation */
#define PORTS_ALLOC_WAIT	0x1000 /* someone wants to allocate */

struct port_info
{
  struct port_class *class;
  int refcnt;
  int weakrefcnt;
  mach_port_mscount_t mscount;
  mach_msg_seqno_t cancel_threshold;
  int flags;
  mach_port_t port_right;
  struct rpc_info *current_rpcs;
  struct port_bucket *bucket;
  void **hentry;
  struct port_info *next, **prevp; /* links on port_class list */
};
/* FLAGS above are the following: */
#define PORT_HAS_SENDRIGHTS	0x0001 /* send rights extant */
#define PORT_INHIBITED		PORTS_INHIBITED
#define PORT_BLOCKED		PORTS_BLOCKED
#define PORT_INHIBIT_WAIT	PORTS_INHIBIT_WAIT

struct port_bucket
{
  mach_port_t portset;
  struct ihash *htable;
  int rpcs;
  int flags;
  int count;
  struct port_bucket *next;
};
/* FLAGS above are the following: */
#define PORT_BUCKET_INHIBITED	PORTS_INHIBITED
#define PORT_BUCKET_BLOCKED	PORTS_BLOCKED
#define PORT_BUCKET_INHIBIT_WAIT PORTS_INHIBIT_WAIT
#define PORT_BUCKET_NO_ALLOC	PORTS_NO_ALLOC
#define PORT_BUCKET_ALLOC_WAIT	PORTS_ALLOC_WAIT

struct port_class
{
  int flags;
  int rpcs;
  struct port_info *ports;
  int count;
  void (*clean_routine) (void *);
  void (*dropweak_routine) (void *);
  struct ports_msg_id_range *uninhibitable_rpcs;
};
/* FLAGS are the following: */
#define PORT_CLASS_INHIBITED	PORTS_INHIBITED
#define PORT_CLASS_BLOCKED	PORTS_BLOCKED
#define PORT_CLASS_INHIBIT_WAIT	PORTS_INHIBIT_WAIT
#define PORT_CLASS_NO_ALLOC	PORTS_NO_ALLOC
#define PORT_CLASS_ALLOC_WAIT	PORTS_ALLOC_WAIT

struct rpc_info
{
  thread_t thread;
  struct rpc_info *next, **prevp;
  struct rpc_notify *notifies;
};

/* An rpc has requested interruption on a port notification.  */
struct rpc_notify
{
  struct rpc_info *rpc;		/* Which rpc this is for. */
  struct ports_notify *notify;	/* Which port/request this refers too. */

  struct rpc_notify *next;	/* Notify for this rpc. */
  unsigned pending;		/* Number of requests this represents.  */

  struct rpc_notify *next_req;	/* rpc for this notify.  */
  struct rpc_notify **prev_req_p; /* who points to this rpc_notify. */
};

/* A notification request on a (not necessarily registered) port.  */
struct ports_notify
{
  mach_port_t port;		/*  */
  mach_msg_id_t what;		/* MACH_NOTIFY_* */
  unsigned pending : 1;		/* There's a notification outstanding.  */
  struct mutex lock;

  struct rpc_notify *reqs;	/* Which rpcs are notified by this port. */
  struct ports_notify *next, **prevp; /* Linked list of all notified ports.  */
};

/* A linked list of ports that have had notification requested.  */
extern struct ports_notify *_ports_notifications;

/* Free lists for notify structures.  */
extern struct ports_notify *_ports_free_ports_notifies;
extern struct rpc_notify *_ports_free_rpc_notifies;

/* 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 ports_msg_id_range
{
  mach_msg_id_t start, end;
  struct ports_msg_id_range *next;
};

/* This is the initial value for the uninhibitable_rpcs field in new
   port_class structures.  The user may define this variable; the default
   value contains only an entry for interrupt_operation.  */
extern struct ports_msg_id_range *ports_default_uninhibitable_rpcs;

/* Port creation and port right frobbing */

/* Create and return a new bucket. */
struct port_bucket *ports_create_bucket (void);

/* Create and return a new port class.  If nonzero, CLEAN_ROUTINE will
   be called for each allocated port object in this class when it is
   being destroyed.   If nonzero, DROPWEAK_ROUTINE will be called
   to request weak references to be dropped.  (If DROPWEAK_ROUTINE is null,
   then normal references and hard references will be identical for
   ports of this class.)  */
struct port_class *ports_create_class (void (*clean_routine)(void *),
				       void (*dropweak_routine)(void *));

/* Create and return in RESULT a new port in CLASS and BUCKET; SIZE bytes
   will be allocated to hold the port structure and whatever private data the
   user desires.  */
error_t ports_create_port (struct port_class *class,
			   struct port_bucket *bucket,
			   size_t size,
			   void *result);

/* Just like ports_create_port, except don't actually put the port
   into the portset underlying BUCKET.  This is intended to be used
   for cases where the port right must be given out before the port is
   fully initialized; with this call you are guaranteed that no RPC
   service will occur on the port until you have finished initializing
   it and installed it into the portset yourself. */
error_t
ports_create_port_noinstall (struct port_class *class,
			     struct port_bucket *bucket,
			     size_t size,
			     void *result);
   
/* For an existing RECEIVE right, create and return in RESULT a new port
   structure; BUCKET, SIZE, and CLASS args are as for ports_create_port. */
error_t ports_import_port (struct port_class *class,
			   struct port_bucket *bucket,
			   mach_port_t port, size_t size,
			   void *result);

/* Destroy the receive right currently associated with PORT and allocate
   a new one. */
void ports_reallocate_port (void *port);

/* Destroy the receive right currently associated with PORT and designate
   RECEIVE as the new one. */
void ports_reallocate_from_external (void *port, mach_port_t receive);

/* Destroy the receive right currently associated with PORT.  After
   this call, ports_reallocate_port and ports_reallocate_from_external
   may not be used. */
void ports_destroy_right (void *port);

/* Return the receive right currently associated with PORT.  The effects
   on PORT are the same as in ports_destroy_right, except that the receive
   right itself is not affected.  Note that in multi-threaded servers, 
   messages might already have been dequeued for this port before it gets
   removed from the portset; such messages will get EOPNOTSUPP errors.  */
mach_port_t ports_claim_right (void *port);

/* Transfer the receive right from FROMPT to TOPT.  FROMPT ends up
   with a destroyed right (as if ports_destroy_right were called) and
   TOPT's old right is destroyed (as if ports_reallocate_from_external
   were called. */
error_t ports_transfer_right (void *topt, void *frompt);

/* Return the name of the receive right associated with PORT.  The user
   is responsible for creating an ordinary  send right from this name.  */
mach_port_t ports_get_right (void *port);



/* Reference counting */
   
/* Look up PORT and return the associated port structure, allocating a
   reference.  If the call fails, return 0.  If BUCKET is nonzero,
   then it specifies a bucket to search; otherwise all buckets will be
   searched.  If CLASS is nonzero, then the lookup will fail if PORT
   is not in CLASS. */
void *ports_lookup_port (struct port_bucket *bucket, 
			 mach_port_t port, struct port_class *class);

/* Allocate another reference to PORT. */
void ports_port_ref (void *port);

/* Allocate a weak reference to PORT. */
void ports_port_ref_weak (void *port);

/* Drop a reference to PORT. */
void ports_port_deref (void *port);

/* Drop a weak reference to PORT. */
void ports_port_deref_weak (void *port);

/* The user is responsible for listening for no senders notifications;
   when one arrives, call this routine for the PORT the message was
   sent to, providing the MSCOUNT from the notification. */
void ports_no_senders (void *port, mach_port_mscount_t mscount);
void ports_dead_name (void *notify, mach_port_t dead_name);

/* Block port creation of new ports in CLASS.  Return the number
   of ports currently in CLASS. */
int ports_count_class (struct port_class *class);

/* Block port creation of new ports in BUCKET.  Return the number
   of ports currently in BUCKET. */
int ports_count_bucket (struct port_bucket *bucket);

/* Permit suspended port creation (blocked by ports_count_class) 
   to continue. */
void ports_enable_class (struct port_class *class);

/* Permit suspend port creation (blocked by ports_count_bucket)
   to continue. */
void ports_enable_bucket (struct port_bucket *bucket);

/* Call FUN once for each port in BUCKET. */
error_t ports_bucket_iterate (struct port_bucket *bucket,
			      error_t (*fun)(void *port));



/* RPC management */
   
/* Type of MiG demuxer routines. */
typedef int (*ports_demuxer_type)(mach_msg_header_t *inp, 
				  mach_msg_header_t *outp);

/* Call this when an RPC is beginning on PORT.  INFO should be 
   allocated by the caller and will be used to hold dynamic state. 
   If this RPC should be abandoned, return EDIED; otherwise we
   return zero. */
error_t ports_begin_rpc (void *port, mach_msg_id_t msg_id,
			 struct rpc_info *info);

/* Call this when an RPC is concluding.  Args must be as for the
   paired call to ports_begin_rpc. */
void ports_end_rpc (void *port, struct rpc_info *info);

/* Begin handling operations for the ports in BUCKET, calling DEMUXER
   for each incoming message.  Return if TIMEOUT is nonzero and no
   messages have been received for TIMEOUT milliseconds.  Use
   only one thread (the calling thread).  */
void ports_manage_port_operations_one_thread(struct port_bucket *bucket,
					     ports_demuxer_type demuxer,
					     int timeout);

/* Begin handling operations for the ports in BUCKET, calling DEMUXER
   for each incoming message.  Return if GLOBAL_TIMEOUT is nonzero and
   no messages have been receieved for GLOBAL_TIMEOUT milliseconds.
   Create threads as necessary to handle incoming messages so that no
   port is starved because of sluggishness on another port.  All
   threads created (and the calling thread) will be wired with
   cthread_wire if WIRE_CTHREADS is non-zero.  All threads created
   (and the calling thread) will be wired with thread_wire if
   WIRE_THREADS is non-zero (it must be the priviliged host port in
   order to succeed).  If LOCAL_TIMEOUT is non-zero, then individual
   threads will die off if they handle no incoming messages for
   LOCAL_TIMEOUT milliseconds. */
void ports_manage_port_operations_multithread (struct port_bucket *bucket,
					       ports_demuxer_type demuxer,
					       int thread_timeout,
					       int global_timeout,
					       int wire_cthreads,
					       mach_port_t wire_threads);
   
/* Interrupt any pending RPC on PORT.  Wait for all pending RPC's to
   finish, and then block any new RPC's starting on that port. */
error_t ports_inhibit_port_rpcs (void *port);

/* Similar to ports_inhibit_port_rpcs, but affects all ports in CLASS. */
error_t ports_inhibit_class_rpcs (struct port_class *class);

/* Similar to ports_inhibit_port_rpcs, but affects all ports in BUCKET. */
error_t ports_inhibit_bucket_rpcs (struct port_bucket *bucket);

/* Similar to ports_inhibit_port_rpcs, but affects all ports whatsoever. */
error_t ports_inhibit_all_rpcs (void);

/* Reverse the effect of a previous ports_inhibit_port_rpcs for this PORT,
   allowing blocked RPC's to continue. */
void ports_resume_port_rpcs (void *port);

/* Reverse the effect of a previous ports_inhibit_class_rpcs for CLASS. */
void ports_resume_class_rpcs (struct port_class *class);

/* Reverse the effect of a previous ports_inhibit_bucket_rpcs for BUCKET. */
void ports_resume_bucket_rpcs (struct port_bucket *bucket);

/* Reverse the effect of a previous ports_inhibit_all_rpcs. */
void ports_resume_all_rpcs (void);

/* Cancel (with thread_cancel) any RPC's in progress on PORT. */
void ports_interrupt_rpcs (void *port);

/* 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);

/* 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);

/* Some handy aliases.  */
#define ports_interrupt_self_on_port_death(obj, port) \
  ports_interrupt_self_on_notification (obj, port, MACH_NOTIFY_DEAD_NAME)

/* 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);

/* Default servers */

/* A notification server that calls the ports_do_mach_notify_* routines.  */
int ports_notify_server (mach_msg_header_t *, mach_msg_header_t *);

/* Notification server routines called by ports_notify_server.  */
extern kern_return_t ports_do_mach_notify_dead_name (mach_port_t notify, mach_port_t deadport);
extern kern_return_t ports_do_mach_notify_msg_accepted (mach_port_t notify, mach_port_t name);
extern kern_return_t ports_do_mach_notify_no_senders (mach_port_t port, mach_port_mscount_t count);
extern kern_return_t ports_do_mach_notify_port_deleted (mach_port_t notify, mach_port_t name);
extern kern_return_t ports_do_mach_notify_port_destroyed (mach_port_t notify, mach_port_t name);
extern kern_return_t
 ports_do_mach_notify_send_once (mach_port_t notify);

/* A default interrupt server */
int ports_interrupt_server (mach_msg_header_t *, mach_msg_header_t *);
extern kern_return_t ports_S_interrupt_operation (mach_port_t, 
						  mach_port_seqno_t);

/* Private data */
extern struct mutex _ports_lock;
extern struct condition _ports_block;
extern struct port_bucket *_ports_all_buckets;
extern int _ports_total_rpcs;
extern int _ports_flags;
#define _PORTS_INHIBITED	PORTS_INHIBITED
#define _PORTS_BLOCKED		PORTS_BLOCKED
#define _PORTS_INHIBIT_WAIT	PORTS_INHIBIT_WAIT
void _ports_complete_deallocate (struct port_info *);
error_t _ports_create_port_internal (struct port_class *, struct port_bucket *,
				     size_t, void *, int);

#endif
