Module voluntary_servitude::ffi
source · Expand description
Voluntary Servitude Foreign Function Interface (FFI)
Allows using this rust library as a C library
While vs_t
(VoluntaryServitude
in C) is thread-safe it’s your responsibility to make sure it exists while pointers to it exist
Single-thread C implementation
#include<assert.h>
#include<stdio.h>
#include "../include/voluntary_servitude.h"
int main(int argc, char **argv) {
// You are responsible for making sure 'vs' exists while accessed
vs_t * vs = vs_new();
// Current vs_t length
// Be careful with data-races since the value, when used, may not be true anymore
assert(vs_len(vs) == 0);
const unsigned int data[2] = {12, 25};
// Inserts void pointer to data to end of vs_t
vs_append(vs, (void *) &data[0]);
vs_append(vs, (void *) &data[1]);
// Creates a one-time lock-free iterator based on vs_t
vs_iter_t * iter = vs_iter(vs);
// Clearing vs_t, doesn't change existing iterators
vs_clear(vs);
assert(vs_len(vs) == 0);
assert(vs_iter_len(iter) == 2);
assert(*(unsigned int *) vs_iter_next(iter) == 12);
// Index changes as you iter through vs_iter_t
assert(vs_iter_index(iter) == 1);
assert(*(unsigned int *) vs_iter_next(iter) == 25);
assert(vs_iter_index(iter) == 2);
assert(vs_iter_next(iter) == NULL);
assert(vs_iter_index(iter) == 2);
// Index doesn't increase after it gets equal to 'len'
// Length also is unable to increase after iterator is consumed
assert(vs_iter_index(iter) == vs_iter_len(iter));
// Never forget to free vs_iter_t
assert(vs_iter_destroy(iter) == 0);
// Create updated vs_iter_t
vs_iter_t * iter2 = vs_iter(vs);
// Never forget to free vs_t
assert(vs_destroy(vs) == 0);
// vs_iter_t keeps existing after the original vs_t is freed (or cleared)
assert(vs_iter_len(iter2) == 0);
assert(vs_iter_next(iter2) == NULL);
assert(vs_iter_index(iter2) == 0);
assert(vs_iter_destroy(iter2) == 0);
printf("Single thread example ended without errors\n");
(void) argc;
(void) argv;
return 0;
}
Multi-thread C implementation
#include<pthread.h>
#include<assert.h>
#include<stdio.h>
#include "../include/voluntary_servitude.h"
const unsigned int num_producers = 4;
const unsigned int num_consumers = 8;
const unsigned int num_producer_values = 1000;
const unsigned int data[3] = {12, 25, 89};
const size_t last_index = sizeof(data) / sizeof(data[0]) - 1;
void * producer();
void * consumer();
int main(int argc, char** argv) {
// You are responsible for making sure 'vs' exists
// while the producers and consumers do
vs_t * const vs = vs_new();
unsigned int current_thread = 0;
pthread_attr_t attr;
pthread_t consumers[num_consumers],
producers[num_producers];
if (pthread_attr_init(&attr) != 0) {
fprintf(stderr, "Failed to initialize pthread arguments.\n");
exit(-1);
}
// Creates producer threads
for (current_thread = 0; current_thread < num_producers; ++current_thread) {
if (pthread_create(&producers[current_thread], &attr, &producer, (void *) vs) != 0) {
fprintf(stderr, "Failed to create producer thread %d.\n", current_thread);
exit(-2);
}
}
// Creates consumers threads
for (current_thread = 0; current_thread < num_consumers; ++current_thread) {
if (pthread_create(&consumers[current_thread], &attr, &consumer, (void *) vs) != 0) {
fprintf(stderr, "Failed to create consumer thread %d.\n", current_thread);
exit(-3);
}
}
// Join all threads, ensuring vs_t* is not used anymore
for (current_thread = 0; current_thread < num_producers; ++current_thread) {
pthread_join(producers[current_thread], NULL);
}
for (current_thread = 0; current_thread < num_consumers; ++current_thread) {
pthread_join(consumers[current_thread], NULL);
}
// Never forget to free the memory allocated through rust
assert(vs_destroy(vs) == 0);
printf("Multi thread example ended without errors\n");
(void) argc;
(void) argv;
return 0;
}
void * producer(void * const vs){
unsigned int index;
for (index = 0; index < num_producer_values; ++index) {
assert(vs_append(vs, (void *) &data[index % last_index]) == 0);
}
return NULL;
}
void * consumer(void * const vs) {
const unsigned int total_values = num_producers * num_producer_values;
unsigned int values;
while (values < total_values) {
unsigned int sum = (values = 0);
vs_iter_t * const iter = vs_iter(vs);
const void * value;
while ((value = vs_iter_next(iter)) != NULL) {
++values;
sum += *(unsigned int *) value;
}
printf("Consumer counts %d elements summing %d.\n", values, sum);
assert(vs_iter_destroy(iter) == 0);
}
return NULL;
}
Functions
Initialize logger according to RUST_LOG env var (exists behind ‘logs’ feature)
Append element to
VoluntaryServitude
Removes all elements from list (preserves existing iterators)
Free
VoluntaryServitude
(preserves existing iterators)Makes lock-free iterator based on
VoluntaryServitude
Free
VSIter
(can happen after VoluntaryServitude
’s free)Returns current iterator index
Returns total size of iterator, it may grow, but never decrease
Obtains next element in iterator, returns NULL if there are no more elements
Atomically extracts current size of
VoluntaryServitude
, be careful with data-races when using itCreates new empty
VoluntaryServitude