[Haskell-cafe] Yet another top-level IO proposal

Robert Dockins robdockins at fastmail.fm
Tue Jan 17 20:44:39 EST 2006


> Hello,
>
> Well it seems like you haven't started another flame war (yet :-).

Indeed; I am a little surprised to hear the silence.

> I'm afraid I haven't properly understood your proposal, because I
> don't have much time right now. It seems to be a bit like George
> Russels proposal (aka "execution contexts").

Somewhat.  However, execution contexts don't have the initializer concept.

> Personally I have never felt the need for thread local state, but
> I have often needed to use the unsafePerformIO hack to create
> *unique state* for API's that are both sane from a users point of
> view and are also invulnerable to accidental or malicious state
> "spoofing". So thread local state isn't really what I want (it's
> a sure way to guarantee that spoofing will occur :-)
>
> You seem to indicate that this is still possible with your scheme,
> but I'm not sure of the details.

If you declare a thread-local in a module but don't export it, then you have 
exclusive control over what happens to that thread-local.  You set the 
initializer and no one else can touch it.  If you set an initializer that 
copies parent values and never write to the cell, you effectively have a 
variable that is set exactly once at program start.

The only way to alter this from outside the module is to use the "clearBank" 
primitive, which resets all thread-locals to empty.  It may be that this 
primitive is too dangerous to include.

On the other hand, I'm not convinced that absolutely unique state is that 
great.  Suppose I want to run multiple copies of my Haskell OS in an emulator 
so I can test the TCP/IP stack I just wrote? I'll need some way to keep the 
"unique" state for each OS separate.

> Maybe you should put all this 
> on the wiki page. I'd like to see how/if you could implement
> the hypothetical device driver API I put there, or even just
> use the "oneShot" function or similar at the top level.

I've attached a hypothetical implementation in the proposed syntax.

> Regards
> --
> Adrian Hey

Robert Dockins
-------------- next part --------------
module FictionalDevice
( device1
, device2
, DeviceHandle
, initDevice
, someDeviceAction
)

import Control.Concurrent
import Control.Concurrent.MVar

type BaseAddress = ...

data DeviceState = 
  DeviceState 
  { isFirstAccess :: Bool
  , baseAddress   :: BaseAddress
  , ...
  }

initialDeviceState :: BaseAddress -> DeviceState
initialDeviceState addr = DeviceState
  { isFirstAccess = True
  , baseAddress   = addr
  , ...
  }

device1baseAddresss :: BaseAddress
device1baseAddress = ...

device2baseAddresss :: BaseAddress
device2baseAddress = ...

threadlocal device1state (MVar DeviceState)
   (initTL (newMVarTL (initialDeviceState device1baseAddress)))

threadlocal device2state (MVar DeviceState)
   (initTL (newMVarTL (initialDeviceState device2baseAddress)))

newtype DeviceHandle = DH (TLRef (MVar DeviceState))

device1 = DH device1state
device2 = DH device2state

doInitDevice :: BaseAddress -> IO ()
doInitDevice addr = ...

initDevice :: DeviceHandle -> IO ()
initDevice (DH ref) = do
  stMvar <- readThreadLocal ref
  modifyMVar_ stMVar (\st -> do
    when (isFirstAccess st) (doInitDevice (baseAddress st))
    return st{ isFirstAccess = False })
  
someDeviceAction :: DeviceHandle -> IO ()
someDeviceAdction h@(DH ref) = do
  initDevice h
  ...


More information about the Haskell-Cafe mailing list