Bluish Coder

Programming Languages, Martials Arts and Computers. The Weblog of Chris Double.


2015-12-16

C linkable libraries with Pony

Note 2017-08-01: Recent releases of Pony have changed some of the details of interacting with C. This post has been updated to work with these changes as of Pony 0.16.1.

My quick look at Pony post covered how to use the FFI to call C code from a Pony program. It's also possible to compile Pony code to a C library that can be linked into a C program. This allows integrating Pony into existing C projects without having to convert the C project into a library to be called by Pony.

I've put a small example of how to do this on github at https://github.com/doublec/pony-clib-example.

Pony has a special type of actor that will result in a C compatible interface being generated. The syntax for this is actor@. The example in the project is:

actor@ Foo
  new create() =>
    None

  be hi() =>
    @printf[I32]("Hi\n".cstring())

This creates a C compatible actor called Foo. It has a constructor create and a behaviour hi. The constructor does nothing and only exists in the example to show how to call it from C. The hi behaviour calls the C function printf using the Pony FFI. With a file main.pony containing this code in a clib directory it can be built with:

$ ponyc -l clib
Building builtin -> packages/builtin
Building clib -> clib
Generating
Optimising
Writing ./clib.o
Archiving ./libclib.a

The -l switch to ponyc results in a static library being built instead of an executable. A libclib.a is generated containing the compiled Pony code. A clib.h is also generated containing the C function declarations for creating and interacting with any actor@ instances. For the Foo actor this looks like:

typedef struct Foo Foo;
Foo* Foo_Alloc();
None* Foo_tag_hi_o__send(Foo* self);
Foo* Foo_tag_create_o__send(Foo* self);

Pony has a runtime that needs to be initialized from C and a scheduler that needs to be started. The definitions for these runtime functions can be found in the ponyc source in libponyrt/pony.h. This needs to be included by the C program. A basic C driver for the example project is:

#include <stdio.h>
#include "clib.h"
#include "pony.h"

int main(int argc, char** argv) {
  pony_init(argc, argv);

  Foo* x = Foo_Alloc();
  Foo_tag_hi_o__send(x);
  Foo_tag_create_o__send(x);

  pony_start(true, true);
  return 0;
}

This sould be compiled and linked against libclib.a and libponyrt.a:

export PONYRT_INCLUDE=/path/to/ponyc/src/libponyrt/
export PONYRT_COMMON=/path/to/ponyc/src/common
export PONYRT_LIB=/path/to/ponyc/build/release/libponyrt.a

gcc -o test -I. -I $(PONYRT_INCLUDE) -I $(PONYRT_COMMON) \
    -g -rdynamic -mcx16 test.c libclib.a $(PONYRT_LIB) -lpthread -ldl

The C code needs to call pony_init to initialize the Pony runtime. It gets passed the standard C argc and argv arguments that main receives so it can handle any Pony runtime specific switches. After this any Pony actors can be allocated and methods or behaviours called on them. The order for actor creation is to call the _Alloc function to allocate the memory for the actor and then the constructor method. Pony names the constructor method based on the name in the Pony code with __send appended.

Actors are asynchronous so just like in Pony code behaviours aren't run immediately. They are queued for execution and will run when the scheduler is started. This is what pony_start does. The arguments passed to pony_start are a boolean indicating whether to block until all Pony actors have completed (by passing a false value) or to return immediately (by passing a true value). If the latter is done then the scheduler runs asynchronously. It can be stopped with a later call to pony_stop. The second parameter indicates whether to initialize Pony specific parts of the runtime (sockets and serialization). It's possible to continue calling Pony functions after the pony_start and before pony_stop. For example:

pony_init(argc, argv);

Foo* x = Foo_Alloc();
Foo_tag_hi_o__send(x);
Foo_tag_create_o__send(x);

pony_start(true, true);

Foo* y = Foo_Alloc();
Foo_tag_hi_o__send(y);
Foo_tag_create_o__send(y);

pony_stop();

In this snippet another actor is created after pony_start and behaviours are called on it. These are queued and run as expected. pony_stop blocks until all Pony actors have completed.

There are other Pony runtime functions in pony.h that allow allocating memory on the current actor heap, unscheduling an actor so that no behaviours will be run until rescheduled, making a C thread 'become' an actor so it can call Pony runtime functions, poll for messages on an unscheduled actor, etc. It's possible to allocate an actor, unschedule it, and then use a C thread to handle its message queue using pony_become. When you aren't handling messages to the unscheduled actor you can call functions to it from C with no race conditions.

None of this is currently documented and it will probably change as usage develops. In the meantime it's a useful feature for using Pony functionality in existing applications.

Tags


This site is accessable over tor as hidden service mh7mkfvezts5j6yu.onion, or Freenet using key:
USK@1ORdIvjL2H1bZblJcP8hu2LjjKtVB-rVzp8mLty~5N4,8hL85otZBbq0geDsSKkBK4sKESL2SrNVecFZz9NxGVQ,AQACAAE/bluishcoder/-44/


Tags

Archives
Links