diff --git a/include/maxscale/spinlock.h b/include/maxscale/spinlock.h index 0d49ed5a0..12c7edaee 100644 --- a/include/maxscale/spinlock.h +++ b/include/maxscale/spinlock.h @@ -12,103 +12,16 @@ */ #pragma once -/** - * @file spinlock.h - * - * Spinlock implementation for MaxScale. - * - * Spinlocks are cheap locks that can be used to protect short code blocks, they are - * generally wasteful as any blocked threads will spin, consuming CPU cycles, waiting - * for the lock to be released. However they are useful in that they do not involve - * system calls and are light weight when the expected wait time for a lock is low. - */ - #include #include +#include MXS_BEGIN_DECLS -#define SPINLOCK_PROFILE 0 - -/** - * The spinlock structure. - * - * In normal builds the structure merely contains a lock value which - * is 0 if the spinlock is not taken and greater than zero if it is held. - * - * In builds with the SPINLOCK_PROFILE option set this structure also holds - * a number of profile related fields that count the number of spins, number - * of waiting threads and the number of times the lock has been acquired. - */ -typedef struct spinlock -{ - int lock; /*< Is the lock held? */ -#if SPINLOCK_PROFILE - uint64_t spins; /*< Number of spins on this lock */ - uint64_t maxspins; /*< Max no of spins to acquire lock */ - uint64_t acquired; /*< No. of times lock was acquired */ - uint64_t waiting; /*< No. of threads acquiring this lock */ - uint64_t max_waiting; /*< Max no of threads waiting for lock */ - uint64_t contended; /*< No. of times acquire was contended */ - THREAD owner; /*< Last owner of this lock */ -#endif -} SPINLOCK; - -#if SPINLOCK_PROFILE -#define SPINLOCK_INIT {0, 0, 0, 0, 0, 0, 0, 0} -#else -#define SPINLOCK_INIT {0} -#endif - -/** - * Debugging macro for testing the state of a spinlock. - * - * @attention ONLY to be used in debugging context. - */ -#define SPINLOCK_IS_LOCKED(l) ((l)->lock != 0 ? true : false) - -/** - * Initialise a spinlock. - * - * @param lock The spinlock to initialise. - */ -extern void spinlock_init(SPINLOCK* lock); - -/** - * Acquire a spinlock. - * - * @param lock The spinlock to acquire - */ -extern void spinlock_acquire(const SPINLOCK* lock); - -/** - * Acquire a spinlock if it is not already locked. - * - * @param lock The spinlock to acquire - * @return True if the spinlock was acquired, otherwise false - */ -extern bool spinlock_acquire_nowait(const SPINLOCK* lock); - -/* - * Release a spinlock. - * - * @param lock The spinlock to release - */ -extern void spinlock_release(const SPINLOCK* lock); - -/** - * Report statistics on a spinlock. This only has an effect if the - * spinlock code has been compiled with the SPINLOCK_PROFILE option set. - * - * NB A callback function is used to return the data rather than - * merely printing to a DCB in order to avoid a dependency on the DCB - * form the spinlock code and also to facilitate other uses of the - * statistics reporting. - * - * @param lock The spinlock to report on - * @param reporter The callback function to pass the statistics to - * @param hdl A handle that is passed to the reporter function - */ -extern void spinlock_stats(const SPINLOCK* lock, void (* reporter)(void*, char*, int), void* hdl); +#define SPINLOCK pthread_mutex_t +#define SPINLOCK_INIT PTHREAD_MUTEX_INITIALIZER +#define spinlock_init(a) pthread_mutex_init(a, NULL) +#define spinlock_acquire(a) pthread_mutex_lock((pthread_mutex_t*)a) +#define spinlock_release(a) pthread_mutex_unlock((pthread_mutex_t*)a) MXS_END_DECLS diff --git a/server/core/CMakeLists.txt b/server/core/CMakeLists.txt index 8b66db098..b70e787cf 100644 --- a/server/core/CMakeLists.txt +++ b/server/core/CMakeLists.txt @@ -43,7 +43,6 @@ add_library(maxscale-common SHARED service.cc session.cc session_command.cc - spinlock.cc ssl.cc users.cc utils.cc diff --git a/server/core/server.cc b/server/core/server.cc index 5a978818e..02d7fdc91 100644 --- a/server/core/server.cc +++ b/server/core/server.cc @@ -945,7 +945,6 @@ static void server_parameter_free(SERVER_PARAM* tofree) */ static size_t server_get_parameter_nolock(const SERVER* server, const char* name, char* out, size_t size) { - mxb_assert(SPINLOCK_IS_LOCKED(&server->lock)); size_t len = 0; SERVER_PARAM* param = server->parameters; diff --git a/server/core/spinlock.cc b/server/core/spinlock.cc deleted file mode 100644 index b5988da3e..000000000 --- a/server/core/spinlock.cc +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (c) 2016 MariaDB Corporation Ab - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file and at www.mariadb.com/bsl11. - * - * Change Date: 2022-01-01 - * - * On the date above, in accordance with the Business Source License, use - * of this software will be governed by version 2 or later of the General - * Public License. - */ - -#include -#include -#include -#include - - -void spinlock_init(SPINLOCK* lock) -{ - lock->lock = 0; -#if SPINLOCK_PROFILE - lock->spins = 0; - lock->maxspins = 0; - lock->acquired = 0; - lock->waiting = 0; - lock->max_waiting = 0; - lock->contended = 0; - lock->owner = 0; -#endif -} - -void spinlock_acquire(const SPINLOCK* const_lock) -{ - SPINLOCK* lock = (SPINLOCK*)const_lock; -#if SPINLOCK_PROFILE - int spins = 0; - - atomic_add(&(lock->waiting), 1); -#endif - - while (__sync_lock_test_and_set(&(lock->lock), 1)) - { -#if SPINLOCK_PROFILE - atomic_add(&(lock->spins), 1); - spins++; -#endif - } - -#if SPINLOCK_PROFILE - if (spins) - { - lock->contended++; - if (lock->maxspins < spins) - { - lock->maxspins = spins; - } - } - lock->acquired++; - lock->owner = thread_self(); - atomic_add(&(lock->waiting), -1); -#endif -} - -bool spinlock_acquire_nowait(const SPINLOCK* const_lock) -{ - SPINLOCK* lock = (SPINLOCK*)const_lock; - if (__sync_lock_test_and_set(&(lock->lock), 1)) - { - return false; - } - -#if SPINLOCK_PROFILE - lock->acquired++; - lock->owner = thread_self(); -#endif - - return true; -} - -void spinlock_release(const SPINLOCK* const_lock) -{ - SPINLOCK* lock = (SPINLOCK*)const_lock; - mxb_assert(lock->lock != 0); -#if SPINLOCK_PROFILE - if (lock->waiting > lock->max_waiting) - { - lock->max_waiting = lock->waiting; - } -#endif - - __sync_lock_release(&lock->lock); -} - -void spinlock_stats(const SPINLOCK* lock, void (* reporter)(void*, char*, int), void* hdl) -{ -#if SPINLOCK_PROFILE - reporter(hdl, "Spinlock acquired", lock->acquired); - if (lock->acquired) - { - reporter(hdl, "Total no. of spins", lock->spins); - if (lock->acquired) - { - reporter(hdl, "Average no. of spins (overall)", lock->spins / lock->acquired); - } - if (lock->contended) - { - reporter(hdl, "Average no. of spins (when contended)", lock->spins / lock->contended); - } - reporter(hdl, "Maximum no. of spins", lock->maxspins); - reporter(hdl, "Maximim no. of blocked threads", lock->max_waiting); - reporter(hdl, "Contended locks", lock->contended); - if (lock->acquired) - { - reporter(hdl, "Contention percentage", (lock->contended * 100) / lock->acquired); - } - } -#endif -} diff --git a/server/core/test/CMakeLists.txt b/server/core/test/CMakeLists.txt index 022d1602e..049b9d8c8 100644 --- a/server/core/test/CMakeLists.txt +++ b/server/core/test/CMakeLists.txt @@ -19,7 +19,6 @@ add_executable(test_modutil test_modutil.cc) add_executable(test_poll test_poll.cc) add_executable(test_server test_server.cc) add_executable(test_service test_service.cc) -add_executable(test_spinlock test_spinlock.cc) add_executable(test_trxcompare test_trxcompare.cc ../../../query_classifier/test/testreader.cc) add_executable(test_trxtracking test_trxtracking.cc) add_executable(test_users test_users.cc) @@ -47,7 +46,6 @@ target_link_libraries(test_modutil maxscale-common) target_link_libraries(test_poll maxscale-common) target_link_libraries(test_server maxscale-common) target_link_libraries(test_service maxscale-common) -target_link_libraries(test_spinlock maxscale-common) target_link_libraries(test_trxcompare maxscale-common) target_link_libraries(test_trxtracking maxscale-common) target_link_libraries(test_users maxscale-common) @@ -74,7 +72,6 @@ add_test(test_modutil test_modutil) add_test(test_poll test_poll) add_test(test_server test_server) add_test(test_service test_service) -add_test(test_spinlock test_spinlock) add_test(test_trxcompare_create test_trxcompare ${CMAKE_CURRENT_SOURCE_DIR}/../../../query_classifier/test/create.test) add_test(test_trxcompare_delete test_trxcompare ${CMAKE_CURRENT_SOURCE_DIR}/../../../query_classifier/test/delete.test) add_test(test_trxcompare_insert test_trxcompare ${CMAKE_CURRENT_SOURCE_DIR}/../../../query_classifier/test/insert.test) diff --git a/server/core/test/test_spinlock.cc b/server/core/test/test_spinlock.cc deleted file mode 100644 index 4ffe5df9c..000000000 --- a/server/core/test/test_spinlock.cc +++ /dev/null @@ -1,266 +0,0 @@ -/* - * Copyright (c) 2016 MariaDB Corporation Ab - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file and at www.mariadb.com/bsl11. - * - * Change Date: 2022-01-01 - * - * On the date above, in accordance with the Business Source License, use - * of this software will be governed by version 2 or later of the General - * Public License. - */ - -/** - * - * @verbatim - * Revision History - * - * Date Who Description - * 18/08-2014 Mark Riddoch Initial implementation - * - * @endverbatim - */ - -// To ensure that ss_info_assert asserts also when builing in non-debug mode. -#if !defined (SS_DEBUG) -#define SS_DEBUG -#endif -#if defined (NDEBUG) -#undef NDEBUG -#endif -#include -#include -#include -#include - -#include - - -/** - * test1 spinlock_acquire_nowait tests - * - * Test that spinlock_acquire_nowait returns false if the spinlock - * is already taken. - * - * Test that spinlock_acquire_nowait returns true if the spinlock - * is not taken. - * - * Test that spinlock_acquire_nowait does hold the spinlock. - */ -static int test1() -{ - SPINLOCK lck; - - spinlock_init(&lck); - spinlock_acquire(&lck); - if (spinlock_acquire_nowait(&lck)) - { - fprintf(stderr, "spinlock_acquire_nowait: test 1.1 failed.\n"); - return 1; - } - spinlock_release(&lck); - if (!spinlock_acquire_nowait(&lck)) - { - fprintf(stderr, "spinlock_acquire_nowait: test 1.2 failed.\n"); - return 1; - } - if (spinlock_acquire_nowait(&lck)) - { - fprintf(stderr, "spinlock_acquire_nowait: test 1.3 failed.\n"); - return 1; - } - spinlock_release(&lck); - - return 0; -} - -static int acquire_time; - -static void test2_helper(void* data) -{ - SPINLOCK* lck = (SPINLOCK*)data; - unsigned long t1 = time(0); - - spinlock_acquire(lck); - acquire_time = time(0) - t1; - spinlock_release(lck); - return; -} - -/** - * test2 spinlock_acquire tests - * - * Check that spinlock correctly blocks another thread whilst the spinlock - * is held. - * - * Take out a lock. - * Start a second thread to take the same lock - * sleep for 10 seconds - * release lock - * verify that second thread took at least 8 seconds to obtain the lock - */ -static int test2() -{ - SPINLOCK lck; - std::thread handle; - struct timespec sleeptime; - - sleeptime.tv_sec = 10; - sleeptime.tv_nsec = 0; - - acquire_time = 0; - spinlock_init(&lck); - spinlock_acquire(&lck); - handle = std::thread(test2_helper, (void*)&lck); - nanosleep(&sleeptime, NULL); - spinlock_release(&lck); - handle.join(); - - if (acquire_time < 8) - { - fprintf(stderr, "spinlock: test 2 failed.\n"); - return 1; - } - return 0; -} - -/** - * test3 spinlock_acquire tests process bound threads - * - * Check that spinlock correctly blocks all other threads whilst the spinlock - * is held. - * - * Start multiple threads that obtain spinlock and run process bound - */ -#define THREADS 5 -#define ITERATIONS 50000 -#define PROCESS_LOOP 10000 -#define SECONDS 15 -#define NANOTIME 100000 - -static int times_run, failures; -static volatile int active; -static int threadrun[THREADS]; -static int nowait[THREADS]; -static SPINLOCK lck; -static void test3_helper(void* data) -{ -// SPINLOCK *lck = (SPINLOCK *)data; - int i; - int n = *(int*)data; - time_t rawtime; - -#if defined (ADD_SOME_NANOSLEEP) - struct timespec sleeptime; - - sleeptime.tv_sec = 0; - sleeptime.tv_nsec = 1; -#endif - - while (1) - { - if (spinlock_acquire_nowait(&lck)) - { - nowait[n]++; - } - else - { - spinlock_acquire(&lck); - } - if (times_run++ > ITERATIONS) - { - break; - } - threadrun[n]++; - /* - * if (99 == (times_run % 100)) { - * time ( &rawtime ); - * fprintf(stderr, "%s Done %d iterations of test, in thread %d.\n", asctime (localtime ( &rawtime - * )), times_run, n); - * } - */ - if (0 != active) - { - fprintf(stderr, "spinlock: test 3 failed with active non-zero after lock obtained.\n"); - failures++; - } - else - { - active = 1; - for (i = 0; i < PROCESS_LOOP; i++) - { - } - } - active = 0; - spinlock_release(&lck); - for (i = 0; i < (4 * PROCESS_LOOP); i++) - { - } -#if defined (ADD_SOME_NANOSLEEP) - nanosleep(&sleeptime, NULL); -#endif - } - spinlock_release(&lck); -} - -static int test3() -{ -// SPINLOCK lck; - std::thread handle[THREADS]; - int i; - int tnum[THREADS]; - time_t rawtime; - - times_run = 0; - active = 0; - failures = 0; - spinlock_init(&lck); - time (&rawtime); - fprintf(stderr, "%s Starting %d threads.\n", asctime (localtime (&rawtime)), THREADS); - for (i = 0; i < THREADS; i++) - { - threadrun[i] = 0; - tnum[i] = i; - handle[i] = std::thread(test3_helper, &tnum[i]); - } - for (i = 0; i < THREADS; i++) - { - fprintf(stderr, - "spinlock_test 3 thread %d ran %d times, no wait %d times before waits.\n", - i, - threadrun[i], - nowait[i]); - } - for (i = 0; i < THREADS; i++) - { - time (&rawtime); - fprintf(stderr, - "%s spinlock_test 3 finished sleeps, about to wait for thread %d.\n", - asctime (localtime (&rawtime)), - i); - handle[i].join(); - } - for (i = 0; i < THREADS; i++) - { - fprintf(stderr, - "spinlock_test 3 thread %d ran %d times, no wait %d times.\n", - i, - threadrun[i], - nowait[i]); - } - time (&rawtime); - fprintf(stderr, "%s spinlock_test 3 completed, %d failures.\n", asctime (localtime (&rawtime)), failures); - return 0 == failures ? 0 : 1; -} - -int main(int argc, char** argv) -{ - int result = 0; - - result += test1(); - result += test2(); - result += test3(); - - exit(result); -}