The Session
module provides a communication channel respecting this set of properties:
A Session
instance obtained by Session.new
represents one side of a communication.
When two instances are connected together, they offer a bidirectional communication channel between these two points. Messages are received on a stream. There is only one reception stream per session instance that receives all messages from all successive connections of this instance.
At maximum two instances can be connected together at any time.
Any one of the two sides of a session communication channel can break the connection. Each side can then be connected back again together or to other session instances.
Each session instance also provides a free-for-all communication channel where anyone can send messages freely without having to connect. These messages are received on a distinctive stream. For example, an application wants to provide a remote control to several remote clients, but only one at a time. With the Session
module, the application can use a single session instance to let only one remote client connect at a time. When the client wants to stop working with the application, it can stop the session, and another client can then take over. If a client is already connected to the application and another one wants to connect to it also, it can use the free-for-all channel to notify the application so. If there are several applications the client wants to connect to successively, it can do so using a single session instance.
Each session instance produces a link state stream. Connections, change of the communication link with the currently connected session and disconnections generate messages on that stream. This can be used to create fault-tolerant applications without using the Fault
module. The possible messages on that stream are:
connect
: this session instance has just been connected to another session instance. Messages can be sent to the other site.
disconnect
: this session instance has been disconnected by the other session instance. No message can be sent while disconnected.
tempFail
: there is a communication problem that temporarily prevents the messages from arriving to the other site. A tempFail
may change into permFail
or ok
, or stays forever (until the site decides to break the session). Note that the application can still send messages in this state, they will be buffered and send correctly if the communication turns ok
or dropped if the communication is broken.
permFail
: the other site was detected crashed. It is the same as disconnect
, except that there is no use to try and contact the other site anymore: it is dead forever.
ok
: after a tempFail
, this state means the communication is fine again, messages are normally arriving at the other site.
This module also offers a session gate, which is a single access point for spawning multiple sessions. Typically, a server can start a gate that will create a different session per client. When a client has finished to work with the server, the session is closed and no more communication is possible between them. However, the client can use the gate again to create a new session with the server.
This module doesn't provide an access control over the session instances and the gates. The application should define its own using the Connection
and Ticket
modules, or the SocketConnection
module.
First example: a client-server implementation.
%% Server side
proc{Server Msg}
... %% insert here the code that responds to Msg
end
L
G={Session.newGate L}
{SocketConnection.offerOn 5000 G}
{ForAll L
proc{$ S}
thread
{ForAll {Session.getStream S}
proc{$ M} {Server M} end}
end
end}
%% client side
G={SocketConnection.take IP 5000} %% IP contains the IP of the server
S={Session.gateConnect G} %% S is a session connected to the server
{S.aSend run} %% sends the run message to the server
...
Second example: a timer server that sends a tick every second. Only one client can connect at a time.
%% server side
S={Session.new}
{SocketConnection.offerOn 5000 S}
thread
proc{Tick}
try {Session.aSend S tick} catch _ then skip end
{Delay 1000} {Tick}
end
in
{Tick}
end
%% client side
LS={Session.new}
{Browse {Session.getStream LS}}
RS={SocketConnection.take IP 5000}
{Session.connect LS RS} %% will fail if the server is not free
...
{Session.disconnect S} %% disconnects from the server => another client can take over
Warning: local and remote session instances play a different role in the functions and procedures below. Except when otherwise stated, the first parameter is always a locally created session. Let's assume Offer
is a function that makes an Oz entity remotely available, and Obtain
the function that gets the offered reference:
|
|
---|---|
|
|
|
|
|
|
At this stage, SiteB
has the remote reference SR
to SA
of SiteA
. SiteB
can connect both sessions by:
{Session.connect SB SR}
However swapping the parameters is invalid:
{Session.connect SR SB}
The first parameter of Session.connect
must be session local to SiteB
, which is not the case here.
is
{Session.is
+X
?B
}
tests whether X
is a Session
. X
can be a remote session.
new
{Session.new
?S
}
returns a new session instance.
connect
{Session.connect
S1
S2
}
Connects S1
with S2
. Raises an exception if S1
or S2
are already connected to another session. Raises an exception if S2
is unreachable.
disconnect
{Session.disconnect
S
}
Disconnects S
to whatever other session it was connected to. This is a synchronous disconnection, ie it waits for all pending messages to arrive before disconnecting. In case of permFail
, the disconnection waits for a change of the communication status. If it resolves to ok
, all pending messages are sent before disconnecting. If it resolves to permFail
or disconnect
, the disconnection is immediate, and pending messages may be lost in the process.
break
{Session.break
S
}
Disconnects S
to whatever other session it was connected to. This is an as soon as possible disconnection: if the communication state was ok
then all sent messages are garanteed to arrive before the disconnection. On the contrary, if the communication was in the permFail
state, the disconnection is immediate and pending messages may be lost in the process. Note that break
is the only way to terminate a session if it goes to permFail
for an infinite amount of time.
getStream
{Session.getStream
S
?Xs
}
Returns the stream of messages sent by sessions connected to S
. The returned stream doesn't start from the creation of S
but from the moment it is asked. For that reason, it is often better to getStream
before connecting to another session, otherwise messages might be lost.
getStateStream
{Session.getStateStream
S
?Xs
}
Returns the stream of communication states of S
with the other sessions it is successively connected to. The returned stream doesn't start from the creation of S
, but from the last known communication state change. So Xs
has the form C
|Cs
where C
is the current communication state of S
and Cs
is the stream that will receive the updates when that state changes. This stream can be composed of one of the following atoms: connect
, disconnect
, tempFail
, ok
, permFail
.
getSideStream
{Session.getSideStream
S
?Xs
}
Returns the free-for-all stream of S
. There is no need to connect to S
to send an element to this stream. The returned stream doesn't start from the creation of S
but from the moment it is asked. Elements of Xs
are composed of pairs RS#M
where RS
is a reference to the session object used to send the message M
.
getState
{Session.getState
S
?T
}
Returns current state of S
, ie {Session.getStateStream
S
}.1
.
getPeer
{Session.getPeer
S1
?S2
}
Returns the session instance S1
is currently connected with, or unit
if S1
is not currently connected.
sSend
{Session.sSend
S
M
}
Sends M
to the reception stream of the session S
is connected to in a synchronous way: this command blocks until this message has arrived on the reception stream of the other site. Raises an exception if S
is not currently connected.
aSend
{Session.aSend
S
M
}
Sends M
to the reception stream of the session S
is connected to. Doesn't block. Raises an exception if S
is not currently connected.
lSend
{Session.lSend
S
M
}
Sends M
to the reception stream of S
itself.
sideSend
{Session.sideSend
S1
S2
M
}
Sends M
to the free-for-all reception stream of S2
. S2
receives the pair S1#M
on his free-for-all stream. Nothing happens if S2
is unreachable.
bind
{Session.bind
S
Event
Proc
}
This procedure provides an event based way of managing communication state changes. Event
must be one of these atoms: connect
, disconnect
, tempFail
, ok
, permFail
. Proc
is a zero parameter procedure that is executed each time the state change specified by Event
occurs on S
.
configure
{Session.configure
S
Param
Value
}
Changes the paramater Param
of S
to Value
. Currently, the only configurable parameter is:
autoBreakAfter
: either an integer representing milliseconds, or the atom inf
for an infinite time (default). Specifies a time to wait before automatically breaking a session when a permFail
occurs.
isConnected
{Session.isConnected
S
?B
}
Returns true
if S
is currently connected, false
otherwise.
wait
{Session.wait[Ok|NotOk|Connect|Disconnect]
S
}
{Session.wait[Ok|NotOk|Connect|Disconnect]Or
S
V
}
{Session.wait[Ok|NotOk|Connect|Disconnect]Ors
S
LV
}
If the communication status of S
is not in the specified state, then blocks until it is. Supported states are:
Ok
means S
is connected to another session instance and the link between them is fine.
NotOk
in all situations that are not Ok
.
Connect
means S
is connected to another session instance.
Disconnect
means S
is not connected to another session instance.
The Or
flavour also specifies a logic variable V
and blocks until the specified state occurs, or V
is determined. The Ors
flavour also specifies a list logic variable LV
and blocks until the specified state occurs, or any of the elements of LV
is determined.
isGate
{Session.isGate
+X
?B
}
tests whether X
is a Gate
.
newGate
{Session.newGate
?S
?G
}
returns a new gate and binds S
to a list of futures. Each time a connection is created on this gate by Session.gateConnect
, the next element of S
is bound to the local session created for the communication.
gateConnect
{Session.gateConnect
G
?S
}
returns a local session S
connected to a session created by the gate G
.
closeGate
{Session.closeGate
G
}
Stops the gate G
from accepting new connections and closes the associated list of sessions. G
must be a local gate.