summaryrefslogtreecommitdiff
path: root/demo
diff options
context:
space:
mode:
authorGravatar Adam Chlipala <adamc@hcoop.net>2009-04-05 11:24:55 -0400
committerGravatar Adam Chlipala <adamc@hcoop.net>2009-04-05 11:24:55 -0400
commita44e147bbb6686867b425b7cf068d14c5f230f51 (patch)
tree629c88474e3f44efa4384f199ee22debb4e240bd /demo
parent18b7332e738d4b97bf3bb9dfa6600277222aab33 (diff)
RoundTrip demo
Diffstat (limited to 'demo')
-rw-r--r--demo/prose10
-rw-r--r--demo/roundTrip.ur29
-rw-r--r--demo/roundTrip.urp5
-rw-r--r--demo/roundTrip.urs1
4 files changed, 45 insertions, 0 deletions
diff --git a/demo/prose b/demo/prose
index 80113c3e..45b7be00 100644
--- a/demo/prose
+++ b/demo/prose
@@ -246,3 +246,13 @@ threads.urp
<p>We specify some client-side code to run on page load using the <tt>onload</tt> attribute of <tt>&lt;body&gt;</tt>. The <tt>onload</tt> code in this example spawns two separate threads running the <tt>loop</tt> code with different prefixes, update intervals, and starting counters.</p>
<p>Old hands at concurrent programming may be worried at the lack of synchronization in this program. Ur/Web uses <i>cooperative multi-threading</i>, not the more common <i>preemptive</i> multi-threading. Only one thread runs at a time, and only particular function calls can trigger context switches. In this example, <tt>sleep</tt> is the only such function that appears.</p>
+
+roundTrip.urp
+
+<p>So far, we've seen examples of client-side code triggering the execution of server-side code. Such remote calls only happen in response to client-side events. It is often useful to allow a client to trigger events on other clients, and Ur/Web facilitates this with a simple asynchronous message-passing facility. The current example introduces the basics of message-passing with a trivial use case, and the next example shows a more realistic case where several clients can communicate.</p>
+
+<p>We are going to provide a silly service where a client can send messages to the server, which the server then echoes back to the client. The SQL table <tt>channels</tt> stores a mapping from client IDs to message channels. The abstract type <tt>client</tt> holds unique client IDs, which Ur/Web generates automatically as needed. A <tt>channel <i>T</i></tt> is a channel to which messages of type <tt><i>T</i></tt> can be sent. Every channel belongs to a single client; anyone can send to a channel, but only the channel's owner can read the messages. Every client is associated with a particular open page on a particular web browser somewhere. Since web browsing sessions are ephemeral, clients and their channels are garbage-collected automatically as the web server loses contact with browsers. When a client is garbage-collected, any database row mentioning it or one of its channels is deleted. It's also possible to include <tt>option client</tt>s (and likewise for channels) in databases, in which case such columns are merely nulled out when they refer to dead clients.</p>
+
+<p>The <tt>main</tt> function begins by retrieving the current client ID, allocating a new channel, and associating that channel with the current client in the database. Next, we allocate a buffer and return the page, which in its <tt>onload</tt> attribute starts two loops running in parallel. In contrast to in the last example, here we only use <tt>spawn</tt> with the call to the first loop, since every client-side event handler is implicitly started in a new thread.</tt>
+
+<p>The first loop, <tt>receiver</tt>, repeatedly reads messages from the channel and writes them to the buffer. The second loop, <tt>sender</tt>, periodically sends messages to the channel. Client code can't send messages directly. Instead, we must use server-side functions to do the sending. Clients aren't trusted to pass channels to the server, so our server-side function <tt>writeBack</tt> instead keys off of the client ID, looking up the corresponding channel in the database.</p>
diff --git a/demo/roundTrip.ur b/demo/roundTrip.ur
new file mode 100644
index 00000000..a2be8083
--- /dev/null
+++ b/demo/roundTrip.ur
@@ -0,0 +1,29 @@
+table channels : { Client : client, Channel : channel (string * int * float) }
+
+fun writeBack v =
+ me <- self;
+ r <- oneRow (SELECT channels.Channel FROM channels WHERE channels.Client = {[me]});
+ send r.Channels.Channel v
+
+fun main () =
+ me <- self;
+ ch <- channel;
+ dml (INSERT INTO channels (Client, Channel) VALUES ({[me]}, {[ch]}));
+
+ buf <- Buffer.create;
+
+ let
+ fun receiver () =
+ v <- recv ch;
+ Buffer.write buf ("(" ^ v.1 ^ ", " ^ show v.2 ^ ", " ^ show v.3 ^ ")");
+ receiver ()
+
+ fun sender s n f =
+ sleep 2000;
+ writeBack (s, n, f);
+ sender (s ^ "!") (n + 1) (f + 1.23)
+ in
+ return <xml><body onload={spawn (receiver ()); sender "" 0 0.0}>
+ <dyn signal={Buffer.render buf}/>
+ </body></xml>
+ end
diff --git a/demo/roundTrip.urp b/demo/roundTrip.urp
new file mode 100644
index 00000000..37de3812
--- /dev/null
+++ b/demo/roundTrip.urp
@@ -0,0 +1,5 @@
+database dbname=test
+sql roundTrip.sql
+
+buffer
+roundTrip
diff --git a/demo/roundTrip.urs b/demo/roundTrip.urs
new file mode 100644
index 00000000..6ac44e0b
--- /dev/null
+++ b/demo/roundTrip.urs
@@ -0,0 +1 @@
+val main : unit -> transaction page