Port threaded LFReg test to C
Project: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/repo Commit: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/commit/3ed5a19f Tree: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/tree/3ed5a19f Diff: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/diff/3ed5a19f Branch: refs/heads/master Commit: 3ed5a19f1448353cb0eae914867df6c30380c9c5 Parents: b083881 Author: Nick Wellnhofer <[email protected]> Authored: Sun May 10 01:48:29 2015 +0200 Committer: Nick Wellnhofer <[email protected]> Committed: Tue May 12 20:15:25 2015 +0200 ---------------------------------------------------------------------- runtime/common/charmonizer.c | 1 - runtime/common/charmonizer.main | 1 - runtime/core/Clownfish/LockFreeRegistry.c | 11 +++ .../core/Clownfish/Test/TestLockFreeRegistry.c | 99 +++++++++++++++++++- runtime/perl/t/binding/038-lock_free_registry.t | 82 ---------------- 5 files changed, 108 insertions(+), 86 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/3ed5a19f/runtime/common/charmonizer.c ---------------------------------------------------------------------- diff --git a/runtime/common/charmonizer.c b/runtime/common/charmonizer.c index e3f4f2f..16156a2 100644 --- a/runtime/common/charmonizer.c +++ b/runtime/common/charmonizer.c @@ -8381,7 +8381,6 @@ S_need_libpthread(chaz_CLI *cli) { chaz_CFlags *temp_cflags; if (chaz_CLI_defined(cli, "disable-threads") - || strcmp(chaz_CLI_strval(cli, "host"), "c") != 0 || chaz_HeadCheck_check_header("windows.h") ) { return 0; http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/3ed5a19f/runtime/common/charmonizer.main ---------------------------------------------------------------------- diff --git a/runtime/common/charmonizer.main b/runtime/common/charmonizer.main index a425d10..35500fc 100644 --- a/runtime/common/charmonizer.main +++ b/runtime/common/charmonizer.main @@ -622,7 +622,6 @@ S_need_libpthread(chaz_CLI *cli) { chaz_CFlags *temp_cflags; if (chaz_CLI_defined(cli, "disable-threads") - || strcmp(chaz_CLI_strval(cli, "host"), "c") != 0 || chaz_HeadCheck_check_header("windows.h") ) { return 0; http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/3ed5a19f/runtime/core/Clownfish/LockFreeRegistry.c ---------------------------------------------------------------------- diff --git a/runtime/core/Clownfish/LockFreeRegistry.c b/runtime/core/Clownfish/LockFreeRegistry.c index 5c6315f..2385f02 100644 --- a/runtime/core/Clownfish/LockFreeRegistry.c +++ b/runtime/core/Clownfish/LockFreeRegistry.c @@ -86,9 +86,20 @@ FIND_END_OF_LINKED_LIST: * while we were allocating that new node), the compare-and-swap will * fail. If that happens, we have to go back and find the new end of the * linked list, then try again. */ +#if 1 if (!Atomic_cas_ptr((void*volatile*)slot, NULL, new_entry)) { goto FIND_END_OF_LINKED_LIST; } +#else + // This non-atomic version can be used to check whether the test suite + // catches any race conditions. + if (*slot == NULL) { + *slot = new_entry; + } + else { + goto FIND_END_OF_LINKED_LIST; + } +#endif return true; } http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/3ed5a19f/runtime/core/Clownfish/Test/TestLockFreeRegistry.c ---------------------------------------------------------------------- diff --git a/runtime/core/Clownfish/Test/TestLockFreeRegistry.c b/runtime/core/Clownfish/Test/TestLockFreeRegistry.c index b030984..3e7edf1 100644 --- a/runtime/core/Clownfish/Test/TestLockFreeRegistry.c +++ b/runtime/core/Clownfish/Test/TestLockFreeRegistry.c @@ -14,6 +14,7 @@ * limitations under the License. */ +#include <stdlib.h> #include <string.h> #define CFISH_USE_SHORT_NAMES @@ -21,11 +22,23 @@ #include "Clownfish/Test/TestLockFreeRegistry.h" +#include "Clownfish/Class.h" #include "Clownfish/LockFreeRegistry.h" #include "Clownfish/String.h" #include "Clownfish/Test.h" #include "Clownfish/TestHarness/TestBatchRunner.h" -#include "Clownfish/Class.h" +#include "Clownfish/TestHarness/TestUtils.h" +#include "Clownfish/Util/Memory.h" + +#define NUM_THREADS 5 + +typedef struct ThreadArgs { + LockFreeRegistry *registry; + uint32_t *nums; + uint32_t num_objs; + uint64_t target_time; + uint32_t succeeded; +} ThreadArgs; TestLockFreeRegistry* TestLFReg_new() { @@ -63,10 +76,92 @@ test_all(TestBatchRunner *runner) { LFReg_destroy(registry); } +static void +S_register_many(void *varg) { + ThreadArgs *args = (ThreadArgs*)varg; + + // Encourage contention, so that all threads try to register at the same + // time. + + // Sleep until target_time. + uint64_t time = TestUtils_time(); + if (args->target_time > time) { + TestUtils_usleep(args->target_time - time); + } + + TestUtils_thread_yield(); + + uint32_t succeeded = 0; + for (uint32_t i = 0; i < args->num_objs; i++) { + String *obj = Str_newf("%u32", args->nums[i]); + if (LFReg_register(args->registry, obj, (Obj*)obj)) { + succeeded++; + } + DECREF(obj); + } + + args->succeeded = succeeded; +} + +static void +test_threads(TestBatchRunner *runner) { + if (!TestUtils_has_threads) { + SKIP(runner, 1, "No thread support"); + return; + } + + LockFreeRegistry *registry = LFReg_new(32); + ThreadArgs thread_args[NUM_THREADS]; + uint32_t num_objs = 10000; + + for (uint32_t i = 0; i < NUM_THREADS; i++) { + uint32_t *nums = (uint32_t*)MALLOCATE(num_objs * sizeof(uint32_t)); + + for (uint32_t j = 0; j < num_objs; j++) { + nums[j] = j; + } + + // Fisher-Yates shuffle. + for (uint32_t j = num_objs - 1; j > 0; j--) { + uint32_t r = TestUtils_random_u64() % (j + 1); + uint32_t tmp = nums[j]; + nums[j] = nums[r]; + nums[r] = tmp; + } + + thread_args[i].registry = registry; + thread_args[i].nums = nums; + thread_args[i].num_objs = num_objs; + } + + Thread *threads[NUM_THREADS]; + uint64_t target_time = TestUtils_time() + 200 * 1000; + + for (uint32_t i = 0; i < NUM_THREADS; i++) { + thread_args[i].target_time = target_time; + threads[i] = TestUtils_thread_create(S_register_many, &thread_args[i]); + } + + uint32_t total_succeeded = 0; + + for (uint32_t i = 0; i < NUM_THREADS; i++) { + TestUtils_thread_join(threads[i]); + total_succeeded += thread_args[i].succeeded; + FREEMEM(thread_args[i].nums); + } + + TEST_INT_EQ(runner, total_succeeded, num_objs, + "registered exactly the right number of entries across all" + " threads"); + + LFReg_destroy(registry); +} + void TestLFReg_Run_IMP(TestLockFreeRegistry *self, TestBatchRunner *runner) { - TestBatchRunner_Plan(runner, (TestBatch*)self, 6); + TestBatchRunner_Plan(runner, (TestBatch*)self, 7); test_all(runner); + test_threads(runner); } http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/3ed5a19f/runtime/perl/t/binding/038-lock_free_registry.t ---------------------------------------------------------------------- diff --git a/runtime/perl/t/binding/038-lock_free_registry.t b/runtime/perl/t/binding/038-lock_free_registry.t deleted file mode 100644 index 4793884..0000000 --- a/runtime/perl/t/binding/038-lock_free_registry.t +++ /dev/null @@ -1,82 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -use strict; -use warnings; - -use Config; -use Test::More; -BEGIN { - if ( $ENV{LUCY_VALGRIND} ) { - plan( skip_all => 'Known leaks' ); - } - elsif ( !defined( $ENV{LUCY_DEBUG} ) ) { - plan( skip_all => 'Debug-only test' ); - } - elsif ( $Config{usethreads} and $^O !~ /mswin/i ) { - plan( tests => 1 ); - } - else { - plan( skip_all => 'No thread support' ); - } -} -use threads; -use threads::shared; -use Time::HiRes qw( time usleep ); -use List::Util qw( shuffle ); -use Clownfish; - -package ImmortalString; -use base qw(Clownfish::String); - -sub DESTROY {} - -package main; - -my $registry = Clownfish::LockFreeRegistry->new( capacity => 32 ); - -sub register_many { - my ( $nums, $delay ) = @_; - - # Encourage contention, so that all threads try to register at the same - # time. - sleep $delay; - threads->yield(); - - my $succeeded = 0; - for my $number (@$nums) { - my $obj = ImmortalString->new($number); - $succeeded += $registry->register( key => $obj, value => $obj ); - } - - return $succeeded; -} - -my @threads; - -my $target_time = time() + .5; -my @num_sets = map { [ shuffle( 1 .. 10000 ) ] } 1 .. 5; -for my $num ( 1 .. 5 ) { - my $delay = $target_time - time(); - my $thread = threads->create( \®ister_many, pop @num_sets, $delay ); - push @threads, $thread; -} - -my $total_succeeded = 0; -$total_succeeded += $_->join for @threads; - -is( $total_succeeded, 10000, - "registered exactly the right number of entries across all threads" ); -
