FFI, safe vs unsafe

Wolfgang Thaller wolfgang.thaller at gmx.net
Wed Apr 12 23:37:57 EDT 2006


John Meacham wrote:

> However, in order to achieve that we would have to annotate the  
> foreign
> functions with whether they use thread local state.

I am not opposed to that; however, you might not like that here  
again, there would be the safe, possibly inefficient default choice,  
which means "might access thread local data", and the possibly more  
efficient annotation that comes with a proof obligation, which says  
"guaranteed not to access thread local data".
The main counterargument is that some libraries, like OpenGL require  
many *fast* nonconcurrent, nonreentrant but tls-using calls (and,  
nost likely, one reentrant and possibly concurrent call for the GLUT  
main event loop). Using OpenGL would probably be infeasible from an  
implementation which requires a "notls" annotation to make foreign  
imports fast.

> it would pretty much
> be vital for implementing them efficiently on a non OS-threaded
> implemenation of the language.

True, with the implementation plan you've outlined so far.
Have you considered hybrid models where most threads are state  
threads (all running in one OS thread) and a few threads (=the bound  
threads) are OS threads which are prevented from actually executing  
in parallel by a few well-placed locks and condition variables? You  
could basically write an wrapper around the state threads and  
pthreads libraries, and you'd get the best of both worlds. I feel it  
wouldn't be that hard to implement, either.

> Oddly enough, depending on the implementation it might actually be
> easier to just make every 'threadlocal' function fully concurrent. you
> have already paid the cost of dealing with OS threads.

Depending on the implementation, yes. This is the case for the  
inefficient implementation we recommended for interpreters like Hugs  
in our bound threads paper; there, the implementation might be  
constrained by the fact that Hugs implements cooperative threading in  
Haskell using continuation passing in the IO monad; the interpreter  
itself doesn't even really know about threads. For jhc, I fell that a  
hybrid implementation would be better.

> they are a bonus in that you can't run concurrent computing haskell
> threads at the same time. you get "free" concurrent threads in other
> languages that you would not get if the libraries just happened to be
> implemented in haskell. However, if the libraries were implemented in
> haskell, you would still get concurrency on OS blocking events because
> the progress guarentee says so.

Hmm... it sounds like you've been assuming cooperative scheduling,  
while I've been assuming preemptive scheduling (at least GHC-style  
preemption, which only checks after x bytes of allocation). Maybe, in  
a cooperative system, it is a little bit of a bonus, although I'd  
still want it for practical reasons. I can make my Haskell  
computations call yield, but how do I make a foreign library (whose  
author will just say "Let them use threads") cooperate? In a  
preemtive system, the ability to run a C computation in the  
background remains a normal use case, not a bonus.

>> 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.
>
> you seem to be contradicting yourself, above you say a performance
> penalty is vitally important in the GUI case if a call takes too  
> long, [...]

I am not. What I was talking about above was not performance, but  
responsiveness; it's somewhat related to fairness in scheduling.
If a foreign call takes 10 microseconds instead of 10 nanoseconds,  
that is a performance penalty that will matter in some circumstances,  
and not in others (after all, people are writing real programs in  
Python...). If a GUI does not respond to events for more than two  
seconds, it is badly written. If the computer or the programming  
language implementation are just too slow (performance) to achieve a  
certain task in that time, the Right Thing To Do is to put up a  
progress bar and keep processing screen update events while doing it,  
or even do it entirely "in the background".
Of course, responsiveness is not an issue for non-interactive  
processes, but for GUIs it is very important.

> Who is to say whether a app that
> muddles along is better or worse than one that is generally snappy but
> has an occasional delay.

I am ;-). Apart from that, I feel that is a false dichotomy, as even  
a factor 1000 slowdown in foreign calls is no excuse to make a GUI  
"generally muddle along".

> Though, I am a fan of neutral language in general. you can't crash the
> system like you can with unsafePerformIO, FFI calls that take a while
> and arn't already wrapped by the standard libraries are relatively  
> rare.
> no need for strong language.

To the end user, all unusable programs are equivalent :-). Also, one  
decision can make a library unusable for interactive programs while  
the other can't; the language should be strong enough to make that  
clear to library writers who have never heard the words "state  
threads" and who don't care much about concurrency in general.

As for your claim about the relative rarity of such calls, I see that  
your bias is very different from mine. My world consists mostly of  
console-based programs that compute something (compilers, etc.) and  
don't need any FFI or concurrency to speak of, and of interactive  
graphical applications (GUIs + games) for which the standard  
libraries are only a tiny fragment of the FFI world. In your world,  
network servers (or applications with a similar structure) seem to  
figure more prominently.

Cheers,

Wolfgang



More information about the Haskell-prime mailing list