Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Embedding Rust tasks in a C program?

Tags:

c

rust

Calling a C library function from Rust program (an executable compiled by rustc) is working well, and and a goal of the Rust team.

Calling a Rust crate function from C program (an executable compiled by clang) is working for simple stuffs, but if I spawn a task, it crashes.

How can I make Rust tasks to work?


Here's my sources and error messages. You also can download from https://github.com/Eonil/TeachingMyselfRust

a.rs

#[no_mangle]
pub fn test1()
{
    let a1  =   proc()
    {
    };
    spawn(a1);
}

b.c

#include <stdio.h>

extern void test1();


int main(int argc, char** argv)
{
    test1();
    return 0;
}

build script

rm -rf ./Build
mkdir ./Build

rustc a.rs --crate-type=staticlib -o ./Build/rust-stuffs.a
clang b.c ./Build/rust-stuffs.a -o Build/out

cd Build
./out

execution result

warning: unlinked native library: System


There are not many persons who know what wonders are opened to them in the
stories and visions of their youth; for when as children we listen and dream,
we think but half-formed thoughts, and when as men we try to remember, we are
dulled and prosaic with the poison of life. But some of us awake in the night
with strange phantasms of enchanted hills and gardens, of fountains that sing
in the sun, of golden cliffs overhanging murmuring seas, of plains that stretch
down to sleeping cities of bronze and stone, and of shadowy companies of heroes
that ride caparisoned white horses along the edges of thick forests; and then
we know that we have looked back through the ivory gates into that world of
wonder which was ours before we were wise and unhappy.

fatal runtime error:  assertion failed: !ptr.is_null()
stack backtrace:
   1:        0x108bc7944 - rt::backtrace::imp::write::hdaa6a604147ca757g8b::v0.10
   2:        0x108b5f4aa - rt::util::abort::h3d7b16d436532c64Bmc::v0.10
   3:        0x108bc605a - rt::local_ptr::compiled::take::hb1c4b940f0aa52aea9a::v0.10
   4:        0x108b5e6ad - task::TaskBuilder::spawn::h2dcf43b5eaa6805aQhC::v0.10
   5:        0x108b5eb6d - task::spawn::hff1c6bd6cfded263NjC::v0.10
   6:        0x108b29670 - test1
   7:        0x108b295fd - main
./run.bash: line 8: 38948 Illegal instruction: 4  ./out
like image 859
eonil Avatar asked Aug 19 '14 14:08

eonil


1 Answers

It's not that simple, I'm afraid. Rust tasks are not plain threads, like pthreads or others in C. They do need some support from Rust runtime, so in order to create tasks you need to start the runtime. One of the ways is to call a Rust function from your C program which in turn will call another C function:

#include <stdio.h>

extern int run(int argc, char** argv, void (*kont)(void));
extern void do_work();

void actual_main(void);

int main(int argc, char** argv) {
    return run(argc, argv, actual_main);
}

void actual_main(void) {
    printf("Hello from C program\n");
    do_work();
}

Rust:

extern crate native;

#[no_mangle]
pub extern fn run(argc: int, argv: *const *const u8, kont: extern fn()) {
    native::start(argc, argv, proc() kont());
}

#[no_mangle]
pub extern fn do_work() {
    spawn(proc() {
        println!("Hello from task");
    });
}

Compilation (on my Linux box; if you're using another OS, you probably have to tweak it):

% rustc --crate-type staticlib --crate-name ex lib.rs
% gcc -c example.c
% gcc -L. -o example example.o -lex -lm -lpthread -lgcc_s -ldl

Running:

% ./example
Hello from C program
Hello from task

This does require you to change the main function. In any case, all code which access runtime-specific functions from Rust (tasks, Rust I/O, boxes, etc) have to be "under" native::start() call in the call stack. This is the simplest way I know of. Or, if that is acceptable, you can write your main() function in Rust, so your executable binary will be rustc output, even though its work will actually happen in C code.

like image 151
Vladimir Matveev Avatar answered Nov 16 '22 22:11

Vladimir Matveev