Re: [Haskell] Network accept loop with graceful shutdown implementation

2006-12-07 Thread Chris Kuklewicz
Cat Dancer wrote:
 I have a prospective implementation of a network accept loop with
 graceful shutdown.
 

Could you add info about where to get your code (or the code) itself to the wiki
 at http://haskell.org/haskellwiki/Concurrency_demos/Graceful_exit ?

 To avoid the unlock (return ()) issue that Chris discovered, this
 implementation uses an additional MVar to indicate that a shutdown is
 in process.  Thus (if the implementation is correct) the accept loop
 will shutdown either because of the MVar flag or by receiving the
 asynchronous exception inside of the 'accept'.

Small clarification: You don't need a safepoint in your code. But unblock
yield is the right code for a safepoint; the unblock (return ()) suggested by
the published paper *does not work* in my small test, while unblock yield
worked every time in a small test.  Simon may updated the documentation
eventually to reflect this.

 To address the issue that Chris noticed of a race condition that new
 threads cannot be started in a 'block' state, yet another MVar is set
 by the accept thread to indicate that it is now inside of a 'block'
 and is ready to receive the asynchronous exception.
___
Haskell mailing list
Haskell@haskell.org
http://www.haskell.org/mailman/listinfo/haskell


Re: [Haskell] Network accept loop with graceful shutdown implementation

2006-12-07 Thread Cat Dancer

On 12/7/06, Chris Kuklewicz [EMAIL PROTECTED] wrote:

Could you add info about where to get your code (or the code) itself to the wiki
 at http://haskell.org/haskellwiki/Concurrency_demos/Graceful_exit ?


OK, I did.


unblock yield is the right code for a safepoint


Be careful.  You are relying on the runtime to wake up the throwing
thread, to let it run long enough to raise the asynchronous exception,
and to have that complete before the thread doing the unblock yield
is resumed.

While a particular Haskell implementation might do all that on a
single processor system, it's hard to see how such a guarantee could
ever be offered on a multiprocessor system.  These days it's common to
buy a computer with a dual core cpu, before long we'll be getting
cpu's with ten cores... and in a few years we'll be seeing hundred
core cpu's.  yield is meaningless on a multiprocessor system.

I'd suggest that if you're using a concurrent language and you find
that the only way you can implement an algorithm is by using yield,
either A) you're wrong and there is a way to implement it without
yield, or B) your concurrent language is deficient and should be
fixed!  :-)
___
Haskell mailing list
Haskell@haskell.org
http://www.haskell.org/mailman/listinfo/haskell


Re: [Haskell] Network accept loop with graceful shutdown implementation

2006-12-07 Thread Taral

On 12/7/06, Chris Kuklewicz [EMAIL PROTECTED] wrote:

Small clarification: You don't need a safepoint in your code. But unblock
yield is the right code for a safepoint; the unblock (return ()) suggested by
the published paper *does not work* in my small test, while unblock yield
worked every time in a small test.  Simon may updated the documentation
eventually to reflect this.


I think people are misunderstanding the nature of a safepoint. The
safepoint is a point at which you are prepared to have exceptions
delivered. This does not mean that they *will* be delivered, just that
they can. If you need to *wait* for an asynchronous exception, then
you shouldn't be using them at all.

--
Taral [EMAIL PROTECTED]
You can't prove anything.
   -- Gödel's Incompetence Theorem
___
Haskell mailing list
Haskell@haskell.org
http://www.haskell.org/mailman/listinfo/haskell


[Haskell] Network accept loop with graceful shutdown implementation

2006-12-06 Thread Cat Dancer

I have a prospective implementation of a network accept loop with
graceful shutdown.

This email builds upon the previous discussion Help needed
interrupting accepting a network connection.

In this code, just the accept loop part has been factored out and put
into its own module.  My hope is that if a fully correct version can
be written, it will be useful to other projects.  The source is
available at http://code.catdancer.ws/acceptloop/ and is in the public
domain.

This AcceptLoop module is currently experimental, relying on an
either-or guarantee for interruptible operations... which I don't
know yet whether Haskell implementations provide -- or even intend to
provide.


Chris Kuklewicz provided several critical insights:

* The return value of the 'accept' call can be passed out of the
 accept thread, allowing the code which implements accept with
 graceful shutdown to be separated from the code which handles
 the incoming client connection.

* Inside of a 'block', interruptible operations may not (will not?)
 allow an asynchronous exception to be raised if the operation does
 not block.

* Clarification for me of the desirable property that inside of a
 'block', interruptible operations are either-or: either they allow
 an asynchronous operation to be raised, or they perform their
 operation, but not both.


I made the following design decisions:

In my original implementation, I used a custom datatype to throw a
dynamic asynchronous exception to the thread calling 'accept'.  In
Chris' rewrite, he used 'killThread', which throws a 'ThreadKilled'
asynchronous exception.  I choose to continue to throw (and catch)
only the specific, custom exception for the purpose of breaking out of
the 'accept'.  A robust implementation may need to catch other
exceptions, however the desired behavior of the thread on receiving an
*unexpected* exception may be different, and so I choose to leave
handling of such an unexpected exception unimplemented for now.

Chris uses STM instead of MVar's for communication with the accept
thread.  The challenge of writing code with STM or MVar's in the
presence of asynchronous exceptions is exactly the same: either a call
to 'atomically' or a call to an MVar operation such as 'putMVar' may
allow an asynchronous exception to be raised inside of a 'block', and
the code then needs to deal with that.

It seems to me that MVar's could be implemented in terms of STM, and
so the question is: are MVar's a more natural, higher level
description of the desired semantics in this case, and would
composable transactions be useful?

A key insight is that in this implementation, a separate thread is
used not for the purposes of concurrency, but solely to limit the
scope of the thrown asynchronous exception.  Once a result is
available from the accept thread (either Just an incoming
connection, or Nothing to say that the accept loop has shutdown),
that result can then be used in a concurrent fashion, such as being
handled in a child thread, passed into an STM transaction or written
to a channel, etc.  But there is no need to use composable
transactions *inside* of the AcceptLoop module.

Thus the only reason to use STM instead of MVar's inside the
implementation is if it turns out that STM has the desired either-or
behavior but MVar's don't.

To avoid the unlock (return ()) issue that Chris discovered, this
implementation uses an additional MVar to indicate that a shutdown is
in process.  Thus (if the implementation is correct) the accept loop
will shutdown either because of the MVar flag or by receiving the
asynchronous exception inside of the 'accept'.

To address the issue that Chris noticed of a race condition that new
threads cannot be started in a 'block' state, yet another MVar is set
by the accept thread to indicate that it is now inside of a 'block'
and is ready to receive the asynchronous exception.
___
Haskell mailing list
Haskell@haskell.org
http://www.haskell.org/mailman/listinfo/haskell