From 75fbadefcaf2c0e4311ae3204a7bd3e30e256eb7 Mon Sep 17 00:00:00 2001 From: Martin Quinson Date: Wed, 22 Feb 2023 16:54:18 +0100 Subject: [PATCH] Implement the semaphore functions in sthread --- ChangeLog | 1 + MANIFEST.in | 3 + examples/sthread/CMakeLists.txt | 3 +- .../sthread/pthread-mc-producer-consumer.tesh | 7 ++ examples/sthread/pthread-producer-consumer.c | 83 ++++++++++++++ .../sthread/pthread-producer-consumer.tesh | 15 +++ src/sthread/sthread.c | 107 +++++++++++++++--- src/sthread/sthread.h | 15 +++ src/sthread/sthread_impl.cpp | 66 ++++++++--- 9 files changed, 263 insertions(+), 37 deletions(-) create mode 100644 examples/sthread/pthread-mc-producer-consumer.tesh create mode 100644 examples/sthread/pthread-producer-consumer.c create mode 100644 examples/sthread/pthread-producer-consumer.tesh diff --git a/ChangeLog b/ChangeLog index 0d62c29c1a..8776e692e5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -43,6 +43,7 @@ Models: sthread: - Implement pthread_join in MC mode. + - Implement semaphore functions in sthread. XBT: - simgrid::xbt::cmdline and simgrid::xbt::binary_name are gone. diff --git a/MANIFEST.in b/MANIFEST.in index d0b59a996a..67bc243f6b 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -597,9 +597,12 @@ include examples/smpi/trace_simple/trace_simple.c include examples/smpi/trace_simple/trace_simple.tesh include examples/sthread/pthread-mc-mutex-simple.tesh include examples/sthread/pthread-mc-mutex-simpledeadlock.tesh +include examples/sthread/pthread-mc-producer-consumer.tesh include examples/sthread/pthread-mutex-simple.c include examples/sthread/pthread-mutex-simple.tesh include examples/sthread/pthread-mutex-simpledeadlock.c +include examples/sthread/pthread-producer-consumer.c +include examples/sthread/pthread-producer-consumer.tesh include examples/sthread/sthread-mutex-simple.c include examples/sthread/sthread-mutex-simple.tesh include teshsuite/catch_simgrid.hpp diff --git a/examples/sthread/CMakeLists.txt b/examples/sthread/CMakeLists.txt index a59d61f650..d53f419c2c 100644 --- a/examples/sthread/CMakeLists.txt +++ b/examples/sthread/CMakeLists.txt @@ -5,7 +5,8 @@ find_package(Threads REQUIRED) ######################################################################### foreach(x - mutex-simple) + mutex-simple + producer-consumer) if("${CMAKE_SYSTEM}" MATCHES "Linux") add_executable (pthread-${x} EXCLUDE_FROM_ALL pthread-${x}.c) diff --git a/examples/sthread/pthread-mc-producer-consumer.tesh b/examples/sthread/pthread-mc-producer-consumer.tesh new file mode 100644 index 0000000000..c2714170ea --- /dev/null +++ b/examples/sthread/pthread-mc-producer-consumer.tesh @@ -0,0 +1,7 @@ +# We ignore the LD_PRELOAD lines from the expected output because they contain the build path +! ignore .*LD_PRELOAD.* + +$ ${bindir:=.}/../../bin/simgrid-mc --cfg=model-check/setenv:LD_PRELOAD=${libdir:=.}/libsgmalloc.so:${libdir:=.}/libsthread.so ${bindir:=.}/pthread-producer-consumer -q +> [0.000000] [sthread/INFO] Starting the simulation. +> [0.000000] [mc_dfs/INFO] Start a DFS exploration. Reduction is: dpor. +> [0.000000] [mc_dfs/INFO] DFS exploration ended. 1819 unique states visited; 108 backtracks (6808 transition replays, 4882 states visited overall) \ No newline at end of file diff --git a/examples/sthread/pthread-producer-consumer.c b/examples/sthread/pthread-producer-consumer.c new file mode 100644 index 0000000000..bc0755036f --- /dev/null +++ b/examples/sthread/pthread-producer-consumer.c @@ -0,0 +1,83 @@ +/* Copyright (c) 2002-2023. The SimGrid Team. All rights reserved. */ + +/* This program is free software; you can redistribute it and/or modify it + * under the terms of the license (GNU LGPL) which comes with this package. */ + +/* Simple producer/consumer example with pthreads and semaphores */ + +#include +#include +#include +#include +#include + +#define AmountProduced 3 /* Amount of items produced by a producer */ +#define AmountConsumed 3 /* Amount of items consumed by a consumer */ +#define ProducerCount 2 /* Amount of producer threads*/ +#define ConsumerCount 2 /* Amount of consumer threads*/ +#define BufferSize 4 /* Size of the buffer */ + +sem_t empty; +sem_t full; +int in = 0; +int out = 0; +int buffer[BufferSize]; +pthread_mutex_t mutex; +int do_output = 1; + +static void* producer(void* id) +{ + for (int i = 0; i < AmountProduced; i++) { + sem_wait(&empty); + pthread_mutex_lock(&mutex); + buffer[in] = i; + if (do_output) + fprintf(stderr, "Producer %d: Insert Item %d at %d\n", *((int*)id), buffer[in], in); + in = (in + 1) % BufferSize; + pthread_mutex_unlock(&mutex); + sem_post(&full); + } + return NULL; +} +static void* consumer(void* id) +{ + for (int i = 0; i < AmountConsumed; i++) { + sem_wait(&full); + pthread_mutex_lock(&mutex); + int item = buffer[out]; + if (do_output) + fprintf(stderr, "Consumer %d: Remove Item %d from %d\n", *((int*)id), item, out); + out = (out + 1) % BufferSize; + pthread_mutex_unlock(&mutex); + sem_post(&empty); + } + return NULL; +} + +int main(int argc, char** argv) +{ + if (argc == 2 && strcmp(argv[1], "-q") == 0) + do_output = 0; + pthread_t pro[2], con[2]; + pthread_mutex_init(&mutex, NULL); + sem_init(&empty, 0, BufferSize); + sem_init(&full, 0, 0); + + int ids[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; // The identity of each thread (for debug messages) + + for (int i = 0; i < ProducerCount; i++) + pthread_create(&pro[i], NULL, (void*)producer, (void*)&ids[i]); + for (int i = 0; i < ConsumerCount; i++) + pthread_create(&con[i], NULL, (void*)consumer, (void*)&ids[i]); + + for (int i = 0; i < ProducerCount; i++) + pthread_join(pro[i], NULL); + for (int i = 0; i < ConsumerCount; i++) + pthread_join(con[i], NULL); + + pthread_mutex_destroy(&mutex); + sem_destroy(&empty); + sem_destroy(&full); + + return 0; +} \ No newline at end of file diff --git a/examples/sthread/pthread-producer-consumer.tesh b/examples/sthread/pthread-producer-consumer.tesh new file mode 100644 index 0000000000..0c035f018e --- /dev/null +++ b/examples/sthread/pthread-producer-consumer.tesh @@ -0,0 +1,15 @@ +$ env ASAN_OPTIONS=verify_asan_link_order=0:$ASAN_OPTIONS LD_PRELOAD=${libdir:=.}/libsthread.so ./pthread-producer-consumer +> [0.000000] [sthread/INFO] Starting the simulation. +> Producer 1: Insert Item 0 at 0 +> Producer 2: Insert Item 0 at 1 +> Consumer 1: Remove Item 0 from 0 +> Producer 1: Insert Item 1 at 2 +> Consumer 2: Remove Item 0 from 1 +> Producer 2: Insert Item 1 at 3 +> Consumer 1: Remove Item 1 from 2 +> Producer 1: Insert Item 2 at 0 +> Consumer 2: Remove Item 1 from 3 +> Producer 2: Insert Item 2 at 1 +> Consumer 1: Remove Item 2 from 0 +> Consumer 2: Remove Item 2 from 1 +> [0.000000] [sthread/INFO] All threads exited. Terminating the simulation. diff --git a/src/sthread/sthread.c b/src/sthread/sthread.c index 3552ec231a..4d04e09f62 100644 --- a/src/sthread/sthread.c +++ b/src/sthread/sthread.c @@ -1,3 +1,8 @@ +/* Copyright (c) 2002-2023. The SimGrid Team. All rights reserved. */ + +/* This program is free software; you can redistribute it and/or modify it + * under the terms of the license (GNU LGPL) which comes with this package. */ + /* SimGrid's pthread interposer. Redefinition of the pthread symbols (see the comment in sthread.h) */ #define _GNU_SOURCE @@ -31,6 +36,9 @@ static sem_t* (*raw_sem_open)(const char*, int); static int (*raw_sem_init)(sem_t*, int, unsigned int); static int (*raw_sem_wait)(sem_t*); static int (*raw_sem_post)(sem_t*); +static int (*raw_sem_destroy)(sem_t*); +static int (*raw_sem_trywait)(sem_t*); +static int (*raw_sem_timedwait)(sem_t*, const struct timespec*); static void intercepter_init() { @@ -50,6 +58,9 @@ static void intercepter_init() raw_sem_init = dlsym(RTLD_NEXT, "sem_init"); raw_sem_wait = dlsym(RTLD_NEXT, "sem_wait"); raw_sem_post = dlsym(RTLD_NEXT, "sem_post"); + raw_sem_destroy = dlsym(RTLD_NEXT, "sem_destroy"); + raw_sem_trywait = dlsym(RTLD_NEXT, "sem_trywait"); + raw_sem_timedwait = dlsym(RTLD_NEXT, "sem_timedwait"); } static int sthread_inside_simgrid = 1; @@ -155,6 +166,84 @@ int pthread_mutex_destroy(pthread_mutex_t* mutex) sthread_enable(); return res; } +int sem_init(sem_t* sem, int pshared, unsigned int value) +{ + if (raw_sem_init == NULL) + intercepter_init(); + + if (sthread_inside_simgrid) + return raw_sem_init(sem, pshared, value); + + sthread_disable(); + int res = sthread_sem_init((sthread_sem_t*)sem, pshared, value); + sthread_enable(); + return res; +} +int sem_destroy(sem_t* sem) +{ + if (raw_sem_destroy == NULL) + intercepter_init(); + + if (sthread_inside_simgrid) + return raw_sem_destroy(sem); + + sthread_disable(); + int res = sthread_sem_destroy((sthread_sem_t*)sem); + sthread_enable(); + return res; +} +int sem_post(sem_t* sem) +{ + if (raw_sem_post == NULL) + intercepter_init(); + + if (sthread_inside_simgrid) + return raw_sem_post(sem); + + sthread_disable(); + int res = sthread_sem_post((sthread_sem_t*)sem); + sthread_enable(); + return res; +} +int sem_wait(sem_t* sem) +{ + if (raw_sem_wait == NULL) + intercepter_init(); + + if (sthread_inside_simgrid) + return raw_sem_wait(sem); + + sthread_disable(); + int res = sthread_sem_wait((sthread_sem_t*)sem); + sthread_enable(); + return res; +} +int sem_trywait(sem_t* sem) +{ + if (raw_sem_trywait == NULL) + intercepter_init(); + + if (sthread_inside_simgrid) + return raw_sem_trywait(sem); + + sthread_disable(); + int res = sthread_sem_trywait((sthread_sem_t*)sem); + sthread_enable(); + return res; +} +int sem_timedwait(sem_t* sem, const struct timespec* abs_timeout) +{ + if (raw_sem_timedwait == NULL) + intercepter_init(); + + if (sthread_inside_simgrid) + return raw_sem_timedwait(sem, abs_timeout); + + sthread_disable(); + int res = sthread_sem_timedwait((sthread_sem_t*)sem, abs_timeout); + sthread_enable(); + return res; +} /* Glibc < 2.31 uses type "struct timezone *" for the second parameter of gettimeofday. Other implementations use "void *" instead. */ @@ -210,24 +299,6 @@ int usleep(useconds_t usec) } #if 0 -int sem_init(sem_t *sem, int pshared, unsigned int value) { - int res; - - res=raw_sem_init(sem,pshared,value); - return res; -} - -int sem_wait(sem_t *sem) { - int res; - - res = raw_sem_wait(sem); - return res; -} - -int sem_post(sem_t *sem) { - return raw_sem_post(sem); -} - int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr) { *cond = sg_cond_init(); return 0; diff --git a/src/sthread/sthread.h b/src/sthread/sthread.h index 3bda35c236..da9b453dce 100644 --- a/src/sthread/sthread.h +++ b/src/sthread/sthread.h @@ -1,3 +1,8 @@ +/* Copyright (c) 2002-2023. The SimGrid Team. All rights reserved. */ + +/* This program is free software; you can redistribute it and/or modify it + * under the terms of the license (GNU LGPL) which comes with this package. */ + /* SimGrid's pthread interposer. Intercepts most of the pthread and semaphore calls to model-check them. * * Intercepting on pthread is somewhat complicated by the fact that pthread is used everywhere in the system headers. @@ -35,6 +40,16 @@ int sthread_mutex_trylock(sthread_mutex_t* mutex); int sthread_mutex_unlock(sthread_mutex_t* mutex); int sthread_mutex_destroy(sthread_mutex_t* mutex); +typedef struct { + void* sem; +} sthread_sem_t; +int sthread_sem_init(sthread_sem_t* sem, int pshared, unsigned int value); +int sthread_sem_destroy(sthread_sem_t* sem); +int sthread_sem_post(sthread_sem_t* sem); +int sthread_sem_wait(sthread_sem_t* sem); +int sthread_sem_trywait(sthread_sem_t* sem); +int sthread_sem_timedwait(sthread_sem_t* sem, const struct timespec* abs_timeout); + int sthread_gettimeofday(struct timeval* tv); void sthread_sleep(double seconds); diff --git a/src/sthread/sthread_impl.cpp b/src/sthread/sthread_impl.cpp index c4aa27d0c8..2e2af7e0eb 100644 --- a/src/sthread/sthread_impl.cpp +++ b/src/sthread/sthread_impl.cpp @@ -1,3 +1,8 @@ +/* Copyright (c) 2002-2023. The SimGrid Team. All rights reserved. */ + +/* This program is free software; you can redistribute it and/or modify it + * under the terms of the license (GNU LGPL) which comes with this package. */ + /* SimGrid's pthread interposer. Actual implementation of the symbols (see the comment in sthread.h) */ #include "smpi/smpi.h" @@ -7,6 +12,7 @@ #include #include #include +#include #include #include @@ -146,6 +152,48 @@ int sthread_mutex_destroy(sthread_mutex_t* mutex) intrusive_ptr_release(static_cast(mutex->mutex)); return 0; } +int sthread_sem_init(sthread_sem_t* sem, int pshared, unsigned int value) +{ + auto s = sg4::Semaphore::create(value); + intrusive_ptr_add_ref(s.get()); + + sem->sem = s.get(); + return 0; +} +int sthread_sem_destroy(sthread_sem_t* sem) +{ + intrusive_ptr_release(static_cast(sem->sem)); + return 0; +} +int sthread_sem_post(sthread_sem_t* sem) +{ + static_cast(sem->sem)->release(); + return 0; +} +int sthread_sem_wait(sthread_sem_t* sem) +{ + static_cast(sem->sem)->acquire(); + return 0; +} +int sthread_sem_trywait(sthread_sem_t* sem) +{ + auto* s = static_cast(sem->sem); + if (s->would_block()) { + errno = EAGAIN; + return -1; + } + s->acquire(); + return 0; +} +int sthread_sem_timedwait(sthread_sem_t* sem, const struct timespec* abs_timeout) +{ + if (static_cast(sem->sem)->acquire_timeout(abs_timeout->tv_sec + + static_cast(abs_timeout->tv_nsec) / 1E9)) { + errno = ETIMEDOUT; + return -1; + } + return 0; +} int sthread_gettimeofday(struct timeval* tv) { @@ -165,24 +213,6 @@ void sthread_sleep(double seconds) } #if 0 -int sem_init(sem_t *sem, int pshared, unsigned int value) { - int res; - - res=raw_sem_init(sem,pshared,value); - return res; -} - -int sem_wait(sem_t *sem) { - int res; - - res = raw_sem_wait(sem); - return res; -} - -int sem_post(sem_t *sem) { - return raw_sem_post(sem); -} - int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr) { *cond = sg_cond_init(); return 0; -- 2.20.1