<Interface>_Interface_Create first mallocs the structure and fills in the operation methods, along with userBind and userDestroy. It also sets the bind method to the internal S_Binding_Create routine.
In the case of a local interface (divined by grubbing around in the partial interface reference), this is all there is to do. The stub sticks the address of the interface structure into the interface ref address hint and returns.
For remote interfaces the stub calls svc_offer to offer the interface and fill in the interface reference.
Destruction of interfaces in very simple. <Interface>_Interface_Destroy first checks to see if the interface's svcIntf field is non-null, indicating a remote interface. If this is so, it calls svc_withdraw to remove the service. Once this returns, it simply frees the structure.
There is an internal stub routine for each interface which is the sole constructor for any binding to the interface. Its prototype is:
static bool_t S_Binding_Create ( Aubergine_Interface *self, Aubergine_Binding **b, svcSession *s )The address of this routine is held in two place: it is the bind method of the binding object, and it is also passed to the runtime library during interface creation and is associated with the svcInterface This means that it can be called both directly by user-level server code or by the runtime library in response to a remote bind request.
When an incoming bind request arrives in the runtime library, it creates a server session object (an svcSession) and a thread to handle requests. This thread then calls the stub level constructor with a pointer to the session. The stub builds a stub-level binding structure, calling user-level initialisation code if necessary, and then returns TRUE with the binding pointer filled in.
If the stub returns FALSE, something has gone wrong and there is no state at the stub or user levels. The runtime library thus deletes the session and the thread terminates.
There is an internal stub routine for each interface which is the sole destructor for any binding to the interface. Its prototype is:
static void S_Binding_Destroy ( Aubergine_Binding *b )The address of this routine is held in two place: it is the destroy method of the binding object, and it is also passed to the runtime library during interface creation and appears as the destr method of the run-time server session. This means that it can be called both by user-level server code or by the runtime library. It should only ever be called by its binding's thread.
What it does is first to call the server session's destroy method, if the session exists. This completely shuts down and frees the session from the runtime library point of view. Note that for the runtime system to destroy a binding, it must call its destr method, and never invoke destroy itself, so as to allow the stubs and user code to free up their resources.
Next it calls the userDestroy method, if present, then frees up the binding structure itself and returns. The binding is now totally gone.
There is one operation dispatcher in the stubs for an interface. This is called by the runtime system whenever a request comes in on a binding, and looks like:
static bool_t S_Dispatch( Aubergine_Binding *b, excpt_t *exc, MSDR *msdrs, u_int32 procno )
Note that neither the dispatcher, nor any operation stub routines, are involved in an invocation across a local binding.
An operation stub routine prototype looks like:
static bool_t S_BuyRound( Beer_Binding *self, excpt_t *exc, MSDR *msdrs )The meanings of the parameters are exactly as for the operation dispatcher.
The stub routine simply unmarshals the arguments, raising an InternalError exception if it goes wrong and calls the relevant ops field of the binding structure.
In the case of an interrogation, if the user routine (the one supplied in the binding structure) returns FALSE, it is assumed that the user has called an exception-raising routine (see below) to signal an exception. In this case the buffer specified by msdrs has been loaded with the parameters of the exception and is ready to go, so the stub simply returns TRUE to the calling runtime routine to indicate that the buffer can be sent.
If the user returns TRUE, then the server implementation is assumed to have terminated normally. The stub invokes the session method reply_init with an exception number of zero to signal successful termination and the results are marshalled. The routine returns TRUE to send the packet off.
If anything else happens, then exc will contain the details of the failure, filled in either by the stub or the runtime library. FALSE is returned to the runtime which will marshal it into an InternalError exception and send it off.
In the case of an announcement, the user routine is assumed to be of void return type. The stub simply returns TRUE, and the runtime takes no further action.
An operation termination is either successful completion, or the raising of a parametrised exception. User routines signal successful completion by returning TRUE, as described above. User-defined exceptions are signalled by the user returning the results of the relevant exception method of the binding.
Note that it is possible for a user-routine to signal a fake engineering failure, since there is a InternalError method available. This is discouraged, because it's pretty confusing apart from anything else. However, it could conceivably be used for RPC-level protocol converters.
The exception stubs being methods of the binding have signatures determined by the parameters of the exceptions themselves as given in the interface specification. In fact, they look a lot like client side announcement stub routines. They simply call the reply_init method of the session, marshal their arguments, and return TRUE. If any engineering failures occur the routine returns false; enough state is left in the session structure for the runtime to marshal and transmit the correct InternalError exception.
It is important to realise that the stubs' semantics are the same as for the local case exception handlers. Though the meanings of TRUE and FALSE as returned by a user operation routine may at first sight seem confusing, they ensure consistency across remote and local bindings.
The control method of any server-side binding is bound to a routine that looks like:
static bool_t S_Binding_Control( Aubergine_Binding *b, u_int32 op, caddr_t param )-which simply acts as a filter for the control method of the session structure: if the session structure is there, it calls it. Otherwise, it returns false.
Interface creation in the local case does not involve a call to svc_offer, instead the address of the newly created interface is stuffed into the interface reference. Destroying an interface just frees it. Care must be taken in this case with outstanding client bindings, as detailed previously.
Construction of bindings is pretty much as in the remote case, except that exception stubs are not filled in. A local bind is indicated by a null session pointer in the bind call.
Destruction of bindings on the server side is the same as the remote case, except that the binding destructor will not attempt to destroy a null session.