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
vs_iter_t
(Iter
in C) can outlive vs_t
and isn’t affected by vs_clear
, it is not thread-safe.
It can only be called by one thread (but multiple vs_iter_t
of the same vs_t
can exist at the same time)
Examples
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 race-conditions 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-producer, multi-consumer C implementation
#include<pthread.h>
#include<assert.h>
#include<stdlib.h>
#include<stdio.h>
#include "../include/voluntary_servitude.h"
const unsigned int num_consumers = 8;
const unsigned int num_producers = 4;
const unsigned int num_threads = 12;
const unsigned int num_producer_values = 10000000;
const unsigned int data = 3;
void * producer(void *);
void * consumer(void *);
int main(int argc, char** argv) {
// You are responsible for making sure 'vs' exists while accessed
vs_t * vs = vs_new();
uint8_t thread = 0;
pthread_attr_t attr;
pthread_t threads[num_threads];
if (pthread_attr_init(&attr) != 0) {
fprintf(stderr, "Failed to initialize pthread arguments.\n");
exit(-1);
}
// Creates producer threads
for (thread = 0; thread < num_producers; ++thread) {
if (pthread_create(&threads[thread], &attr, &producer, (void *) vs) != 0) {
fprintf(stderr, "Failed to create producer thread %d.\n", thread);
exit(-2);
}
}
// Creates consumers threads
for (thread = 0; thread < num_consumers; ++thread) {
if (pthread_create(&threads[num_producers + thread], &attr, &consumer, (void *) vs) != 0) {
fprintf(stderr, "Failed to create consumer thread %d.\n", thread);
exit(-3);
}
}
// Join all threads, ensuring vs_t* is not used anymore
for (thread = 0; thread < num_threads; ++thread) {
pthread_join(threads[thread], NULL);
}
// Never forget to free the memory allocated through the lib
assert(vs_destroy(vs) == 0);
printf("Multi-thread C example ended without errors\n");
(void) argc;
(void) argv;
return 0;
}
void * producer(void * vs){
unsigned int index;
for (index = 0; index < num_producer_values; ++index) {
assert(vs_append(vs, (void *) &data) == 0);
}
return NULL;
}
void * consumer(void * vs) {
const unsigned int total_values = num_producers * num_producer_values;
unsigned int values = 0;
while (values < total_values) {
vs_iter_t * iter = vs_iter(vs);
void * value;
values = 0;
while ((value = vs_iter_next(iter)) != NULL) {
++values;
}
printf("%d elements\n", values);
// Never forget to free the memory allocated through the lib
assert(vs_iter_destroy(iter) == 0);
}
return NULL;
}
Functions
Initializes logger according to
RUST_LOG
env var (exists behind the logs
feature)Append element to
VoluntaryServitude
Removes all elements from
VoluntaryServitude
(preserves existing Iter
)Free
VoluntaryServitude
(preserves existing Iter
)Makes lock-free iterator (
Iter
) based on VoluntaryServitude
Free
Iter
(can happen after VoluntaryServitude
’s free)Returns current
Iter
indexReturns total size of
Iter
, it may grow, but never decreaseObtains next element in
Iter
, returns NULL
if there are no more elementsAtomically extracts current size of
VoluntaryServitude
, be careful with race conditions when using itCreates new empty
VoluntaryServitude
Type Definitions
VoluntaryServitude
’s representation in C