Skip to content

Commit

Permalink
Add a small multithreading test case
Browse files Browse the repository at this point in the history
This adds a test case that just spawns a bunch of threads quickly and
calls efi_get_variable() on an empty directory.  It fails pretty
reliably without either of the following commits:

65a3ad0 error.c: make our 'global' state be thread local
3d19e7d efivarfs.c: Call get_efivarfs_path() from a constructor

Signed-off-by: Peter Jones <pjones@redhat.com>
  • Loading branch information
marler8997 authored and vathpela committed Oct 15, 2020
1 parent 766772c commit 73fae6c
Show file tree
Hide file tree
Showing 6 changed files with 173 additions and 7 deletions.
5 changes: 0 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
*.map
*.pc
*.S
!src/guids.S
*.so
*.so.*
*.spec
Expand All @@ -22,7 +21,3 @@ core.*
cov-int
vgcore.*
scan-results/
src/efivar
src/efivar-static
src/makeguids
src/guid-symbols.c
6 changes: 6 additions & 0 deletions src/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
!guids.S
*.bin
efivar
efivar-guids.h
efivar-static
makeguids
guid-symbols.c
thread-test
6 changes: 5 additions & 1 deletion src/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ include $(TOPDIR)/src/include/defaults.mk

LIBTARGETS=libefivar.so libefiboot.so
STATICLIBTARGETS=libefivar.a libefiboot.a
BINTARGETS=efivar
BINTARGETS=efivar thread-test
STATICBINTARGETS=efivar-static
PCTARGETS=efivar.pc efiboot.pc
TARGETS=$(LIBTARGETS) $(BINTARGETS) $(PCTARGETS)
Expand Down Expand Up @@ -86,6 +86,10 @@ libefiboot.so : | libefiboot.map libefivar.so
libefiboot.so : LIBS=efivar
libefiboot.so : MAP=libefiboot.map

thread-test : thread-test.o
thread-test : CFLAGS=$(HOST_CFLAGS) -I$(TOPDIR)/src/include/efivar
thread-test : LIBS=pthread efivar

deps : $(ALL_SOURCES)
@$(MAKE) -f $(SRCDIR)/include/deps.mk deps SOURCES="$(ALL_SOURCES)"

Expand Down
117 changes: 117 additions & 0 deletions src/thread-test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
/*
* thread-test.c - test (some) efivar thread safety
* Copyright Jonathan Marler
*/

#include <alloca.h>
#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <sys/types.h>
#include <unistd.h>

// returns: the number of threads created
static size_t
multi_pthread_create(pthread_t * threads, unsigned count,
void *(*start_routine)(void *))
{
size_t i = 0;
int result;

for (; i < count; i++) {
result = pthread_create(&threads[i], NULL, start_routine, NULL);
if (result != 0) {
perror("pthread_create");
break;
}
}

return i;
}

#define TEST_SUCCESS NULL
#define TEST_FAIL ((void*)1)
static const char *test_result_str(void *result)
{
return (result == TEST_SUCCESS) ? "SUCCESS" : "FAIL";
}

// returns: 0 on success
static int
multi_pthread_join(pthread_t * threads, unsigned count, void **worst_result)
{
for (unsigned i = 0; i < count; i++) {
void *result;
int join_result;

join_result = pthread_join(threads[i], &result);
if (join_result != 0) {
perror("pthread_join");
return 1;
}
printf("[MAIN-THREAD] child %d exited with %s\n", i,
test_result_str(result));
if (result != NULL) {
*worst_result = result;
}
}
return 0;
}

#include <efivar.h>
#define LOOP_COUNT 100

static void *loop_get_variable_size_test(void *_ __attribute__((__unused__)))
{
//printf("[DEBUG] test running on new thread!\n");
for (unsigned i = 0; i < LOOP_COUNT; i++) {
size_t size;
efi_guid_t guid = { 0 };
int result;

result = efi_get_variable_size(guid, "foo2", &size);
if (result == 0 || errno != ENOENT) {
printf("fail, iteration=%u, result=%d errno=%d, expected error\n",
i, result, errno);
return TEST_FAIL;
} else {
//printf("[DEBUG] iteration=%u, result=%d errno=%d\n", i, result, errno);
}
}
return TEST_SUCCESS;
}

static int multithreaded_test(size_t count, void *(*test_func)(void *))
{
pthread_t *threads = alloca(sizeof(pthread_t) * count);
if (count != multi_pthread_create(threads, count, test_func)) {
// error already logged
return 1;
}
void *worst_result = TEST_SUCCESS;
if (multi_pthread_join(threads, count, &worst_result)) {
// error already logged
return 1;
}
printf("worst result %s\n", test_result_str(worst_result));
return (worst_result == TEST_SUCCESS) ? 0 : -1;
}

int main(int argc, char *argv[])
{
int thread_count;

if (argc != 2) {
printf("Usage: %s THREAD_COUNT\n", argv[0]);
return 1;
}

thread_count = atoi(argv[1]);
printf("thread count %d\n", thread_count);
return multithreaded_test(thread_count, loop_get_variable_size_test);
}

// vim:fenc=utf-8:tw=75:noet
6 changes: 5 additions & 1 deletion tests/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# Peter Jones, 2019-06-18 11:10
#

all: clean test0 test1 test2 test3 test4
all: clean test0 test1 test2 test3 test4 test5

GRUB_PREFIX ?= grub2
EFIVAR ?= $(TOPDIR)/src/efivar
Expand Down Expand Up @@ -80,6 +80,10 @@ test4:
@rm test.4.0.result.var test.4.1.result.var
@echo passed

test5:
@echo testing threading in libefivar
@TOPDIR=$(TOPDIR) $(TOPDIR)/tests/test-threading

.PHONY: all clean test0
# vim:ft=make
#
40 changes: 40 additions & 0 deletions tests/test-threading
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#!/usr/bin/env sh
# SPDX-License-Identifier: LGPL-2.1-or-later
# test (some) efivar thread safety
#
# Copyright Jonathan Marler

set -e

if [ "x$TOPDIR" = "x" ] ; then
TOPDIR="$(realpath "$(dirname "$0")/../")"
fi

rm -rf scratch
mkdir scratch

EFIVARFS_PATH=""
LD_LIBRARY_PATH=""
LIBEFIVAR_OPS=""

EFIVARFS_PATH=$(realpath scratch)/
LD_LIBRARY_PATH="${TOPDIR}/src/"
LIBEFIVAR_OPS=efivarfs
export EFIVARFS_PATH LD_LIBRARY_PATH LIBEFIVAR_OPS

test() {
echo "================================================================================"
echo "testing $1 thread..."
echo "================================================================================"
if "${TOPDIR}/src/thread-test" "$1" ; then
echo "${1} thread worked"
else
echo "${1} thread failed"
exit 1
fi
}

test 1
test 2
test 4
test 64

0 comments on commit 73fae6c

Please sign in to comment.