Skip to content

Commit ef0b071

Browse files
committed
Merge haskell-distributed.gitub.com/development
1 parent 6b84752 commit ef0b071

14 files changed

+1415
-725
lines changed

Diff for: Gemfile

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
source 'https://rubygems.org'
2+
gem 'github-pages'

Diff for: Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,4 @@ endif
2121

2222
.PHONY: serve
2323
serve:
24-
jekyll --pygments --no-lsi --safe --server
24+
jekyll serve -w

Diff for: _config.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
exclude: [".rvmrc", ".rbenv-version", "README.md", "Rakefile", "changelog.md"]
2-
auto: true
32
lsi: false
3+
auto: true
44
pygments: true
55
safe: true
66

@@ -13,4 +13,4 @@ author:
1313
github: hyperthunk
1414
twitter: hyperthunk
1515

16-
production_url: http://haskell-distributed.github.com/distributed-process-platform
16+
production_url: http://hyperthunk.github.com/website-next-preview

Diff for: _layouts/tutorial1.html renamed to _layouts/tutorial.html

+5-3
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@
2121
<div class="span3 sidebar">
2222
<div data-spy="affix" data-offset-bottom="290">
2323
<ul class="nav nav-list sidenav">
24-
<li><a href="#create_a_node"><i class="icon-chevron-right"></i> Create a node</a></li>
25-
<li><a href="#send_messages"><i class="icon-chevron-right"></i> Send messages</a></li>
26-
<li><a href="#spawning_remote_processes"><i class="icon-chevron-right"></i> Spawning Remote Processes</a></li>
24+
{% for section in page.sections %}
25+
<li>
26+
<a href="#{{ section | downcase | replace: ' ', '_' }}"><i class="icon-chevron-right"></i> {{ section }}</a>
27+
</li>
28+
{% endfor %}
2729
</ul>
2830
</div>
2931
</div>

Diff for: _layouts/tutorial3.html

-42
This file was deleted.

Diff for: documentation.md

+55-83
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ layout: documentation
33
title: Documentation
44
---
55

6-
### Cloud Haskell
6+
### Cloud Haskell Platform
77

8-
This is [*Cloud Haskell*][1]. Cloud Haskell is a set of libraries
8+
This is the [*Cloud Haskell Platform*][1]. Cloud Haskell is a set of libraries
99
that bring Erlang-style concurrency and distribution to Haskell programs. This
1010
project is an implementation of that distributed computing interface, where
1111
processes communicate with one another through explicit message passing rather
@@ -238,14 +238,14 @@ types such as `TMVar` just as normal Haskell threads would.
238238
### Typed Channels
239239

240240
Channels provides an alternative to message transmission with `send` and `expect`.
241-
While `send` and `expect` allow transmission of messages of any `Serializable`
241+
While `send` and `expect` allow us to transmit messages of any `Serializable`
242242
type, channels require a uniform type. Channels work like a distributed equivalent
243243
of Haskell's `Control.Concurrent.Chan`, however they have distinct ends: a single
244244
receiving port and a corollary send port.
245245

246246
Channels provide a nice alternative to *bare send and receive*, which is a bit
247-
*unHaskellish*, because the processes message queue has messages of multiple
248-
types, and we have to do dynamic type checking.
247+
*un-Haskell-ish*, since our process' message queue can contain messages of multiple
248+
types, forcing us to undertake dynamic type checking at runtime.
249249

250250
We create channels with a call to `newChan`, and send/receive on them using the
251251
`{send,receive}Chan` primitives:
@@ -264,19 +264,17 @@ channelsDemo = do
264264
{% endhighlight %}
265265

266266
Channels are particularly useful when you are sending a message that needs a
267-
response, because the code that receives the response knows exactly where it
268-
came from - i.e., it knows that it came from the `SendPort` connected to
269-
the `ReceivePort` on which it just received a response.
267+
response, because we know exactly where to look for the reply.
270268

271-
Channels can sometimes allows message types to be simplified, as passing a
272-
`ProcessId` to reply to isn't required. Channels are not so useful when you
273-
need to spawn a process and then send a bunch a messages to it and wait for
274-
replies, because we can’t send the `ReceivePort`.
269+
Channels can also allow message types to be simplified, as passing a
270+
`ProcessId` for the reply isn't required. Channels aren't so useful when we
271+
need to spawn a process and send a bunch a messages to it, then wait for
272+
replies however; we can’t send a `ReceivePort` since it is not `Serializable`.
275273

276-
ReceivePorts can be merged, so you can listen on several simultaneously. In the
277-
latest version of [distributed-process][2], you can listen for *regular* messages
278-
and on multiple channels at the same time, using `matchChan` in the list of
279-
allowed matches passed `receive`.
274+
`ReceivePort`s can be merged, so we can listen on several simultaneously. In the
275+
latest version of [distributed-process][2], we can listen for *regular* messages
276+
and multiple channels at the same time, using `matchChan` in the list of
277+
allowed matches passed `receiveWait` and `receiveTimeout`.
280278

281279
### Linking and monitoring
282280

@@ -289,71 +287,27 @@ a set of children, starting, stopping and restarting them as necessary.
289287

290288
### Stopping Processes
291289

292-
Some processes, like the *outer* process in the previous example, will run until
293-
they've completed and then return their value. This is just as we find with IO action,
294-
and there is an instance of `MonadIO` for the `Process` monad, so you can `liftIO` if
295-
you need to evaluate IO actions.
296-
297290
Because processes are implemented with `forkIO` we might be tempted to stop
298291
them by throwing an asynchronous exception to the process, but this is almost
299-
certainly the wrong thing to do. Instead we might send a kind of poison pill,
300-
which the process *ought* to handle by shutting down gracefully. Unfortunately
301-
because of the asynchronous nature of sending, this is no good because `send`
302-
will not fail under any circumstances. In fact, because `send` doesn't block,
303-
we therefore have no way to know if the recipient existed at the time we sent the
304-
poison pill. Even if the recipient did exist, we still have no guarantee that
305-
the message we sent actually arrived - the network connection between the nodes
306-
could have broken, for example. Making this *shutdown* protocol synchronous is
307-
no good either - how long would we wait for a reply? Indefinitely?
308-
309-
Exit signals come in two flavours - those that can
310-
be caught and those that cannot. A call to
311-
`exit :: (Serializable a) => ProcessId -> a -> Process ()` will dispatch an
312-
exit signal to the specified process. These *signals* can be intercepted and
313-
handled by the destination process however, so if you need to terminate the
314-
process in a brutal way, you can use the `kill :: ProcessId -> String -> Process ()`
315-
function, which sends an exit signal that cannot be handled.
316-
317-
------
318-
#### __An important note about exit signals__
319-
320-
Exit signals in Cloud Haskell are unlike asynchronous exceptions in regular
321-
haskell code. Whilst processes *can* use asynchronous exceptions - there's
322-
nothing stoping this since the `Process` monad is an instance of `MonadIO` -
323-
exceptions thrown are not bound by the same ordering guarantees as messages
324-
delivered to a process. Link failures and exit signals *might* be implemented
325-
using asynchronous exceptions - that is the case in the current
326-
implementation - but these are implemented in such a fashion that if you
327-
send a message and *then* an exit signal, the message is guaranteed to arrive
328-
first.
329-
330-
You should avoid throwing your own exceptions in code where possible. Instead,
331-
you should terminate yourself, or another process, using the built-in primitives
332-
`exit`, `kill` and `die`.
333-
334-
{% highlight haskell %}
335-
exit pid reason -- force `pid` to exit - reason can be any `Serializable` message
336-
kill pid reason -- reason is a string - the *kill* signal cannot be caught
337-
die reason -- as 'exit' but kills *us*
338-
{% endhighlight %}
339-
340-
The `exit` and `kill` primitives do essentially the same thing, but catching
341-
the specific exception thrown by `kill` is impossible, making `kill` an
342-
*untrappable exit signal*. Of course you could trap **all** exceptions, but
343-
you already know that's a very bad idea right!?
344-
345-
The `exit` primitive is a little different. This provides support for trapping
346-
exit signals in a generic way, so long as your *exit handler* is able to
347-
recognise the underlying type of the 'exit reason'. This (reason for exiting)
348-
is stored as a raw `Message`, so if your handler takes the appropriate type
349-
as an input (and therefore the `Message` can be decoded and passed to the
350-
handler) then the handler will run. This is pretty much the same approach as
351-
exception handling using `Typeable`, except that we decide whether or not the
352-
exception can be handled based on the type of `reason` instead of the type of
353-
the exception itself.
354-
355-
Calling `die` will immediately raise an exit signal (i.e., `ProcessExitException`)
356-
in the calling process.
292+
certainly the wrong thing to do. Firstly, processes might reside on a remote
293+
node, in which case throwing an exception is impossible. Secondly, if we send
294+
some messages to a process' mailbox and then dispatch an exception to kill it,
295+
there is no guarantee that the subject will receive our message before being
296+
terminated by the asynchronous exception.
297+
298+
To terminate a process unconditionally, we use the `kill` primitive, which
299+
dispatches an asynchronous exception (killing the subject) safely, respecting
300+
remote calls to processes on disparate nodes and observing message ordering
301+
guarantees such that `send pid "hello" >> kill pid "goodbye"` behaves quite
302+
unsurprisingly, delivering the message before the kill signal.
303+
304+
Exit signals come in two flavours however - those that can be caught and those
305+
that cannot. Whilst a call to `kill` results in an _un-trappable_ exception,
306+
a call to `exit :: (Serializable a) => ProcessId -> a -> Process ()` will dispatch
307+
an exit signal to the specified process that can be caught. These *signals* are
308+
intercepted and handled by the destination process using `catchExit`, allowing
309+
the receiver to match on the `Serializable` datum tucked away in the *exit signal*
310+
and decide whether to oblige or not.
357311

358312
----
359313

@@ -373,7 +327,7 @@ The [distributed-process-platform][18] library implements parts of the
373327
in the original paper and implemented by the [remote][14] package. In particular,
374328
we diverge from the original design and defer to many of the principles
375329
defined by Erlang's [Open Telecom Platform][13], taking in some well established
376-
Haskell concurrency design patterns alongside.
330+
Haskell concurrency design patterns along the way.
377331

378332
In fact, [distributed-process-platform][18] does not really consider the
379333
*task layer* in great detail. We provide an API comparable to remote's
@@ -465,6 +419,12 @@ The API for `Async` is fairly rich, so reading the haddocks is suggested.
465419

466420
#### Managed Processes
467421

422+
The main idea behind a `ManagedProcess` is to separate the functional
423+
and non-functional aspects of an actor. By functional, we mean whatever
424+
application specific task the actor performs, and by non-functional
425+
we mean the *concurrency* or, more precisely, handling of the process'
426+
mailbox and its interaction with other actors (i.e., clients).
427+
468428
Looking at *typed channels*, we noted that their insistence on a specific input
469429
domain was more *haskell-ish* than working with bare send and receive primitives.
470430
The `Async` sub-package also provides a type safe interface for receiving data,
@@ -473,12 +433,12 @@ although it is limited to running a computation and waiting for its result.
473433
The [Control.Distributed.Processes.Platform.ManagedProcess][21] API provides a
474434
number of different abstractions that can be used to achieve similar benefits
475435
in your code. It works by introducing a standard protocol between your process
476-
and the *world around*, which governs how to handle request/reply processing,
477-
exit signals, timeouts, sleep/hibernation with `threadDelay` and even provides
436+
and the *world outside*, which governs how to handle request/reply processing,
437+
exit signals, timeouts, sleeping/hibernation with `threadDelay` and even provides
478438
hooks that terminating processes can use to clean up residual state.
479439

480440
The [API documentation][21] is quite extensive, so here we will simply point
481-
out the obvious differences. A implemented implemented with `ManagedProcess`
441+
out the obvious differences. A process implemented with `ManagedProcess`
482442
can present a type safe API to its callers (and the server side code too!),
483443
although that's not its primary benefit. For a very simplified example:
484444

@@ -516,6 +476,18 @@ just provides callback functions which take some state and either return a
516476
new state and a reply, or just a new state. The process is *managed* in the
517477
sense that its mailbox is under someone else's control.
518478

479+
A NOTE ABOUT THE CALL API AND THAT IT WILL FAIL (WITH UNHANDLED MESSAGE) IF
480+
THE CALLER IS EXPECTING A TYPE THAT DIFFERS FROM THE ONE THE SERVER PLANS
481+
TO RETURN, SINCE THE RETURN TYPE IS ENCODED IN THE CALL-MESSAGE TYPE ITSELF.
482+
483+
TODO: WRITE A TEST TO PROVE THE ABOVE
484+
485+
TODO: ADD AN API BASED ON SESSION TYPES AS A KIND OF MANAGED PROCESS.....
486+
487+
In a forthcoming tutorial, we'll look at the `Control.Distributed.Process.Platform.Task`
488+
API, which looks a lot like `Async` but manages exit signals in a single thread and makes
489+
configurable task pools and task supervision strategy part of its API.
490+
519491
More complex examples of the `ManagedProcess` API can be seen in the
520492
[Managed Processes tutorial][22]. API documentation for HEAD is available
521493
[here][21].

0 commit comments

Comments
 (0)