News: Stay up to date

The Étoilé community is an active group of developers, designers, testers and users. New work is being done every day. Visit often to find out what we've been up to.


Pragmatic Smalltalk and C

Posted on 19 August 2012 by David Chisnall

Next week, I'm presenting a paper at IWST with the title Smalltalk in a C World, describing LanguageKit / Pragmatic Smalltalk. This seemed like a good time to polish the C interoperability stuff and show one of the examples from the paper here. This is now in svn as Languages/Compiler/examples/

<< headers='("dispatch/dispatch.h", "unistd.h")' >>
NSObject subclass: SmalltalkTool [
    run [
        | queue count |
        queue := C dispatch_get_global_queue: { 0. 0 }.
        ETTranscript show: 'main thread' ; cr .
        count := 1.
        1 to: 10 do: [
            C dispatch_async: { queue .
                    ETTranscript show: 'Another thread' ;
                                 show: count ; cr . 
                    count := count + 1 
                ] } ] .
        C sleep: 1.
        ETTranscript show: 'Threads used: ' ; show: count ; cr. 

The first thing to notice here is the headers pragma. This lets you specify C headers inside a Smalltalk source file. If SourceCodeKit is installed then this pragma will instruct the compiler to use it to determine type information from functions using it. If it isn't installed, then you'll get an error something like this:

$ edlc -f
2012-08-19 13:13:08.670 edlc[99020] WARNING: Unable to load header: dispatch/dispatch.h
2012-08-19 13:13:08.671 edlc[99020] WARNING: Unable to load header: unistd.h
2012-08-19 13:13:08.671 edlc[99020] ERROR: Can not determine type for dispatch_get_global_queue
2012-08-19 13:13:08.672 edlc[99020] ERROR: Can not determine type for dispatch_async
2012-08-19 13:13:08.672 edlc[99020] ERROR: Can not determine type for sleep
2012-08-19 13:13:08.672 edlc[99020] Failed to compile input.

Note the warnings that it can't find the headers. These are only warnings, because it may still be able to run the code, but in this case it can't: it fails to find the type information for the C functions that we're calling. Install SourceCodeKit and you'll see something more like this:

$ edlc -f
LIBCLANG FATAL ERROR: Program used external function 'dispatch_get_global_queue' which could not be resolved!
Abort trap: 6 (core dumped)

It is now correctly compiling the Smalltalk code, but it fails when it tries to run it, because the dispatch_get_global_queue() function isn't present. You need to explicitly tell it to load the libdispatch library:

$ edlc -f -l dispatch
main thread
Another thread1
Another thread2
Another thread3
Another thread4
Another thread5
Another thread6
Another thread7
Another thread8
Another thread9
Another thread10
Threads used: 11

Note that we just call it dispatch. You can also specify,, or a full path and it will try to find it. In the latest version of edlc, you can also specify multiple libraries to load.

Now that it's working, let's look at what's happening. Each of the message sends to the fake class C are translated into function calls. There are three of these, two in libdispatch and one in libc. The dispatch_async() function takes two arguments: a queue and a block. LanguageKit is boxing and unboxing the queue, which is just a C opaque type (i.e. a pointer). We use the same blocks ABI as C, so Smalltalk blocks passed to libdispatch Just Work.

This is spawning 11 tasks, which will be run in either separate threads or be multiplexed onto a smaller number of OS threads, depending on system load. This shows Smalltalk using real threads. If you add a sleep into the start of the block, this becomes very obvious, as you see the race condition in this program (the counter is not updated atomically).