For each SharedObj.T
subtype T
intended to support the shared
object protocol, there must be a pair of shared object stubs, one
of which distributes local method invocations, the other of which
applies incoming remote method invocations. \index{Shared Object
stubs}
The shared object stub defines a subtype of T
in which every
method is overridden by a procedure which sends the method
invocation information to a central sequencer, waits until it
receives this information back in sequence with method invocations
from other copies of the shared object, and then applies the method
locally. Such an object is constructed by the shared object
runtime whenever a subtype T
of SharedObj.T
is created and
initialized, or received via the network object runtime.
.\index{Shared Object stubs!distribution}
The shared object stub also defines a single procedure of type
Dispatcher
that is called to unmarshal and dispatch remote
invocations. \index{Shared Object stubs!incoming method invocation}
INTERFACESharedObjStubLib ; IMPORT EventProtocol, EventStubLib, Rd, Wr, SharedObj, ObjectSpace, Thread, Pickle2 AS Pickle; FROM EventProtocol IMPORT Byte8, Int32; TYPE StubProtocol = EventProtocol.StubProtocol;
Typecode = CARDINAL;
Typecode
is the type of those values returned by the Modula-3
TYPECODE
operator. \index{typecodes}
Handle <: PublicHandle; PublicHandle = OBJECT eh: EventStubLib.Handle; local: BOOLEAN; END;
Handle
represents a single message being worked on by a client,
either being created or responded to. Clients need to see the
EventStubLib.Handle
to call the EventStubLib routines.
\paragraph{Shared object client stubs.} \index{stubs!client}
Here is a simplified sketch of the procedure calls performed by a
client to make a shared update call to a method of obj
:
VAR m := StartCall(obj); BEGIN IF MarshalArgs(m) THEN <marshal to "c" the number of this method> <marshal to "c" the method arguments> SequenceCall(m, stubProt); END; TRY AcquireWriteLock(obj); <apply the method> FINALLY ReleaseWriteLock(obj); IF m # NIL THEN EndCall(m) END; END END;The sender always marshals arguments in its native format; the receiver performs any conversions that may be needed. Here are the specifications of the client protocol procedures:
PROCEDURE StartCall(obj: SharedObj.T) : Handle RAISES {Wr.Failure, Thread.Alerted};
Return a Handle structure to the owner ofobj
, write to the connection a protocol request to perform a remote method call toobj
, using the data representationNativeRep
.\ttindex{SharedObjRT.StartCall}
Upon return from
StartCall
, the client stub should marshal
a specification of the method being invoked followed by any
arguments.
PROCEDURE MarshalArgs(m: Handle): BOOLEAN;
MarshalArgs
returns true if the arguments need to be marshalled.
Arguments are only marshalled if the call is going to be sent to
other machines.
PROCEDURE SequenceCall(m: Handle; stubProt: StubProtocol) RAISES {SharedObj.Error, Thread.Alerted};
SequenceCall
indicates the end of the arguments for the current method invocation event, and blocks waiting for the update event to be sequenced. The valuestubProt
is the stub protocol version under which the arguments and results were encoded.\ttindex{SharedObjRT.SequenceCall}
Upon return from
SequenceCall
the client stub should actually
apply the method to the local object.
PROCEDURE EndCall(m: Handle) RAISES {Thread.Alerted};
EndCall
indicates the end of the shared object call. This call must be made after sequence call, and the local method should be called betweenSequenceCall
andEndCall
. \ttindex{SharedObjStubLib.EndCall}
\paragraph{Marshaling of generic data.} \index{marshaling!of generic data} The following procedures are made available to permit the generic marshaling of various primitive data types. In general, the
EventStubLib
routines are used, except where equivalent routines
exist here.
PROCEDURE OutRef (h: Handle; r: REFANY) RAISES {Wr.Failure, Thread.Alerted};
Marshal the data structure reachable fromr
. Certain datatypes are handled specially: subtypes ofRd.T
andWr.T
are not allowed to be marshalled. The typesTEXT
andREF ARRAY OF TEXT
are marshaled by copying via custom code for speed. Subtypes ofNetObj.T
andSharedObj.T
are copied by sending their network identifiers and only copying the data at a later point if the corresponding object does not exist on the remote machines. All others are marshaled by copying as pickles. \ttindex{EventStubLib.OutRef}
PROCEDURE OutChars (h: Handle; READONLY chars: ARRAY OF CHAR) RAISES {Wr.Failure, Thread.Alerted};
Marshal a char array in native format.
PROCEDURE OutBytes (h: Handle; READONLY bytes: ARRAY OF Byte8) RAISES {Wr.Failure, Thread.Alerted};
Marshal a byte array.
PROCEDURE OutInteger (h: Handle; i: INTEGER) RAISES {Wr.Failure, Thread.Alerted};
Marshal an integer in native format.
PROCEDURE OutInt32 (h: Handle; i: Int32) RAISES {Wr.Failure, Thread.Alerted};
Marshal a 32-bit integer in native format.
PROCEDURE OutByte (h: Handle; i: Byte8) RAISES {Wr.Failure, Thread.Alerted};
Marshal a byte.
PROCEDURE OutBoolean (h: Handle; bool: BOOLEAN) RAISES {Wr.Failure, Thread.Alerted};
Marshal a boolean value.
PROCEDURE OutReal (h: Handle; r: REAL) RAISES {Wr.Failure, Thread.Alerted};
Marshal a real in native format.
PROCEDURE OutLongreal (h: Handle; card: LONGREAL) RAISES {Wr.Failure, Thread.Alerted};
Marshal a longreal in native format.
PROCEDURE OutExtended (h: Handle; card: EXTENDED) RAISES {Wr.Failure, Thread.Alerted};
Marshal an extended in native format.
PROCEDURE OutCardinal (h: Handle; card: CARDINAL) RAISES {Wr.Failure, Thread.Alerted};
Marshal a cardinal in native format.
The following procedures are provided in support of generic unmarshaling of data. In all cases,
rep
indicates the encoding of the incoming
data. These procedures could be replaced by inline unmarshaling code
whenever the relevant elements of rep
match the corresponding elements
of NativeRep
.
PROCEDURE InRef (h: EventStubLib.Handle; tc := -1): REFANY RAISES {SharedObj.Error, Rd.Failure, Thread.Alerted};
Unmarshal a marshaled subtype ofREFANY
as pickled byOutRef
. Iftc
is non-negative, it is the typecode for the intended type of the reference. AError
exception is raised if the unpickled result is not a subtype of this type. Iftc
is negative, no type checking is performed. \ttindex{EventStubLib.InRef}
PROCEDURE InChars (h: EventStubLib.Handle; VAR chars: ARRAY OF CHAR) RAISES {SharedObj.Error, Rd.Failure, Thread.Alerted};
Unmarshal a char array of length NUMBER(chars)
.
PROCEDURE InBytes (h: EventStubLib.Handle; VAR bytes: ARRAY OF Byte8) RAISES {SharedObj.Error, Rd.Failure, Thread.Alerted};
Unmarshal a byte array of length NUMBER(bytes)
.
PROCEDURE InInteger (h: EventStubLib.Handle; min := FIRST(INTEGER); max := LAST(INTEGER) ): INTEGER RAISES {SharedObj.Error, Rd.Failure, Thread.Alerted};
Unmarshal an integer, checking that its value is in [min..max]
.
PROCEDURE InInt32 (h: EventStubLib.Handle; min := FIRST(Int32); max := LAST(Int32)): Int32 RAISES {SharedObj.Error, Rd.Failure, Thread.Alerted};
Unmarshal a 32-bit integer, checking that its value is in
[min..max]
.
PROCEDURE InByte (h: EventStubLib.Handle; max := LAST(Byte8)): Byte8 RAISES {SharedObj.Error, Rd.Failure, Thread.Alerted};
Unmarshal a byte, checking that its value is in [0..max]
.
PROCEDURE InBoolean (h: EventStubLib.Handle): BOOLEAN RAISES {SharedObj.Error, Rd.Failure, Thread.Alerted};
Unmarshal a boolean value.
PROCEDURE InReal (h: EventStubLib.Handle): REAL RAISES {SharedObj.Error, Rd.Failure, Thread.Alerted};
Unmarshal a real value.
PROCEDURE InLongreal (h: EventStubLib.Handle): LONGREAL RAISES {SharedObj.Error, Rd.Failure, Thread.Alerted};
Unmarshal a longreal value.
PROCEDURE InExtended (h: EventStubLib.Handle): EXTENDED RAISES {SharedObj.Error, Rd.Failure, Thread.Alerted};
Unmarshal an extended value.
PROCEDURE InCardinal (h: EventStubLib.Handle; lim: CARDINAL := LAST(CARDINAL)): CARDINAL RAISES {SharedObj.Error, Rd.Failure, Thread.Alerted};
Unmarshal a cardinal, checking that its value is in [0..lim]
.
\paragraph{Local Object Locking.} \index{Shared object stubs!locking the object} The stubs need to get lock the object before they act on it. Non-update method stubs execute the object method inside a read lock, which can be acquired simultaneously be multiple readers, as follows:
TRY AcquireReadLock(self); RETURN <execute non-update method> FINALLY ReleaseReadLock(self); END;Update methods execute the object method inside an exclusive lock, in a similar fashion:
TRY AcquireWriteLock(self); RETURN <execute update method> FINALLY ReleaseWriteLock(self); END;PROCEDURE ReleaseReadLock(self: SharedObj.T); PROCEDURE AcquireReadLock(self: SharedObj.T); PROCEDURE ReleaseWriteLock(self: SharedObj.T); PROCEDURE AcquireWriteLock(self: SharedObj.T);\paragraph{Pickling stub routines.} Before an object can be sent to another machine, the local space must finish initializing. In particular, we must either identify ourself as a sequencer or set our sequencer.PROCEDURE StartWritePickle(obj: SharedObj.T; wr: Pickle.Writer) RAISES {Pickle.Error, Wr.Failure, Thread.Alerted}; PROCEDURE EndWritePickle(obj: SharedObj.T; wr: Pickle.Writer) RAISES {Pickle.Error, Wr.Failure, Thread.Alerted}; PROCEDURE StartReadPickle(obj: SharedObj.T; rd: Pickle.Reader; from: ObjectSpace.T) RAISES {Pickle.Error, Rd.Failure, Thread.Alerted}; PROCEDURE SetupNewCopy(obj: SharedObj.T; rd: Pickle.Reader; id: Pickle.RefID; from: ObjectSpace.T): SharedObj.T RAISES {Pickle.Error, Thread.Alerted}; PROCEDURE InhibitTransmission(tc: INTEGER; reason: TEXT); (* Inhibits the network transmission of any object whose type has typecode "tc"; a Pickle.Error will result. By default, Callback objects cannot be transmitted. If an implementor wishes to transmit a Callback, they must implement the pickle function. The reason should be a string like "callback object cannot be transmitted/duplicated". *) END SharedObjStubLib.