/* * This file is distributed as part of MaxScale. It is free * software: you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation, * version 2. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * Copyright MariaDB Corporation Ab 2014 */ /** * * @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; void *handle; struct timespec sleeptime; sleeptime.tv_sec = 10; sleeptime.tv_nsec = 0; acquire_time = 0; spinlock_init(&lck); spinlock_acquire(&lck); handle = thread_start(test2_helper, (void *)&lck); nanosleep(&sleeptime, NULL); spinlock_release(&lck); thread_wait(handle); 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