Commit 235c01b3 authored by Florian Fischer's avatar Florian Fischer
Browse files

initial commit

parents
Pipeline #49085 failed with stages
in 54 seconds
---
BasedOnStyle: Google
ColumnLimit: 100
TabWidth: 2
UseTab: Always
Checks: bugprone-*, cert-*, linuxkernel-*, modernize-*, performance-*, portability-*, readability-*,-readability-isolate-declaration,-clang-diagnostic-empty-translation-unit
WarningsAsErrors: clang-*, readability-*, performance-*
# top-most EditorConfig file
root = true
[*.{c,cpp,h}]
indent_style = tab
indent_size = 2
build*
tags
.gdb_history
subprojects/emper
image: "flowdalic/debian-dev:1.6"
before_script:
- apt update
- apt install -y --no-install-recommends git
- |
TOOLS="cc c++ clang clang++ gcc g++ clang-tidy clang meson include-what-you-use"
for tool in $TOOLS; do
echo "$tool version"
$tool --version
done
stages:
- smoke-test
- test
smoke-test:
stage: smoke-test
script: make smoke-test
variables:
BUILDTYPE: debugoptimized
CC: gcc
CXX: g++
EXTRA_NINJA_ARGS: -v
.test:
stage: test
script:
- make test
.test-w-coverage:
extends: .test
variables:
COVERAGE_REPORT: 'true'
coverage: '/lines\.\.\.\.\.\.: \d+\.\d+/'
artifacts:
expose_as: 'Test Coverage Report'
paths: ['build/meson-logs/coveragereport/']
.gcc:
variables:
CC: gcc
CXX: g++
.clang:
variables:
CC: clang
CXX: clang++
test-gcc:
extends:
# We only do test coverage with gcc for now. It appears the gcovr
# in buster is to old for the clang we use.
# See https://gitlab.cs.fau.de/flow/speedymalloc/-/jobs/157107#L222
- .test-w-coverage
- .gcc
test-gcc-release:
extends: test-gcc
variables:
BUILDTYPE: release
test-gcc-debug:
extends: test-gcc
variables:
BUILDTYPE: debug
test-clang:
extends:
- .test
- .clang
test-clang-release:
extends: test-clang
variables:
BUILDTYPE: release
test-clang-debug:
extends: test-clang
variables:
BUILDTYPE: debug
This diff is collapsed.
SHELL=bash
# https://stackoverflow.com/a/39124162/194894
word-dot = $(word $2,$(subst ., ,$1))
MESON_VERSION=$(shell meson --version)
MESON_MAJOR_VERSION=$(call word-dot, $(MESON_VERSION), 1)
MESON_MINOR_VERSION=$(call word-dot, $(MESON_VERSION), 2)
.PHONY: all build check check-format clean coverage debug\
format release smoke-test test
all: build
export BUILDTYPE ?= debugoptimized
export BUILDDIR = build-$(BUILDTYPE)
NINJA_BIN ?= ninja
NINJA := $(NINJA_BIN) $(EXTRA_NINJA_ARGS)
build:
[[ -L build ]] || ./tools/prepare-build-dir
$(NINJA) -C $@
release:
rm -f build
$(MAKE) build BUILDTYPE=$@
debug:
rm -f build
$(MAKE) build BUILDTYPE=$@
coverage:
$(MAKE) COVERAGE_REPORT=true test
SMOKE_TEST_NINJA_TARGETS += iwyu
TEST_NINJA_TARGETS += test
ifeq ($(COVERAGE_REPORT), true)
ifeq ($(shell command -v gcovr &> /dev/null && echo true), true)
TEST_NINJA_TARGETS += coverage
export EMPER_ECHO_SERVER_COVERAGE = true
else
$(warning can not produce coverage report, because missing gcovr)
endif
endif
# Meson >= 0.52 will automatically generate a clang-tidy target if a
# .clang-tidy file is found.
# Source version check: https://stackoverflow.com/a/3732456/194894
ifeq ($(shell [ $(MESON_MINOR_VERSION) -ge 52 ] && echo true), true)
SMOKE_TEST_NINJA_TARGETS += clang-tidy
else
$(warning old mesion version $(MESON_VERSION) detected, meson >= 0.52 required for clang-tidy)
endif
smoke-test: all check-format check-license
cd build && meson test --suite smoke
$(NINJA) -C build $(SMOKE_TEST_NINJA_TARGETS)
test: all
$(NINJA) -C build $(TEST_NINJA_TARGETS)
check: smoke-test test
format: all
$(NINJA) -C build clang-format
check-format:
./tools/check-format
check-license:
./tools/check-license
clean:
rm -rf build
rm -rf build-*
# emper-echo-server
A simple, tcp echo-server build upon the emper and it's IO subsystem.
Its only purpose is to test and benchmark the emper IO performance.
set pagination off
set startup-with-shell off
start
set scheduler-locking step
b _exit
commands
run
end
c
[
{ include: ["@<gtest/.*>", "private", "<gtest/gtest.h>", "public"] },
]
\ No newline at end of file
project('emper-echo-server', 'c', 'cpp',
version : '0.1',
default_options : [
'warning_level=3',
'c_std=gnu11',
'cpp_std=c++17',
'b_ndebug=if-release',
'werror=true',
])
add_project_arguments(
'-D_GNU_SOURCE',
language: 'c'
)
emper_proj = subproject('emper', default_options: ['async_lib=true'])
emper_dep = emper_proj.get_variable('emper_dep')
dependencies = [emper_dep]
run_target('iwyu',
command: 'tools/check-iwyu')
conf_data = configuration_data()
conf_data.set('USE_ASYNC_IO', get_option('use_async_io'))
subdir('src')
subdir('test')
option('use_async_io', type: 'boolean', value: true)
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright © 2020 Florian Fischer
/* #include <assert.h> */
#include <errno.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#include "config.h"
#define EMPER_ASYNC_LIB
#define EMPER_ASYNC_NETWORK
#include "emper.h"
#define unlikely(x) __builtin_expect(!!(x), 1)
int port;
_Noreturn void die(const char* msg) {
perror(msg);
exit(EXIT_FAILURE);
}
ssize_t send_all(int fd, const void* buf, size_t len) {
size_t bytes_written = 0;
while (bytes_written < len) {
#ifdef USE_ASYNC_IO
ssize_t new_bytes_written = async_send(fd, buf, len - bytes_written);
#else
ssize_t new_bytes_written = send(fd, buf, len - bytes_written, 0);
#endif
if (unlikely(new_bytes_written < 1)) {
return new_bytes_written;
}
bytes_written += new_bytes_written;
}
return (ssize_t)bytes_written;
}
#define BUF_MAX 4096
void client_func(void* arg) {
int client_fd = *(int*)arg;
free(arg);
char buf[BUF_MAX];
for (;;) {
#ifdef USE_ASYNC_IO
ssize_t bytes_recv = async_recv(client_fd, (void*)&buf, BUF_MAX);
#else
ssize_t bytes_recv = recv(client_fd, (void*)&buf, BUF_MAX);
#endif
// socket was shutdown
if (unlikely(bytes_recv == 0)) {
return;
}
if (unlikely(bytes_recv == -1)) {
perror("recv failed");
if (errno != EINTR) {
return;
}
}
ssize_t bytes_send = send_all(client_fd, buf, bytes_recv);
// socket was shutdown
if (unlikely(bytes_send == 0)) {
return;
}
if (unlikely(bytes_send == -1)) {
perror("recv failed");
if (errno != EINTR) {
return;
}
}
}
}
#define BACKLOG 5
static void welcome_func() {
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) {
die("socket failed");
}
struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(servaddr)); // NOLINT
// assign IP, PORT
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = INADDR_ANY;
servaddr.sin_port = htons(port);
int reuseaddr = 1;
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(reuseaddr)) == -1) {
die("setsockopt failed");
}
if (bind(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1) {
die("bind failed");
}
if (listen(sockfd, BACKLOG) != 0) {
die("listen failed");
}
for (;;) {
#ifdef USE_ASYNC_IO
int client_fd = async_accept(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr));
#else
socklen_t address_len = sizeof(servaddr);
int client_fd = accept(sockfd, (struct sockaddr*)&servaddr, &address_len);
#endif
if (unlikely(client_fd < 0)) {
perror("accept failed");
}
int* passable_fd = malloc(sizeof(int));
if (unlikely(!passable_fd)) {
die("allocating passable fd failed");
}
*passable_fd = client_fd;
fiber* client_fiber = fiber_from(client_func, (void*)passable_fd);
schedule(client_fiber);
}
}
int main(int argc, char* argv[]) {
if (argc < 2) {
printf("Please give a port number: ./%s [port]\n", argv[0]);
exit(EXIT_FAILURE);
}
const int decimal = 10;
port = (int)strtol(argv[1], NULL, decimal);
init_runtime();
fiber* welcome_fiber = fiber_from0(welcome_func);
schedule(welcome_fiber);
wait_until_runtime_finished();
}
configure_file(output: 'config.h', configuration: conf_data)
include_dir = include_directories('.')
emper_c = emper_proj.get_variable('emper_c')
emper_library_include = emper_proj.get_variable('emper_library_include')
sources = ['emper-echo-server.c']
exe = executable('emper-echo-server',
sources,
include_directories : [include_dir, emper_library_include],
dependencies: dependencies,
link_with: emper_c)
SortIncludes: false
DisableFormat: true
\ No newline at end of file
[wrap-git]
url = git@gitlab.cs.fau.de:i4/manycore/emper.git
revision = io_uring_network
Checks: -readability-magic-numbers,-clang-analyzer-unix.Malloc,-clang-analyzer-optin.portability.UnixAPI
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright © 2020 Florian Fischer
#include <pthread.h>
#include <stdio.h> // for asprintf
#include <stdlib.h> // for size_t, free, malloc
#include <string.h> // for strlen, strcmp, strcpy, strncmp
#include <sys/sysinfo.h>
#include "hash_table.h"
#include "hash_table_entry.h" // for ht_value_t, value
#define HT_DEFAULT_SIZE 10
static char* new_str(const char* s) {
char* as;
asprintf(&as, "%s", s);
return as;
}
static ht_value_t new_str_value(const char* d) {
size_t data_len = strlen(d) + 1;
ht_value_t v = ht_value_new(data_len, 0);
if (v) {
memcpy(v->data, d, data_len);
}
return v;
}
static void insert(ht_t* ht, const char* key, const char* d) {
char* k = new_str(key);
ht_value_t v = new_str_value(d);
bool res = ht_set(ht, k, v);
if (!res) {
printf("can't set key %s to value %s\n", key, v->data);
exit(1);
}
}
static void find_present(ht_t* ht, const char* key, const char* expected_value, const char* msg) {
ht_value_t v;
bool found = ht_get(ht, key, &v);
if (!found) {
printf("Can't find value for %s %s\n", msg, key);
exit(1);
}
if (!(strcmp(v->data, expected_value) == 0)) {
printf("expected value '%s' does not match '%s'\n", expected_value, v->data);
exit(1);
}
}
static void find_absent(ht_t* ht, const char* key, const char* msg) {
ht_value_t v;
bool found = ht_get(ht, key, &v);
if (found) {
printf("found value for %s %s with value v: %s\n", msg, key, v->data);
exit(1);
}
}
ht_t* ht;
const size_t iterations = 10000;
void* thread_func(void* arg) {
char* key_char = (char*)arg;
char key_str[2];
key_str[0] = *key_char;
key_str[1] = 0; // terminate string
for (size_t i = 0; i < iterations; ++i) {
insert(ht, key_str, key_str);
find_present(ht, key_str, key_str, "concurrently inserted key");
bool removed = ht_remove(ht, key_str);
if (!removed) {
printf("just inserted key %s not present for removal\n", key_str);
exit(1);
}
find_absent(ht, key_str, "inserted and removed key");
}
return NULL;
}
int main() {
ht = ht_new(HT_DEFAULT_SIZE);
int nprocs = get_nprocs();
int double_capacity = ht_get_capacity(ht) * 2;
int nthreads = nprocs > double_capacity ? nprocs : double_capacity;
pthread_t* threads = malloc(sizeof(pthread_t) * nthreads);
if (!threads) {
perror("malloc failed");
exit(1);
}
char* key_chars = malloc(sizeof(char) * nthreads);
if (!key_chars) {
perror("malloc failed");
exit(1);
}
for (int i = 0; i < nthreads; ++i) {
key_chars[i] = 'A' + i;
pthread_create(&threads[i], NULL, thread_func, &key_chars[i]);
}
for (int i = 0; i < nthreads; ++i) {
pthread_join(threads[i], NULL);
}
ht_free(ht);
}
tests = {
# 'simple_test.c':
# {
# 'description': 'Simple test',
# 'smoke_test': true,
# },
}
# Meson integration for GTest and GMock
# See https://mesonbuild.com/Dependencies.html#gtest-and-gmock
gtest_dep = dependency('gtest', main: true)
#subdir('fixtures')
foreach source, test_dict : tests
# TODO: Use meson fs (filesystem) module once meson >= 0.53 is in
# buster-backports, instead of split('.')[0]
# test_name = fs.replace_suffix(source, '')
# The test_name is the name of the source file without the file suffix.
test_name = source.split('.')[0]
test_dep = [dependency('threads')]
if test_dict.get('gtest', false)
test_dep += gtest_dep
endif
test_exe = executable(test_name, source,
include_directories: [include_dir],
#link_with: beehive_hash_table_lib,
dependencies: test_dep,
)
if test_dict.get('smoke_test', false)
test_suite = 'smoke'
else
test_suite = 'all'
endif
test(test_dict.get('description', ''),
test_exe,
is_parallel: test_dict.get('is_parallel', true),
suite: test_suite,
timeout: 60
)
endforeach
#!/usr/bin/env bash
set -euo pipefail
# Pretty fancy method to get reliable the absolute path of a shell
# script, *even if it is sourced*. Credits go to GreenFox on
# stackoverflow: http://stackoverflow.com/a/12197518/194894
pushd . > /dev/null
SCRIPTDIR="${BASH_SOURCE[0]}";
while([ -h "${SCRIPTDIR}" ]); do
cd "`dirname "${SCRIPTDIR}"`"
SCRIPTDIR="$(readlink "`basename "${SCRIPTDIR}"`")";
done
cd "`dirname "${SCRIPTDIR}"`" > /dev/null
SCRIPTDIR="`pwd`";
popd > /dev/null
DEBUG=false
while getopts d OPT; do
case $OPT in
d)
set -x
DEBUG=true
;;
*)
echo "usage: ${0##*/} [-dq} [--] ARGS..."
exit 2
esac
done
shift $(( OPTIND - 1 ))
OPTIND=1
ROOTDIR=$(readlink -f "${SCRIPTDIR}/..")
MAX_PROCS=$(nproc)
CHECKED_FILES_FILE=$(mktemp)
if ! $DEBUG; then
trap 'rm "${CHECKED_FILES_FILE}"' EXIT
fi
cd "${ROOTDIR}"
# Note that the --dry-run and --Werror clang-format arguments require
# clang-format 10 or higher. See https://reviews.llvm.org/D68554
find . \( -path './subprojects' -o -path '*/\.*' -o -path "./build*" \) -prune -o \
-type f -regextype posix-extended -regex '.*\.(c|h|cpp)' -print0 |\
tee "${CHECKED_FILES_FILE}" |\
xargs --null --max-args=3 --max-procs="${MAX_PROCS}" \
clang-format --style=file --dry-run -Werror
FILE_COUNT=$(<"${CHECKED_FILES_FILE}" tr -cd '\0' | wc -c)
echo "Checked ${FILE_COUNT} files for format violations"
Supports Markdown
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