FFI, safe vs unsafe

Wolfgang Thaller wolfgang.thaller at gmx.net
Wed Apr 12 19:35:22 EDT 2006

John Meacham wrote:
> This doesn't have to do with bound threads, [...]

I brought it up because the implementation you are proposing  
fullfills the most important feature provided by bound threads,  
namely to be able to access the thread local state of the "main" OS  
thread (the one that runs C main ()), only for nonconcurrent calls,  
but not concurrent calls. This gives people a reason to specify some  
calls as nonconcurrent, even when they are actually expected to  
block, and it is desirable for other threads to continue running.
This creates an implementation-specific link between the concurrent/ 
nonconcurrent question and support for OS-thread-local-state. I would  
probably end up writing different code for different Haskell  
implemlementations in this situation.

Note that some predictable way of interacting with OS threads (and OS- 
thread local state) is necessary in order to be able to use some  
libraries at all, so not using OS threads *at all* might not be a  
feasible method of implementing a general-purpose programming  
language (or at least, not a feasible implementation method for  
general purpose implementations of general purpose programming  

The whole point of having bound threads is to NOT require a 1:1  
correspondence between OS threads and Haskell threads but still be  
able to interact with libraries that use OS-thread-local-state. They  
allow implementers to use OS threads just for *some* threads (i.e.  
just where necessary), while still having full efficiency and freedom  
of implementation for the other ("non-bound") threads.

There might be simpler schemes that can support libraries requiring  
OS-thread-local-state for the most common use cases, but there is a  
danger that it will interact with the concurrent/nonconcurrent issue  
in implementation-specific ways if we don't pay attention.

> I object to the idea that concurrent calls are 'safer'. getting it  
> wrong
> either way is a bug. it should fail in the most obvious way rather  
> than
> the way that can remain hidden for a long time.

How can making a call "concurrent" rather than "nonconcurrent" ever  
be a bug?

> in any case, blocking is a pretty well defined operation on operating
> systems, it is when the kernel can context switch you away waiting  
> for a
> resource, which is the main use of concurrent calls. the ability to  
> use
> them for long calculations in C is a sort of bonus, the actual main  
> use
> is to ensure the progress guarentee,

I disagree with this. First, concurrent calls serve a real-world  
purpose for all interactive programs. GUI applications are soft  
realtime systems; if a GUI application stops processing events for  
more than 2 seconds (under regular system load), I consider it buggy.
Second, although blocking is well-defined for kernel operations, the  
documented interface of most libraries does not include any  
guarantees on whether they will block the process or not; sometimes  
the difference might be entirely irrelevant; does it make a  
difference whether a drawing function in a library writes to video  
memory or sends an X request accross the network? Saying something  
"takesawhile" doesn't muddy things; it is a strictly weaker condition  
than whether something blocks.
Calculations done by foreign calls are not a "bonus", but an  
important use case for concurrent calls. Think of a library call that  
causes a multimedia library to recompress an entire video file;  
estimated time required is between a few seconds and a day. In a  
multithreaded program, this call needs to be concurrent. It is true  
that the program will still terminate even if the call is  
nonconcurrent, but in the real world, termination will probably occur  
by the user choosing to "force quit" an application that is "not  
responding" (also known as sending SIGTERM or even SIGKILL).

Reducing the issue to the question whether a function blocks or not  
is just plain wrong.

> I'd actually prefer it if there were no default and it had to
> be specified in neutral language because I think this is one of those
> things I want FFI library writers to think about.

But as I have been saying, the decision that FFI library writers have  
to make, or rather the only decision that they *can* make, is the  
simple decision of "can I guarantee that this call will return to its  
caller (or reenter Haskell via a callback) before the rest of the  
program is negatively affected by a pause". If the function could  
block, the answer is a definite "no", otherwise the question is  
inherently fuzzy. Unfortunately, I don't see a way of avoiding this  

The question "can I provide a certain guarantee or not" could be  
answered with "no" by default to flatten the learning curve a bit. My  
objection against having "no default" is not very strong, but I do  
object to specifying this "in neutral language". This situation does  
not call for neutral language; rather, it has to be made clear that  
one of the options comes with a proof obligation and the other only  
with a performance penalty.



P.S.: I'm sticking to the concurrent/nonconcurrent terminology for  
now. What terminology is really best will depend on the outcome of  
this discussion.

More information about the Haskell-prime mailing list