-
Notifications
You must be signed in to change notification settings - Fork 43
Distribution
Links has for some time incorporated process-based, actor-ish concurrency, and more recently, has incorporated session-typed channels. Both of these have worked on the client, and on the server.
However, while the abstractions themselves imply location transparency (for example, if we have a process ID, we should be able to send to it irrespective of where the process is located), there has until now existed a "barrier" between different concurrency runtimes. The only way of doing client-server communication has been RPC calls, which are very handy for some things, but less so for others.
The distribution patches aim to break this barrier. It is now possible to use channel- and actor-based processes across this gap.
Here is a program which spawns a loop on the server, which response with a "Hello" value to any request it receives.
module Client {
fun mainPage(serverPid) {
var _ = spawnClient {
fun loop(n) {
var ourPid = self();
if (n > 0) {
serverPid ! Hi(ourPid);
receive { case x -> print("Received hello!"); loop(n - 1) }
} else {
print("Done!");
}
}
loop(5)
};
page
<html>
<h1>Hi!</h1>
</html>
}
}
module Server {
fun go() {
spawn {
fun loop() {
receive {
case Hi(pid) -> pid ! Hello; loop()
}
}
loop()
}
}
}
fun main() {
var serverPid = Server.go();
addRoute("/", fun (_, _) { Client.mainPage(serverPid) } );
serveWebsockets();
servePages()
}
main()
The Client
module has a mainPage
which creates a page, given a server process ID.
spawnClient
ensures a process is spawned on the client. Here, we spawn a process which loops 5 times: the process retrieves its own PID using self()
, sends a message to the server, and blocks waiting for a response. The server hears the response and the PID, and sends back a Hello
message. The client receives the response, decrements the counter, and loops.
The main extra bit of work to be done here is to call serveWebsockets()
before servePages()
, in order to initialise the websocket logic which underpins this.
module Sessions {
typename Ping = [| Ping |];
typename Pong = [| Pong |];
typename PingPong =
?(Ping).!(Pong). [&| Again: PingPong, Stop: End |&];
}
open Sessions
module Client {
fun mainPage(ap) {
var _ = spawnClient {
var c = request(ap);
fun loop(n, c) {
var ourPid = self();
var c = send (Ping, c);
var (_, c) = receive(c);
print("Received pong!");
if (n > 0) {
var c = select Again c;
loop(n - 1, c)
} else {
var _ = select Stop c;
print("Done!");
}
}
loop(5, c)
};
page
<html>
<h1>Hi!</h1>
</html>
}
}
module Server {
fun clientLoop(c) {
var (_, c) = receive(c);
print("Received ping!");
var c = send(Pong, c);
offer(c) {
case Again(c) -> clientLoop(c);
case Stop(c) -> ()
}
}
fun go(ap) {
spawn {
fun loop() {
var c = accept(ap);
clientLoop(c);
loop()
}
loop()
}
}
}
fun main() {
var ap = (new() : AP(PingPong));
var _ = Server.go(ap);
addRoute("/", fun (_, _) { Client.mainPage(ap) } );
serveWebsockets();
servePages()
}
main()
We can adapt the previous example to use session types. We firstly describe (in the Sessions
module) a session type (from the view of the server) which receives a ping, receives a pong, and then receives a decision whether to loop and go again, or to stop.
In main()
, we create an "access point" called ap
using the new
command. Note that while we choose to give this a type annotation, this isn't actually necessary---it helps with documentation and debugging, however. An access point is a "matchmaking service". In this case, the server gets the "server end" by passing ap
as an argument to accept
in the go
function, whereas the client (running in the browser) gets the "client end" by passing ap
as an argument to request
.
You can find a distributed chat server in examples/distribution/chatserver
. Run it from that directory to get styles to work.
-
We don't do anything fancy when a client goes away mid-session. This is the subject of ongoing research, but we're looking to repurpose Affine Sessions (Mostrous & Vasconcelos, 2014).
-
Access points for sessions can only reside on the server at the moment.
-
Multiparty sessions