Commit c4c80a92 authored by Hanspeter Portner's avatar Hanspeter Portner

Merge commit 'd6286506'

parents 6bf6ae3e d6286506
Pipeline #22309211 passed with stages
in 4 minutes and 27 seconds
stages:
- test
.variables_template: &variables_definition
variables:
BASE_NAME: "varchunk"
PKG_CONFIG_PATH: "/opt/lv2/lib/pkgconfig:/opt/${CI_BUILD_NAME}/lib/pkgconfig:/usr/lib/${CI_BUILD_NAME}/pkgconfig"
.common_template: &common_definition
<<: *variables_definition
stage: test
.build_template: &build_definition
<<: *common_definition
script:
- meson --cross-file "${CI_BUILD_NAME}" build
- ninja -C build
.test_template: &test_definition
<<: *common_definition
script:
- meson --cross-file "${CI_BUILD_NAME}" build
- ninja -C build
- cd build
- meson test --verbose --wrap "${CI_BUILD_NAME}.wrap"
.universal_linux_template: &universal_linux_definition
image: ventosus/universal-linux-gnu
<<: *test_definition
.arm_linux_template: &arm_linux_definition
image: ventosus/arm-linux-gnueabihf
<<: *test_definition
.universal_w64_template: &universal_w64_definition
image: ventosus/universal-w64-mingw32
before_script:
- ln -s /usr/i686-w64-mingw32/lib/libwinpthread-1.dll /opt/i686-w64-mingw32/lib/libwinpthread-1.dll
- ln -s /usr/x86_64-w64-mingw32/lib/libwinpthread-1.dll /opt/x86_64-w64-mingw32/lib/libwinpthread-1.dll
<<: *test_definition
.universal_apple_template: &universal_apple_definition
image: ventosus/universal-apple-darwin
<<: *build_definition
# building in docker
x86_64-linux-gnu:
<<: *universal_linux_definition
i686-linux-gnu:
<<: *universal_linux_definition
arm-linux-gnueabihf:
<<: *arm_linux_definition
x86_64-w64-mingw32:
<<: *universal_w64_definition
i686-w64-mingw32:
<<: *universal_w64_definition
universal-apple-darwin:
<<: *universal_apple_definition
language: c
os: linux
compiler:
- gcc
- clang
before_install:
- if [ "$CC" = "clang" ]; then sudo add-apt-repository -y ppa:h-rayflood/llvm-upper; fi
- sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
- sudo apt-get -q update
install:
- if [ "$CC" = "clang" ]; then sudo apt-get install -y clang-3.6 libstdc++-5-dev; fi
- if [ "$CC" = "gcc" ]; then sudo apt-get install -y gcc-5 g++-5; fi
before_script:
- if [ "$CC" = "clang" ]; then export CXX="clang++-3.6" CC="clang-3.6" CFLAGS="-ffreestanding"; fi
- if [ "$CC" = "gcc" ]; then export CXX="g++-5" CC="gcc-5"; fi
- mkdir build && pushd build && cmake -DBUILD_TESTING=1 -DCMAKE_BUILD_TYPE=Debug .. && popd
script:
- pushd build && make && ARGS="-VV" make test && popd
cmake_minimum_required(VERSION 2.8)
project(varchunk)
set(CMAKE_C_FLAGS "-std=gnu11 -Wextra -Wno-unused-parameter -ffast-math -fvisibility=hidden ${CMAKE_C_FLAGS}")
set(CMAKE_C_FLAGS "-Wshadow -Wimplicit-function-declaration -Wmissing-prototypes -Wstrict-prototypes ${CMAKE_C_FLAGS}")
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
# find pthreads
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
include(CTest)
if(${BUILD_TESTING})
add_executable(test_varchunk
test_varchunk.c)
target_link_libraries(test_varchunk ${CMAKE_THREAD_LIBS_INIT} rt)
add_test(API-Test test_varchunk)
endif()
......@@ -13,79 +13,86 @@
### Build Status
[![Build Status](https://travis-ci.org/OpenMusicKontrollers/varchunk.svg)](https://travis-ci.org/OpenMusicKontrollers/varchunk)
[![build status](https://gitlab.com/OpenMusicKontrollers/varchunk/badges/master/build.svg)](https://gitlab.com/OpenMusicKontrollers/varchunk/commits/master)
### Usage
### Build / test
git clone https://git.open-music-kontrollers.ch/lad/varchunk
cd varchunk
meson build
cd build
ninja -j4
ninja test
``` c
#include <pthread.h>
#include <varchunk.h>
### Usage
static void *
producer_main(void *arg)
{
varchunk_t *varchunk = arg;
void *ptr;
const size_t towrite = sizeof(uint32_t);
uint32_t counter = 0;
#include <pthread.h>
#include <varchunk.h>
while(counter <= 1000000)
static void *
producer_main(void *arg)
{
if( (ptr = varchunk_write_request(varchunk, towrite)) )
varchunk_t *varchunk = arg;
void *ptr;
const size_t towrite = sizeof(uint32_t);
uint32_t counter = 0;
while(counter <= 1000000)
{
// write 'towrite' bytes to 'ptr'
*(uint32_t *)ptr = counter++;
varchunk_write_advance(varchunk, towrite);
if( (ptr = varchunk_write_request(varchunk, towrite)) )
{
// write 'towrite' bytes to 'ptr'
*(uint32_t *)ptr = counter++;
varchunk_write_advance(varchunk, towrite);
}
}
}
return NULL;
}
static void *
consumer_main(void *arg)
{
varchunk_t *varchunk = arg;
const void *ptr;
size_t toread;
return NULL;
}
while(1)
static void *
consumer_main(void *arg)
{
if( (ptr = varchunk_read_request(varchunk, &toread)) )
varchunk_t *varchunk = arg;
const void *ptr;
size_t toread;
while(1)
{
// read 'toread' bytes from 'ptr'
if(*(uint32_t *)ptr >= 1000000)
break;
varchunk_read_advance(varchunk);
if( (ptr = varchunk_read_request(varchunk, &toread)) )
{
// read 'toread' bytes from 'ptr'
if(*(uint32_t *)ptr >= 1000000)
break;
varchunk_read_advance(varchunk);
}
}
}
return NULL;
}
return NULL;
}
int
main(int argc, char **argv)
{
if(!varchunk_is_lock_free())
return -1;
int
main(int argc, char **argv)
{
if(!varchunk_is_lock_free())
return -1;
pthread_t producer;
pthread_t consumer;
varchunk_t *varchunk = varchunk_new(8192, true);
if(!varchunk)
return -1;
pthread_t producer;
pthread_t consumer;
varchunk_t *varchunk = varchunk_new(8192, true);
if(!varchunk)
return -1;
pthread_create(&consumer, NULL, consumer_main, varchunk);
pthread_create(&producer, NULL, producer_main, varchunk);
pthread_create(&consumer, NULL, consumer_main, varchunk);
pthread_create(&producer, NULL, producer_main, varchunk);
pthread_join(producer, NULL);
pthread_join(consumer, NULL);
pthread_join(producer, NULL);
pthread_join(consumer, NULL);
varchunk_free(varchunk);
varchunk_free(varchunk);
return 0;
}
```
return 0;
}
### License
......
project('varchunk', 'c', default_options : [
'buildtype=release',
'warning_level=3',
'werror=true',
'b_lto=true',
'c_std=c11'])
version = run_command('cat', 'VERSION').stdout().strip()
add_project_arguments('-D_GNU_SOURCE', language : 'c')
conf_data = configuration_data()
cc = meson.get_compiler('c')
thread_dep = dependency('threads')
deps = [thread_dep]
if host_machine.system() == 'linux'
rt_dep = cc.find_library('rt')
deps += rt_dep
endif
test_varchunk = executable('test_varchunk',
'test_varchunk.c',
dependencies : deps,
install : false)
test('Test', test_varchunk,
args : ['100000'],
timeout : 360) # seconds
......@@ -30,49 +30,48 @@
# include <fcntl.h>
# include <string.h>
# define VARCHUNK_USE_SHARED_MEM
#endif
#define ITERATIONS 10000000
#define THRESHOLD (RAND_MAX / 256)
static const struct timespec req = {
.tv_sec = 0,
.tv_nsec = 1
};
#endif
typedef struct _varchunk_shm_t varchunk_shm_t;
struct _varchunk_shm_t {
char *name;
int fd;
varchunk_t *varchunk;
};
static uint64_t iterations = 10000000;
#define THRESHOLD (RAND_MAX / 256)
#define PAD(SIZE) ( ( (size_t)(SIZE) + 7U ) & ( ~7U ) )
static void *
producer_main(void *arg)
{
varchunk_t *varchunk = arg;
void *ptr;
const void *end;
uint8_t *ptr;
const uint8_t *end;
size_t written;
uint64_t cnt = 0;
while(cnt < ITERATIONS)
while(cnt < iterations)
{
#if !defined(_WIN32)
if(rand() < THRESHOLD)
{
nanosleep(&req, NULL);
}
#endif
written = rand() * 1024.f / RAND_MAX;
written = PAD(rand() * 1024.f / RAND_MAX);
size_t maximum;
if( (ptr = varchunk_write_request_max(varchunk, written, &maximum)) )
{
assert(maximum >= written);
end = ptr + written;
for(void *src=ptr; src<end; src+=sizeof(uint64_t))
for(uint8_t *src=ptr; src<end; src+=sizeof(uint64_t))
{
*(uint64_t *)src = cnt;
assert(*(uint64_t *)src == cnt);
}
varchunk_write_advance(varchunk, written);
//fprintf(stdout, "P %"PRIu64" %zu %zu\n", cnt, written, maximum);
cnt++;
}
else
......@@ -88,23 +87,28 @@ static void *
consumer_main(void *arg)
{
varchunk_t *varchunk = arg;
const void *ptr;
const void *end;
const uint8_t *ptr;
const uint8_t *end;
size_t toread;
uint64_t cnt = 0;
while(cnt < ITERATIONS)
while(cnt < iterations)
{
#if !defined(_WIN32)
if(rand() < THRESHOLD)
{
nanosleep(&req, NULL);
}
#endif
if( (ptr = varchunk_read_request(varchunk, &toread)) )
{
end = ptr + toread;
for(const void *src=ptr; src<end; src+=sizeof(uint64_t))
for(const uint8_t *src=ptr; src<end; src+=sizeof(uint64_t))
{
assert(*(const uint64_t *)src == cnt);
}
varchunk_read_advance(varchunk);
//fprintf(stdout, "C %"PRIu64" %zu\n", cnt, toread);
cnt++;
}
else
......@@ -133,6 +137,15 @@ test_threaded()
varchunk_free(varchunk);
}
#if defined(VARCHUNK_USE_SHARED_MEM)
typedef struct _varchunk_shm_t varchunk_shm_t;
struct _varchunk_shm_t {
char *name;
int fd;
varchunk_t *varchunk;
};
static int
varchunk_shm_init(varchunk_shm_t *varchunk_shm, const char *name, size_t minimum, bool release_and_acquire)
{
......@@ -141,7 +154,9 @@ varchunk_shm_init(varchunk_shm_t *varchunk_shm, const char *name, size_t minimum
varchunk_shm->name = strdup(name);
if(!varchunk_shm->name)
{
return -1;
}
bool is_first = true;
varchunk_shm->fd = shm_open(varchunk_shm->name, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
......@@ -167,7 +182,9 @@ varchunk_shm_init(varchunk_shm_t *varchunk_shm, const char *name, size_t minimum
}
if(is_first)
{
varchunk_init(varchunk_shm->varchunk, body_size, release_and_acquire);
}
return 0;
}
......@@ -183,13 +200,14 @@ varchunk_shm_deinit(varchunk_shm_t *varchunk_shm)
free(varchunk_shm->name);
}
#if defined(VARCHUNK_USE_SHARED_MEM)
static void
test_shared()
{
const char *name = "/varchunk_shm_test";
pid_t pid = fork();
assert(pid != -1);
if(pid == 0) // child
{
varchunk_shm_t varchunk_shm;
......@@ -214,8 +232,15 @@ test_shared()
int
main(int argc, char **argv)
{
#if !defined(_WIN32)
const int seed = time(NULL);
srand(seed);
#endif
if(argc >= 2)
{
iterations = atoi(argv[1]);
}
assert(varchunk_is_lock_free());
......
......@@ -93,7 +93,7 @@ struct _varchunk_t {
atomic_size_t head;
atomic_size_t tail;
uint8_t buf [0] __attribute__((aligned(sizeof(varchunk_elmnt_t))));
uint8_t buf [] __attribute__((aligned(sizeof(varchunk_elmnt_t))));
};
static inline bool
......@@ -195,13 +195,13 @@ varchunk_write_request_max(varchunk_t *varchunk, size_t minimum, size_t *maximum
if(end > varchunk->size) // available region wraps over at end of buffer
{
// get first part of available buffer
void *buf1 = varchunk->buf + head;
uint8_t *buf1 = varchunk->buf + head;
const size_t len1 = varchunk->size - head;
if(len1 < padded) // not enough space left on first part of buffer
{
// get second part of available buffer
void *buf2 = varchunk->buf;
uint8_t *buf2 = varchunk->buf;
const size_t len2 = end & varchunk->mask;
if(len2 < padded) // not enough space left on second buffer, either
......@@ -232,7 +232,7 @@ varchunk_write_request_max(varchunk_t *varchunk, size_t minimum, size_t *maximum
}
else // available region is contiguous
{
void *buf = varchunk->buf + head;
uint8_t *buf = varchunk->buf + head;
if(space < padded) // no space left on contiguous buffer
{
......@@ -271,7 +271,7 @@ varchunk_write_advance(varchunk_t *varchunk, size_t written)
if(varchunk->gapd > 0)
{
// fill end of first buffer with gap
varchunk_elmnt_t *elmnt = (void *)varchunk->buf + head;
varchunk_elmnt_t *elmnt = (varchunk_elmnt_t *)(varchunk->buf + head);
elmnt->size = varchunk->gapd - sizeof(varchunk_elmnt_t);
elmnt->gap = 1;
......@@ -283,7 +283,7 @@ varchunk_write_advance(varchunk_t *varchunk, size_t written)
else // varchunk->gapd == 0
{
// fill written element header
varchunk_elmnt_t *elmnt = (void *)varchunk->buf + head;
varchunk_elmnt_t *elmnt = (varchunk_elmnt_t *)(varchunk->buf + head);
elmnt->size = written;
elmnt->gap = 0;
}
......@@ -322,9 +322,9 @@ varchunk_read_request(varchunk_t *varchunk, size_t *toread)
if(end > varchunk->size) // available buffer wraps around at end
{
// first part of available buffer
const void *buf1 = varchunk->buf + tail;
const uint8_t *buf1 = varchunk->buf + tail;
const size_t len1 = varchunk->size - tail;
const varchunk_elmnt_t *elmnt = buf1;
const varchunk_elmnt_t *elmnt = (const varchunk_elmnt_t *)buf1;
if(elmnt->gap) // gap elmnt?
{
......@@ -332,8 +332,9 @@ varchunk_read_request(varchunk_t *varchunk, size_t *toread)
_varchunk_read_advance_raw(varchunk, tail, len1);
// second part of available buffer
const void *buf2 = varchunk->buf;
elmnt = buf2; // there will always be at least on element after a gap
const uint8_t *buf2 = varchunk->buf;
// there will always be at least on element after a gap
elmnt = (const varchunk_elmnt_t *)buf2;
*toread = elmnt->size;
return buf2 + sizeof(varchunk_elmnt_t);
......@@ -347,8 +348,8 @@ varchunk_read_request(varchunk_t *varchunk, size_t *toread)
else // available buffer is contiguous
{
// get buffer
const void *buf = varchunk->buf + tail;
const varchunk_elmnt_t *elmnt = buf;
const uint8_t *buf = varchunk->buf + tail;
const varchunk_elmnt_t *elmnt = (const varchunk_elmnt_t *)buf;
*toread = elmnt->size;
return buf + sizeof(varchunk_elmnt_t);
......@@ -367,7 +368,7 @@ varchunk_read_advance(varchunk_t *varchunk)
assert(varchunk);
// get elmnt header from tail (for size)
const size_t tail = atomic_load_explicit(&varchunk->tail, memory_order_relaxed);
const varchunk_elmnt_t *elmnt = (const void *)varchunk->buf + tail;
const varchunk_elmnt_t *elmnt = (const varchunk_elmnt_t *)(varchunk->buf + tail);
// advance read tail
_varchunk_read_advance_raw(varchunk, tail,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment