
First some definitions:

An *address* is an opaque object used for identifying entities such as
groups and group members.  A process may have more than one member
address.

A process *joins* a group by specifying the group address.

A *group* is comprised of the set of processes that have joined using
the group's address.  The exact membership of the group is not necessarily
known to anybody.

A *view* is a list of member addresses.

The *current view* of a group at some member is the list of members of
the group (typically those members that are believed to be reachable
from that member).

A *message* is a list of bytes.

A *channel* is a representation of one of the members of a process.
A channel can be associated with at most one group at a time.  It is
used to send and receive messages for the group, as well as to obtain
the current view.

An *event* is an object that is passed between a process and one of
its channels.  Examples are messages and view updates.

------------------------------------------------------------------------

In many group communication systems, messages and group views are
not independent of one another.  Messages are usually delivered to
a particular view, and often also sent in a particular view.  It is
therefore necessary that we synchronize *view changes* with message
sending and delivery using a three-way handshake between a process and a
channel.

The channel delivers three types of events to a process:

	message delivery:	a message from the group
	block request:		a request to stop sending messages
	view update:		a new view of the group

The process delivers two types of events to a channel:

	message send:		a message to the group
	block acknowledgment:	the process agrees to not send
				messages until the next view update

When a channel wants to update the view, it first requests the process
to stop sending messages.  The process eventually responds with an
acknowledgment.  Now the channel can communicate with its peers in
other processes to agree on a new view.  When completed, it unblocks
the process by presenting it with a new view.

A few comments:  a process is allowed to pass message send events to the
channel while blocked, but the semantics vary.  In some cases, they may
be ignored, or result in some exception.  In other cases, they may just
have weaker delivery guarantees with respect to view changes.

A process is allowed to pass a block acknowledgment down to a channel
before it gets a block request in case it already knows that it won't
need to send any more messages in the current view.  In that case, the
channel may not pass a block request event up when a view change occurs.

The interaction between a process and its channel can thus be characterized
by two states:

	Normal:		send events have strong semantics
	Blocked:	send events are discouraged

The system passes from Normal to Blocked state when the process passes a
block acknowledgment event to the channel, and back when the channel passes
a view update event to the process.  The initial state is Blocked.

	enum { Normal, Blocked } state = Blocked initially;
	address[] current_view = { my_address } initially;

	in Normal state {
		event "message send" from Process to Channel:
			message is sent in current_view and its
			delivery semantics are well-defined somehow.
		event "block request" from Channel to Process:
			doesn't change anything, but requests the
			process to eventually pass a "block ack".
		event "block ack" from Process to Channel:
			state := Blocked;
			(but doesn't change the current view).
	}
	in Blocked state {
		event "view update" from Channel to Process:
			current_view := new view;
			state := Normal;
	}
	in either state { 
		event "message delivery" from Channel to Process:
			message is delivered in current_view;
	}

------------------------------------------------------------------------

We will now present an abstract interface (= a collection of methods)
for channels without any language or system binding.  The interface is
designed to make it easily ``bindable'' to any language or system, and
is inspired by the BSD socket interface.  All methods can return some
exceptional conditions.  We will usually omit failure exceptions.

Channel interfaces are created by Channel Factories.  Different factories
correspond to different implementations.  In BSD, channel factories would
typically be accessed through the socket() system call.

interface Channel {
	connect(address group_addr) --> ()
		A channel factory does not necessarily need to associate
		a channel with a particular group, protocol, quality of
		service, or anything until connect() is invoked.

	getmyaddress() --> (address me)
		Returns the member address of the channel.  The address
		may only be available after a successful connect().

	getview() --> (List<address> members)
		Returns the current view, which always has to include
		the member address of the channel.  The view may only
		be available after a successful connect().

	recv() --> (address source, Event event)
		Event is one of MessageEvent, ViewEvent, or BlockEvent.
		In case of a MessageEvent, address source contains the
		address of the sender of the message.  (However, some
		group communications may not keep track of senders, in
		which case it is allowed to use some place holder such
		as the group address.)

		In case of a ViewEvent, the source address is a special
		predefined address called ViewAddress.  The ViewEvent
		is otherwise empty (that is, has no state associated with
		it), but the current view can be retrieved using getview().

		In case of a BlockEvent, the source is a special
		predefined address called BlockAddress.  The BlockEvent
		is otherwise empty.

	send(address destination, message msg) --> ()
		Sends a "message send" event to the channel.  A special
		*broadcast address* is available to send a message to all
		members in the current view.  Otherwise a member address
		is specified.

		Note that send succeeds even if the group communication
		system is trying to block the application, so in case you
		have enabled blocking, you *must* invoke recv() regularly
		in order to find out if the current view is trying to block
		for a update.
	
	blockack() --> ()
		Sends a "block acknowledgment" event to the channel.

	disconnect() --> ()
		Disassociates the current channel with the group specified
		in connect().
	
	setopt(option opt, value val) --> ()
		Specifies the value for one of a set of options that the
		channel supports.  All channel have to support the following
		options:

		Block: a Boolean option (initially off) that specifies
			that the process is interested in BlockEvents
			If not, BlockEvents are not delivered.
		View: a Boolean option (initially off) that specifies
			that the process is interested in ViewEvents.
			If not, ViewEvents are not delivered, but the
			current view is still accessible through getview().
		Local: a Boolean option (initially off) that specifies
			that the process is interested in receiving its
			own broadcast messages to the group.
		
		setopt() can also be used to change a variety of quality
		of service parameters to the channel that may (or may not)
		have been specified to the factory when the channel was
		created.
	
	getopt(option opt) --> (value val)
		Returns the current value of an option.
}

Note that it is *not* specified that send() and recv() methods are only
allowed after connect().  These kinds of restrictions are up to the
implementation of the channel.

------------------------------------------------------------------------

C/BSD socket binding

In this section we show how this interface may be bound to the UNIX BSD
interface for the C programming language.

An address would be of type "struct sockaddr".

A channel would be represented by a "file descriptor" returned by socket().

Channel.send() would be implemented by sendto() for point-to-point messages,
and by send() for broadcasts.

Channel.recv() would be implemented by recvfrom().  The source address
returned by recvfrom implies the type of event returned.

Channel.connect() would be implemented by connect().

Channel.disconnect() would be implemented by shutdown().  (BSD close() or
WINSOCK closesocket() is used for destruction of channels, not for
disconnecting.)

Channel.blockack() would be implemented using sendto() using some
special address (e.g., INADDR_BLOCKACK).

Channel.getmyname() would be implemented by getsockname().  Similarly,
getpeername() would return the group address.

Channel.getview() would be implemented using ioctl().

Channel.setopt(), and Channel.getopt() can be implemented by getopt() and
setopt() resp., but it may be more easily portable if also implemented
using ioctl().

------------------------------------------------------------------------

In this section we address issues concerning particular implementations.

Managed Groups
==============

There are at least four ways in which managed groups can be supported:

1) the factory may have an option specifying which core group to use.
2) the group name specified in connect() may be used to look up which
   core group to use.
3) setopt() may support a CORE_GROUP option in case you want to allow
   switching core groups on-the-fly.  This option would be factory-specific
   and is therefore not defined in the interface.
4) any object may support more than one interface.  Objects that support
   the channel interface may support other interfaces for managed groups.



