|
|
|
# Background
|
|
|
|
|
|
|
|
Even though we most likely won't want to make concurrency part of the
|
|
|
|
language. perhaps we should consider adding thread annotations to the
|
|
|
|
FFI spec now, so that C bindings with thread annotations can be portable
|
|
|
|
(even if they don't actually make use of concurrency).
|
|
|
|
|
|
|
|
|
|
|
|
In the FFI, there are two ways to annotate a foreign call:
|
|
|
|
|
|
|
|
Now, if I understand the situation with ghc, it has two ways of binding
|
|
|
|
something
|
|
|
|
|
|
|
|
- **safe** (the default). This means the foreign call can invoke a foreign export,
|
|
|
|
and hence re-enter Haskell.
|
|
|
|
|
|
|
|
- **unsafe**. The programmer guarantees that the foreign call cannot re-enter Haskell,
|
|
|
|
and the implementation may use this information to optimize the call.
|
|
|
|
|
|
|
|
safe - might block, might call back into the haskell runtime
|
|
|
|
unsafe - won't block, won't call back into the haskell runtime.
|
|
|
|
|
|
|
|
Additionally in GHC, "safe" means that the call should not prevent other Haskell threads from making progress. It indicates a *concurrent foreign call* (see [Concurrency](concurrency)).
|
|
|
|
|
|
|
|
|
|
|
|
while this is fine for ghc, I was hoping we could separate out the
|
|
|
|
mechanisms of whether a routine will block and whether it is "safe".
|
|
|
|
|
|
|
|
If the standard is to include concurrency, then it is a significant burden to include the full functionality of "safe" foreign calls as implemented by GHC as a requirement. It would imply that the runtime must be able to handle multi-threaded call-in, that is, call-ins from multiple OS threads simultaneously. In contrast, if the implementation only has to handle call-ins that occur during a synchrounous call-out, the implementation is much simpler.
|
|
|
|
|
|
|
|
|
|
|
|
so, I suggest we keep 'safe' and 'unsafe' as they are in the report, and
|
|
|
|
add 'blockable' meaning the routine might block indefinitly and if the
|
|
|
|
implementation uses concurrency then it should take efforts to ensure it
|
|
|
|
does not block the whole runtime.
|
|
|
|
# Proposal
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
in particular, the common case of 'blockable unsafe' can be implemented
|
|
|
|
particularly simply compared to 'blockable safe' since we only need to
|
|
|
|
spawn off a pthread for the routine and have it write a status byte to a
|
|
|
|
pipe when done that can be handled via the standard select mechanism of
|
|
|
|
an implementation.
|
|
|
|
There are simpler positions that can be adopted that reduce the requirements on the implementation. The current proposal is this:
|
|
|
|
|
|
|
|
|
|
|
|
- **concurrent** indicates a foreign call that runs concurrently with other running Haskell threads.
|
|
|
|
|
|
|
|
so, in any case, the point of adding it to the standard is so that
|
|
|
|
everyone can annotate their functions 'blockable' and implementations
|
|
|
|
without concurrency will know to ignore it and not die with an error.
|
|
|
|
- **nonreentrant** (**pure** has also been suggested) indicates that the foreign call does not invoke
|
|
|
|
and Haskell code.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for GHC it would be as simple as just treating 'blockable' as implying
|
|
|
|
'safe' and perhaps someone could implement 'blockable unsafe' for ghc
|
|
|
|
someday so that it won't require -threaded for (many? all?) blockable
|
|
|
|
routines.
|
|
|
|
and the standard would only require that concurrent is supported with nonreentrant; concurrent alone is an extension. The standard would also clarify that multi-threaded call-in is not a requirement.
|
|
|
|
|
|
|
|
|