MXS-2067: Replace SPINLOCK with pthread_mutex_t
Replaced the SPINLOCK implementation with pthread_mutex_t. The SPINLOCK interface is still used and will be removed later on.
This commit is contained in:
parent
fe81b399b2
commit
fc1e36429c
@ -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 <maxscale/cdefs.h>
|
||||
#include <stdbool.h>
|
||||
#include <pthread.h>
|
||||
|
||||
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
|
||||
|
@ -43,7 +43,6 @@ add_library(maxscale-common SHARED
|
||||
service.cc
|
||||
session.cc
|
||||
session_command.cc
|
||||
spinlock.cc
|
||||
ssl.cc
|
||||
users.cc
|
||||
utils.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;
|
||||
|
||||
|
@ -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 <maxscale/spinlock.h>
|
||||
#include <maxbase/assert.h>
|
||||
#include <maxbase/atomic.h>
|
||||
#include <time.h>
|
||||
|
||||
|
||||
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
|
||||
}
|
@ -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)
|
||||
|
@ -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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <thread>
|
||||
|
||||
#include <maxscale/spinlock.h>
|
||||
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user