docs.rs failed to build voluntary-servitude-1.0.0
Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
Visit the last successful build:
voluntary-servitude-4.0.8
Voluntary Servitude
Uses system allocator by default, jemmaloc can be enabled with the 'jemmaloc' feature
- Currently only implements a thread-safe appendable list with a lock-free iterator
FFI implementation available, C examples are in ./examples folder, library is in ./dist
FFI docs are in 'ffi' module documentation
Logging is available behind the 'logs' feature
Use RUST_LOG env var to config the level (trace, debug, info)
Example:
RUST_LOG=trace cargo test --features "logs"
Api Docs
Since it's not in the package manager you have to generate it, run:
cargo docs --open
Tests
Has unit tests, integrations tests, doctests and C FFI test examples
Rust tests:
cargo test
For C examples (tests) go to the ./examples and run:
make test
Macros
-
vsread!
Exactly like the 'vec!' macro
assert_eq!(vsread![].iter().collect::<Vec<_>>(), vec![]);
assert_eq!(vsread![1, 2, 3].iter().collect::<Vec<_>>(), vec![&1, &2, &3]);
assert_eq!(vsread![1; 3].iter().collect::<Vec<_>>(), vec![&1, &1, &1]);
Datastructures
-
VSRead
Appendable list that can create lock-free iterator
-
VSReadIter (can't instantiate it)
Lock-free iterator based on VSRead
Basic usage
Single thread
// Create VSRead with 3 elements
// vsread![] makes an empty VSRead
// vsread![1; 3] makes a VSRead with 3 elements with 1 as value
let list = vsread![0, 1, 2];
assert_eq!(list.len(), 3);
// The 'iter' method makes a one-time lock-free iterator (VSReadIter) based on VSRead
assert_eq!(list.iter().len(), 3);
// You can get the current iteration index (can be compared with the length 'len')
assert_eq!(list.iter().index(), 0);
// Appends 9997 elements to it
assert_eq!((3..10000).map(|i| list.append(i)).count(), 9997);
// Iterates through all elements to ensure it's what we inserted
let count = list.iter().enumerate().map(|(i, el)| assert_eq!(&i, el)).count();
assert_eq!(count, 10000);
// List can also be cleared
list.clear();
assert_eq!(list.len(), 0);
Multi producer, multi consumer
use std::{thread::spawn, sync::Arc};
const CONSUMERS: usize = 8;
const PRODUCERS: usize = 4;
fn main() {
let list = Arc::new(vsread![]); // or Arc::new(VSRead::default());
let mut handlers = vec![];
// Creates producer threads to insert 10k elements each
for _ in 0..PRODUCERS {
let l = Arc::clone(&list);
handlers.push(spawn(move || { let _ = (0..10000).map(|i| l.append(i)).count(); }));
}
// Creates consumer threads to print number of elements until all elements are inserted
for _ in 0..CONSUMERS {
let consumer = Arc::clone(&list);
handlers.push(spawn(move || {
loop {
let count = consumer.iter().count();
println!("{} elements", count);
if count == PRODUCERS * 10000 { break; }
}
}));
}
// Join threads
for handler in handlers.into_iter() {
handler.join().expect("Failed to join thread");
}
}
Single thread C example (FFI)
#include<assert.h>
#include "include/voluntary_servitude.h"
int main(int argc, char **argv) {
// Rust allocates memory through malloc
vsread_t * const vsread = vsread_new();
// Current vsread_t length
// Be careful with data-races since the value, when used, may not be true anymore
assert(vsread_len(vsread) == 0);
const unsigned int data[2] = {12, 25};
// Inserts void pointer to data to end of vsread_t
vsread_append(vsread, (void *) data);
vsread_append(vsread, (void *) (data + 1));
// Creates a one-time lock-free iterator based on vsread_t
vsread_iter_t * const iter = vsread_iter(vsread);
// Index changes as you iter through vsread_iter_t
assert(vsread_iter_index(iter) == 0);
// Clears vsread_t, doesn't change existing iterators
vsread_clear(vsread);
assert(vsread_len(vsread) == 0);
assert(vsread_iter_len(iter) == 2);
assert(*(unsigned int *) vsread_iter_next(iter) == 12);
assert(vsread_iter_index(iter) == 1);
assert(*(unsigned int *) vsread_iter_next(iter) == 25);
assert(vsread_iter_index(iter) == 2);
assert(vsread_iter_next(iter) == NULL);
assert(vsread_iter_index(iter) == 2);
assert(vsread_iter_len(iter) == 2);
// Never forget to free vsread_iter_t
vsread_iter_destroy(iter);
// Create updated vsread_iter_t
vsread_iter_t * const iter2 = vsread_iter(vsread);
assert(vsread_iter_len(iter2) == 0);
assert(vsread_iter_next(iter2) == NULL);
vsread_iter_destroy(iter2);
// Never forget to free vsread_t
vsread_destroy(vsread);
return 0;
}
Multi thread C example (FFI)
#include<pthread.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};
void* producer();
void* consumer();
int main(int argc, char** argv)
{
// Rust allocates memory through malloc
vsread_t * const vsread = vsread_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 *) vsread) != 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 *) vsread) != 0) {
fprintf(stderr, "Failed to create consumer thread %d.\n", current_thread);
exit(-3);
}
}
// Join all threads, ensuring vsread_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
vsread_destroy(vsread);
(void) argc;
(void) argv;
return 0;
}
void * producer(void * const vsread){
unsigned int index;
for (index = 0; index < num_producer_values; ++index) {
vsread_append(vsread, (void *) (data + (index % 2)));
}
return NULL;
}
void * consumer(void * const vsread) {
const unsigned int total_values = num_producers * num_producer_values;
unsigned int values = 0;
while (values < total_values) {
unsigned int sum = (values = 0);
vsread_iter_t * const iter = vsread_iter(vsread);
const void * value;
while ((value = vsread_iter_next(iter)) != NULL) {
++values;
sum += *(unsigned int *) value;
}
printf("Consumer counts %d elements summing %d.\n", values, sum);
vsread_iter_destroy(iter);
}
return NULL;
}