Author: stefan2 Date: Thu Apr 12 11:23:00 2012 New Revision: 1325206 URL: http://svn.apache.org/viewvc?rev=1325206&view=rev Log: On the reprop-cache branch: Complete test implementation. - add multi-process test and share its code with the multi-threaded one - automatically determine the number of HW threads and the suggested number of test iterations on that machine
* build.conf (named_atomic-test-proc): new exe target (__ALL_TESTS__): add it here as dependency * subversion/tests/libsvn_subr/named_atomic-test-common.h new file, replaces the old test_pipeline_thread() implementation * subversion/tests/libsvn_subr/named_atomic-test-proc.c worker process working similarly to the old test_pipeline_thread() code * subversion/tests/libsvn_subr/named_atomic-test.c (TEST_PROC): new define (THREAD_COUNT): drop; determined dynamically now (hw_thread_count, suggested_iterations): dynamically elicited parameters (fail): drop unused function (init_test_shm): clear fixed data only (init_concurrency_test_shm): new; clears dynamic data (thread_func_t, thread_baton, test_thread, run_threads): turn worker count and iterations into parameters (run_procs): new (calibrate_iterations, calibrate_concurrency): new; determine system performance (test_basics): ensure proper initialization before first test (test_multithreaded, test_multiprocess): reimplement (test_funcs): adapt Added: subversion/branches/revprop-cache/subversion/tests/libsvn_subr/named_atomic-test-common.h - copied, changed from r1311801, subversion/branches/revprop-cache/subversion/tests/libsvn_subr/named_atomic-test.c subversion/branches/revprop-cache/subversion/tests/libsvn_subr/named_atomic-test-proc.c Modified: subversion/branches/revprop-cache/build.conf subversion/branches/revprop-cache/subversion/tests/libsvn_subr/named_atomic-test.c Modified: subversion/branches/revprop-cache/build.conf URL: http://svn.apache.org/viewvc/subversion/branches/revprop-cache/build.conf?rev=1325206&r1=1325205&r2=1325206&view=diff ============================================================================== --- subversion/branches/revprop-cache/build.conf (original) +++ subversion/branches/revprop-cache/build.conf Thu Apr 12 11:23:00 2012 @@ -792,6 +792,14 @@ sources = named_atomic-test.c install = test libs = libsvn_test libsvn_subr apr +[named_atomic-test-proc] +description = Sub-process for named atomics +type = exe +path = subversion/tests/libsvn_subr +sources = named_atomic-test-proc.c +install = sub-test +libs = libsvn_subr apr + [path-test] description = Test path library type = exe @@ -1140,9 +1148,9 @@ libs = __ALL__ fs-test fs-base-test fs-fsfs-test fs-pack-test skel-test strings-reps-test changes-test locks-test repos-test checksum-test compat-test config-test hashdump-test named-atomics-test - mergeinfo-test opt-test path-test stream-test string-test time-test - utf-test target-test error-test cache-test spillbuf-test - revision-test + named-atomics-test-proc mergeinfo-test opt-test path-test stream-test + string-test time-test utf-test target-test error-test cache-test + spillbuf-test revision-test subst_translate-test translate-test random-test window-test Copied: subversion/branches/revprop-cache/subversion/tests/libsvn_subr/named_atomic-test-common.h (from r1311801, subversion/branches/revprop-cache/subversion/tests/libsvn_subr/named_atomic-test.c) URL: http://svn.apache.org/viewvc/subversion/branches/revprop-cache/subversion/tests/libsvn_subr/named_atomic-test-common.h?p2=subversion/branches/revprop-cache/subversion/tests/libsvn_subr/named_atomic-test-common.h&p1=subversion/branches/revprop-cache/subversion/tests/libsvn_subr/named_atomic-test.c&r1=1311801&r2=1325206&rev=1325206&view=diff ============================================================================== --- subversion/branches/revprop-cache/subversion/tests/libsvn_subr/named_atomic-test.c (original) +++ subversion/branches/revprop-cache/subversion/tests/libsvn_subr/named_atomic-test-common.h Thu Apr 12 11:23:00 2012 @@ -1,5 +1,6 @@ /* - * named_atomic-test.c: a collection of svn_named_atomic__t tests + * named_atomic-test-common.h: shared function implementations for + * named_atomic-test * * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one @@ -21,429 +22,14 @@ * ==================================================================== */ -/* ==================================================================== - To add tests, look toward the bottom of this file. -*/ - -#include <stdio.h> -#include <string.h> - -#include <apr_pools.h> -#include <apr_file_io.h> - -#include "../svn_test.h" - -#include "svn_io.h" -#include "svn_error.h" -#include "svn_pools.h" -#include "private/svn_named_atomic.h" - -/* Some constants that we will use in our tests */ - -/* to separate this code from any production environment */ -#define TEST_NAMESPACE "SvnTests" - -/* All our atomics start with that name */ -#define ATOMIC_NAME "MyTestAtomic" - -/* Factor used to create non-trivial 64 bit numbers */ -#define HUGE_VALUE 1234567890123456ll - -/* Number of concurrent threads / processes in sync. tests. - * The test code is not generic enough, yet, to support larger values. */ -#define THREAD_COUNT 4 - -/* A quick way to create error messages. */ -static svn_error_t * -fail(apr_pool_t *pool, const char *fmt, ...) -{ - va_list ap; - char *msg; - - va_start(ap, fmt); - msg = apr_pvsprintf(pool, fmt, ap); - va_end(ap); - - return svn_error_create(SVN_ERR_TEST_FAILED, 0, msg); -} - -/* The individual tests */ - -static svn_error_t * -test_basics(apr_pool_t *pool) -{ - svn_atomic_namespace__t *ns; - svn_named_atomic__t *atomic; - apr_int64_t value; - - /* Use a separate namespace for our tests isolate them from production */ - SVN_ERR(svn_atomic_namespace__create(&ns, TEST_NAMESPACE, pool)); - - /* Test a non-exisiting atomic */ - SVN_ERR(svn_named_atomic__get(&atomic, ns, ATOMIC_NAME "x", FALSE)); - SVN_TEST_ASSERT(atomic == NULL); - - /* Now, we auto-create it */ - SVN_ERR(svn_named_atomic__get(&atomic, ns, ATOMIC_NAME, TRUE)); - SVN_TEST_ASSERT(atomic != NULL); - - /* The default value should be 0 */ - SVN_TEST_ASSERT_ERROR(svn_named_atomic__read(&value, NULL), - SVN_ERR_BAD_ATOMIC); - value = 1; - SVN_ERR(svn_named_atomic__read(&value, atomic)); - SVN_TEST_ASSERT(value == 0); - - /* Write should return the previous value. */ - SVN_TEST_ASSERT_ERROR(svn_named_atomic__write(&value, 0, NULL), - SVN_ERR_BAD_ATOMIC); - value = 1; - SVN_ERR(svn_named_atomic__write(&value, 21, atomic)); - SVN_TEST_ASSERT(value == 0); - SVN_ERR(svn_named_atomic__read(&value, atomic)); - SVN_TEST_ASSERT(value == 21); - - SVN_ERR(svn_named_atomic__write(&value, 42, atomic)); - SVN_TEST_ASSERT(value == 21); - SVN_ERR(svn_named_atomic__read(&value, atomic)); - SVN_TEST_ASSERT(value == 42); - - SVN_ERR(svn_named_atomic__write(NULL, 17, atomic)); - SVN_ERR(svn_named_atomic__read(&value, atomic)); - SVN_TEST_ASSERT(value == 17); - - /* Adding & subtracting values */ - SVN_TEST_ASSERT_ERROR(svn_named_atomic__add(&value, 0, NULL), - SVN_ERR_BAD_ATOMIC); - SVN_ERR(svn_named_atomic__add(&value, 25, atomic)); - SVN_TEST_ASSERT(value == 42); - SVN_ERR(svn_named_atomic__add(NULL, 47, atomic)); - SVN_ERR(svn_named_atomic__read(&value, atomic)); - SVN_TEST_ASSERT(value == 89); - - SVN_ERR(svn_named_atomic__add(&value, -25, atomic)); - SVN_TEST_ASSERT(value == 64); - SVN_ERR(svn_named_atomic__add(NULL, -22, atomic)); - SVN_ERR(svn_named_atomic__read(&value, atomic)); - SVN_TEST_ASSERT(value == 42); - - /* Compare-and-exchange */ - SVN_TEST_ASSERT_ERROR(svn_named_atomic__cmpxchg(&value, 0, 0, NULL), - SVN_ERR_BAD_ATOMIC); - value = 1; - SVN_ERR(svn_named_atomic__cmpxchg(&value, 99, 41, atomic)); - SVN_TEST_ASSERT(value == 42); - - value = 1; - SVN_ERR(svn_named_atomic__cmpxchg(&value, 98, 42, atomic)); - SVN_TEST_ASSERT(value == 42); - SVN_ERR(svn_named_atomic__cmpxchg(&value, 67, 98, atomic)); - SVN_TEST_ASSERT(value == 98); - - SVN_ERR(svn_named_atomic__cmpxchg(NULL, 42, 67, atomic)); - SVN_ERR(svn_named_atomic__read(&value, atomic)); - SVN_TEST_ASSERT(value == 42); - - return SVN_NO_ERROR; -} - -static svn_error_t * -test_bignums(apr_pool_t *pool) -{ - svn_atomic_namespace__t *ns; - svn_named_atomic__t *atomic; - apr_int64_t value; - - /* Use a separate namespace for our tests isolate them from production */ - SVN_ERR(svn_atomic_namespace__create(&ns, TEST_NAMESPACE, pool)); - - /* Auto-create our atomic variable */ - SVN_ERR(svn_named_atomic__get(&atomic, ns, ATOMIC_NAME, TRUE)); - SVN_TEST_ASSERT(atomic != NULL); - - /* Write should return the previous value. */ - - SVN_ERR(svn_named_atomic__write(NULL, 0, atomic)); - value = 1; - SVN_ERR(svn_named_atomic__write(&value, 21 * HUGE_VALUE, atomic)); - SVN_TEST_ASSERT(value == 0 * HUGE_VALUE); - SVN_ERR(svn_named_atomic__read(&value, atomic)); - SVN_TEST_ASSERT(value == 21 * HUGE_VALUE); - - SVN_ERR(svn_named_atomic__write(&value, 17 * HUGE_VALUE, atomic)); - SVN_TEST_ASSERT(value == 21 * HUGE_VALUE); - - /* Adding & subtracting values */ - SVN_ERR(svn_named_atomic__add(&value, 25 * HUGE_VALUE, atomic)); - SVN_TEST_ASSERT(value == 42 * HUGE_VALUE); - SVN_ERR(svn_named_atomic__add(&value, -25 * HUGE_VALUE, atomic)); - SVN_TEST_ASSERT(value == 17 * HUGE_VALUE); - - /* Compare-and-exchange */ - value = 1; - SVN_ERR(svn_named_atomic__cmpxchg(&value, 99 * HUGE_VALUE, 41 * HUGE_VALUE, atomic)); - SVN_TEST_ASSERT(value == 17 * HUGE_VALUE); - - value = 1; - SVN_ERR(svn_named_atomic__cmpxchg(&value, 98 * HUGE_VALUE, 17 * HUGE_VALUE, atomic)); - SVN_TEST_ASSERT(value == 17 * HUGE_VALUE); - SVN_ERR(svn_named_atomic__read(&value, atomic)); - SVN_TEST_ASSERT(value == 98 * HUGE_VALUE); - - return SVN_NO_ERROR; -} - -static svn_error_t * -test_multiple_atomics(apr_pool_t *pool) -{ - svn_atomic_namespace__t *ns; - svn_named_atomic__t *atomic1; - svn_named_atomic__t *atomic2; - svn_named_atomic__t *atomic1_alias; - svn_named_atomic__t *atomic2_alias; - apr_int64_t value1; - apr_int64_t value2; - - /* Use a separate namespace for our tests isolate them from production */ - SVN_ERR(svn_atomic_namespace__create(&ns, TEST_NAMESPACE, pool)); - - /* Create two atomics */ - SVN_ERR(svn_named_atomic__get(&atomic1, ns, ATOMIC_NAME "1", TRUE)); - SVN_ERR(svn_named_atomic__get(&atomic2, ns, ATOMIC_NAME "2", TRUE)); - SVN_TEST_ASSERT(atomic1 != NULL); - SVN_TEST_ASSERT(atomic2 != NULL); - SVN_TEST_ASSERT(atomic1 != atomic2); - - /* Get aliases to those */ - SVN_ERR(svn_named_atomic__get(&atomic1_alias, ns, ATOMIC_NAME "1", TRUE)); - SVN_ERR(svn_named_atomic__get(&atomic2_alias, ns, ATOMIC_NAME "2", TRUE)); - SVN_TEST_ASSERT(atomic1 == atomic1_alias); - SVN_TEST_ASSERT(atomic2 == atomic2_alias); - - /* The atomics shall not overlap, i.e. changes to one do not affect the other */ - SVN_ERR(svn_named_atomic__write(NULL, 0, atomic1)); - SVN_ERR(svn_named_atomic__write(NULL, 0, atomic2)); - SVN_ERR(svn_named_atomic__write(&value1, 21 * HUGE_VALUE, atomic1)); - SVN_ERR(svn_named_atomic__write(&value2, 42 * HUGE_VALUE, atomic2)); - SVN_TEST_ASSERT(value1 == 0); - SVN_TEST_ASSERT(value2 == 0); - - SVN_ERR(svn_named_atomic__read(&value1, atomic1)); - SVN_ERR(svn_named_atomic__read(&value2, atomic2)); - SVN_TEST_ASSERT(value1 == 21 * HUGE_VALUE); - SVN_TEST_ASSERT(value2 == 42 * HUGE_VALUE); - - SVN_ERR(svn_named_atomic__add(&value1, 25 * HUGE_VALUE, atomic1)); - SVN_ERR(svn_named_atomic__add(&value2, -25 * HUGE_VALUE, atomic2)); - SVN_TEST_ASSERT(value1 == 46 * HUGE_VALUE); - SVN_TEST_ASSERT(value2 == 17 * HUGE_VALUE); - - value1 = 1; - value2 = 1; - SVN_ERR(svn_named_atomic__cmpxchg(&value1, 4 * HUGE_VALUE, 46 * HUGE_VALUE, atomic1)); - SVN_ERR(svn_named_atomic__cmpxchg(&value2, 98 * HUGE_VALUE, 17 * HUGE_VALUE, atomic2)); - SVN_TEST_ASSERT(value1 == 46 * HUGE_VALUE); - SVN_TEST_ASSERT(value2 == 17 * HUGE_VALUE); - - SVN_ERR(svn_named_atomic__read(&value1, atomic1)); - SVN_ERR(svn_named_atomic__read(&value2, atomic2)); - SVN_TEST_ASSERT(value1 == 4 * HUGE_VALUE); - SVN_TEST_ASSERT(value2 == 98 * HUGE_VALUE); - - return SVN_NO_ERROR; -} - -static svn_error_t * -test_namespaces(apr_pool_t *pool) -{ - svn_atomic_namespace__t *test_namespace1; - svn_atomic_namespace__t *test_namespace1_alias; - svn_atomic_namespace__t *test_namespace2; - svn_atomic_namespace__t *test_namespace2_alias; - svn_atomic_namespace__t *default_namespace = NULL; - svn_atomic_namespace__t *default_namespace_alias; - svn_named_atomic__t *atomic1; - svn_named_atomic__t *atomic2; - svn_named_atomic__t *atomic1_alias; - svn_named_atomic__t *atomic2_alias; - svn_named_atomic__t *atomic_default; - apr_int64_t value; - - /* Use a separate namespace for our tests isolate them from production */ - SVN_ERR(svn_atomic_namespace__create(&test_namespace1, TEST_NAMESPACE "1", pool)); - SVN_ERR(svn_atomic_namespace__create(&test_namespace1_alias, TEST_NAMESPACE "1", pool)); - SVN_ERR(svn_atomic_namespace__create(&test_namespace2, TEST_NAMESPACE "2", pool)); - SVN_ERR(svn_atomic_namespace__create(&test_namespace2_alias, TEST_NAMESPACE "2", pool)); - SVN_ERR(svn_atomic_namespace__create(&default_namespace_alias, NULL, pool)); - - /* Create two atomics with the same name in different namespaces */ - SVN_ERR(svn_named_atomic__get(&atomic1, test_namespace1, ATOMIC_NAME, TRUE)); - SVN_ERR(svn_named_atomic__get(&atomic1_alias, test_namespace1_alias, ATOMIC_NAME, FALSE)); - SVN_ERR(svn_named_atomic__get(&atomic2, test_namespace2, ATOMIC_NAME, TRUE)); - SVN_ERR(svn_named_atomic__get(&atomic2_alias, test_namespace2_alias, ATOMIC_NAME, FALSE)); - SVN_TEST_ASSERT(atomic1 != atomic1_alias); - SVN_TEST_ASSERT(atomic1_alias != NULL); - SVN_TEST_ASSERT(atomic2 != atomic2_alias); - SVN_TEST_ASSERT(atomic2_alias != NULL); - - /* Access default namespace (without changing it)*/ - SVN_ERR(svn_named_atomic__get(&atomic_default, default_namespace, ATOMIC_NAME, FALSE)); - SVN_TEST_ASSERT(atomic_default == NULL); - SVN_ERR(svn_named_atomic__get(&atomic_default, default_namespace_alias, ATOMIC_NAME, FALSE)); - SVN_TEST_ASSERT(atomic_default == NULL); - - /* Write data to our atomics */ - SVN_ERR(svn_named_atomic__write(NULL, 21 * HUGE_VALUE, atomic1)); - SVN_ERR(svn_named_atomic__write(NULL, 42 * HUGE_VALUE, atomic2)); - - /* Now check who sees which value */ - SVN_ERR(svn_named_atomic__read(&value, atomic1)); - SVN_TEST_ASSERT(value == 21 * HUGE_VALUE); - SVN_ERR(svn_named_atomic__read(&value, atomic2)); - SVN_TEST_ASSERT(value == 42 * HUGE_VALUE); - - SVN_ERR(svn_named_atomic__read(&value, atomic1_alias)); - SVN_TEST_ASSERT(value == 21 * HUGE_VALUE); - SVN_ERR(svn_named_atomic__read(&value, atomic2_alias)); - SVN_TEST_ASSERT(value == 42 * HUGE_VALUE); - - return SVN_NO_ERROR; -} -/* Bring shared memory to a defined state. This is very useful in case of - * lingering problems from previous tests or test runs. +/* "pipeline" test: worker with ID 0 initializes the data; all workers + * (COUNT in total) have one input and one output bucket that form a ring + * spanning all workers. Each worker passes the value along ITERATIONS times. */ static svn_error_t * -init_test_shm(apr_pool_t *pool) -{ - svn_atomic_namespace__t *ns; - svn_named_atomic__t *atomic; - apr_pool_t *scratch = svn_pool_create(pool); - - /* get the two I/O atomics for this thread */ - SVN_ERR(svn_atomic_namespace__create(&ns, TEST_NAMESPACE, scratch)); - - SVN_ERR(svn_named_atomic__get(&atomic, ns, ATOMIC_NAME, TRUE)); - SVN_ERR(svn_named_atomic__write(NULL, 0, atomic)); - SVN_ERR(svn_named_atomic__get(&atomic, ns, ATOMIC_NAME "0", TRUE)); - SVN_ERR(svn_named_atomic__write(NULL, 0, atomic)); - SVN_ERR(svn_named_atomic__get(&atomic, ns, ATOMIC_NAME "1", TRUE)); - SVN_ERR(svn_named_atomic__write(NULL, 0, atomic)); - SVN_ERR(svn_named_atomic__get(&atomic, ns, ATOMIC_NAME "2", TRUE)); - SVN_ERR(svn_named_atomic__write(NULL, 0, atomic)); - SVN_ERR(svn_named_atomic__get(&atomic, ns, ATOMIC_NAME "3", TRUE)); - SVN_ERR(svn_named_atomic__write(NULL, 0, atomic)); - SVN_ERR(svn_named_atomic__get(&atomic, ns, "counter", TRUE)); - SVN_ERR(svn_named_atomic__write(NULL, 0, atomic)); - - apr_pool_clear(scratch); - - SVN_ERR(svn_atomic_namespace__create(&ns, TEST_NAMESPACE "1", scratch)); - SVN_ERR(svn_named_atomic__get(&atomic, ns, ATOMIC_NAME, TRUE)); - SVN_ERR(svn_named_atomic__write(NULL, 0, atomic)); - apr_pool_clear(scratch); - - SVN_ERR(svn_atomic_namespace__create(&ns, TEST_NAMESPACE "2", scratch)); - SVN_ERR(svn_named_atomic__get(&atomic, ns, ATOMIC_NAME, TRUE)); - SVN_ERR(svn_named_atomic__write(NULL, 0, atomic)); - apr_pool_clear(scratch); - - /* done */ - - return SVN_NO_ERROR; -} - -#ifdef APR_HAS_THREADS - -/* Pass tokens around in a ring buffer with each station being handled - * by a separate thread. Try to provoke token loss caused by faulty sync. - */ - -/* our thread function type - */ -typedef svn_error_t *(*thread_func_t)(int, int, int, apr_pool_t *); - -/* Per-thread input and output data. - */ -struct thread_baton -{ - int thread_count; - int thread_no; - int iterations; - svn_error_t *result; - thread_func_t func; -}; - -/* APR thread function implementation: A wrapper around baton->func that - * handles the svn_error_t return value. - */ -static void * -APR_THREAD_FUNC test_thread(apr_thread_t *thread, void *baton) -{ - struct thread_baton *params = baton; - apr_pool_t *pool = svn_pool_create_ex(NULL, NULL); - - params->result = (*params->func)(params->thread_no, - params->thread_count, - params->iterations, - pool); - apr_pool_destroy(pool); - - return NULL; -} - -/* Runs FUNC in THREAD_COUNT concurrent threads and combine the results. - */ -static svn_error_t * -run_threads(apr_pool_t *pool, int iterations, thread_func_t func) -{ - apr_status_t status; - int i; - svn_error_t *error = SVN_NO_ERROR; - - /* all threads and their I/O data */ - apr_thread_t *threads[THREAD_COUNT]; - struct thread_baton batons[THREAD_COUNT]; - - /* start threads */ - for (i = 0; i < THREAD_COUNT; ++i) - { - batons[i].thread_count = THREAD_COUNT; - batons[i].thread_no = i; - batons[i].iterations = iterations; - batons[i].func = func; - - status = apr_thread_create(&threads[i], - NULL, - test_thread, - &batons[i], - pool); - if (status != APR_SUCCESS) - SVN_ERR(svn_error_wrap_apr(status, "could not create a thread")); - } - - /* Wait for threads to finish and return result. */ - for (i = 0; i < THREAD_COUNT; ++i) - { - apr_status_t retval; - status = apr_thread_join(&retval, threads[i]); - if (status != APR_SUCCESS) - SVN_ERR(svn_error_wrap_apr(status, "waiting for thread's end failed")); - - if (batons[i].result) - error = svn_error_compose_create (error, svn_error_quick_wrap - (batons[i].result, apr_psprintf(pool, "Thread %d failed", i))); - } - - return error; -} - -/* test thread code: thread 0 initializes the data; all threads have one - * one input and one output bucket that form a ring spanning all threads. - */ -static svn_error_t * -test_pipeline_thread(int thread_no, int thread_count, int iterations, apr_pool_t *pool) +test_pipeline(int id, int count, int iterations, apr_pool_t *pool) { svn_atomic_namespace__t *ns; svn_named_atomic__t *atomicIn; @@ -459,7 +45,7 @@ test_pipeline_thread(int thread_no, int apr_pstrcat(pool, ATOMIC_NAME, apr_itoa(pool, - thread_no), + id), NULL), TRUE)); SVN_ERR(svn_named_atomic__get(&atomicOut, @@ -467,17 +53,18 @@ test_pipeline_thread(int thread_no, int apr_pstrcat(pool, ATOMIC_NAME, apr_itoa(pool, - (thread_no + 1) % thread_count), + (id + 1) % count), NULL), TRUE)); + /* our iteration counter */ SVN_ERR(svn_named_atomic__get(&atomicCounter, ns, "counter", TRUE)); - if (thread_no == 0) + if (id == 0) { /* Initialize values in thread 0, pass them along in other threads */ - for (i = 1; i <= thread_count; ++i) + for (i = 1; i <= count; ++i) do /* Generate new token (once the old one has been removed)*/ SVN_ERR(svn_named_atomic__cmpxchg(&old_value, @@ -501,7 +88,7 @@ test_pipeline_thread(int thread_no, int /* All tokes must come in in the same order */ if (counter < iterations) - SVN_TEST_ASSERT((last_value % thread_count) == (value - 1)); + SVN_TEST_ASSERT((last_value % count) == (value - 1)); last_value = value; /* Wait for the target atomic to become vacant and write the token */ @@ -524,60 +111,3 @@ test_pipeline_thread(int thread_no, int return SVN_NO_ERROR; } - -/* test interface */ -static svn_error_t * -test_multithreaded(apr_pool_t *pool) -{ - apr_time_t start; - int iterations; - - SVN_ERR(init_test_shm(pool)); - - /* calibrate */ - start = apr_time_now(); - SVN_ERR(run_threads(pool, 100, test_pipeline_thread)); - iterations = 2000000 / (int)((apr_time_now() - start) / 100 + 1); - - /* run test for 2 seconds */ - SVN_ERR(init_test_shm(pool)); - SVN_ERR(run_threads(pool, iterations, test_pipeline_thread)); - - return SVN_NO_ERROR; -} -#endif - -static svn_error_t * -test_multiprocess(apr_pool_t *pool) -{ - return fail(pool, "Not implemented"); -} - -/* - ==================================================================== - If you add a new test to this file, update this array. - - (These globals are required by our included main()) -*/ - -/* An array of all test functions */ -struct svn_test_descriptor_t test_funcs[] = - { - SVN_TEST_PASS2(init_test_shm, - "initialization"), - SVN_TEST_PASS2(test_basics, - "basic r/w access to a single atomic"), - SVN_TEST_PASS2(test_bignums, - "atomics must be 64 bits"), - SVN_TEST_PASS2(test_multiple_atomics, - "basic r/w access to multiple atomics"), - SVN_TEST_PASS2(test_namespaces, - "use different namespaces"), -#ifdef APR_HAS_THREADS - SVN_TEST_PASS2(test_multithreaded, - "multithreaded access to atomics"), -#endif - SVN_TEST_XFAIL2(test_multiprocess, - "multi-process access to atomics"), - SVN_TEST_NULL - }; Added: subversion/branches/revprop-cache/subversion/tests/libsvn_subr/named_atomic-test-proc.c URL: http://svn.apache.org/viewvc/subversion/branches/revprop-cache/subversion/tests/libsvn_subr/named_atomic-test-proc.c?rev=1325206&view=auto ============================================================================== --- subversion/branches/revprop-cache/subversion/tests/libsvn_subr/named_atomic-test-proc.c (added) +++ subversion/branches/revprop-cache/subversion/tests/libsvn_subr/named_atomic-test-proc.c Thu Apr 12 11:23:00 2012 @@ -0,0 +1,88 @@ +/* + * named_atomic-test-proc.c: a collection of svn_named_atomic__t tests + * + * ==================================================================== + * 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. + * ==================================================================== + */ + +/* ==================================================================== + To add tests, look toward the bottom of this file. +*/ + + +#include <stdio.h> + +#include <apr_pools.h> + +#include "svn_io.h" +#include "svn_error.h" +#include "svn_pools.h" + +/* shared test implementation */ +#include "named_atomic-test-common.h" + +/* Very simple process frame around the actual test code */ +int +main(int argc, const char *argv[]) +{ + svn_boolean_t got_error = FALSE; + apr_pool_t *pool; + svn_error_t *err; + + int id = 0; + int count = 0; + int iterations = 0; + + /* Initialize APR (Apache pools) */ + if (apr_initialize() != APR_SUCCESS) + { + printf("apr_initialize() failed.\n"); + exit(1); + } + + pool = svn_pool_create(NULL); + + /* lean & mean parameter parsing */ + if (argc != 4) + { + printf("Usage: named_atomic-test-proc ID COUNT ITERATIONS.\n"); + exit(1); + } + + id = (int)apr_atoi64(argv[1]); + count = (int)apr_atoi64(argv[2]); + iterations = (int)apr_atoi64(argv[3]); + + /* run test routine */ + + err = test(id, count, iterations, pool); + if (err) + { + got_error = TRUE; + svn_handle_error2(err, stderr, FALSE, "svn:"); + svn_error_clear(err); + } + + /* Clean up APR */ + svn_pool_destroy(pool); + apr_terminate(); + + return got_error; +} + \ No newline at end of file Modified: subversion/branches/revprop-cache/subversion/tests/libsvn_subr/named_atomic-test.c URL: http://svn.apache.org/viewvc/subversion/branches/revprop-cache/subversion/tests/libsvn_subr/named_atomic-test.c?rev=1325206&r1=1325205&r2=1325206&view=diff ============================================================================== --- subversion/branches/revprop-cache/subversion/tests/libsvn_subr/named_atomic-test.c (original) +++ subversion/branches/revprop-cache/subversion/tests/libsvn_subr/named_atomic-test.c Thu Apr 12 11:23:00 2012 @@ -50,22 +50,287 @@ /* Factor used to create non-trivial 64 bit numbers */ #define HUGE_VALUE 1234567890123456ll -/* Number of concurrent threads / processes in sync. tests. - * The test code is not generic enough, yet, to support larger values. */ -#define THREAD_COUNT 4 +/* Name of the worker process executable */ +#define TEST_PROC "named_atomic-test-proc" -/* A quick way to create error messages. */ +/* shared test implementation */ +#include "named_atomic-test-common.h" + +/* number of hardware threads (logical cores) that we may use. + * Will be set to at least 2 - even on unicore machines. */ +static int hw_thread_count = 0; + +/* number of iterations that we should perform on concurrency tests + * (will be calibrated to about 2s runtime)*/ +static int suggested_iterations = 0; + +/* Bring shared memory to a defined state. This is very useful in case of + * lingering problems from previous tests or test runs. + */ +static svn_error_t * +init_test_shm(apr_pool_t *pool) +{ + svn_atomic_namespace__t *ns; + svn_named_atomic__t *atomic; + apr_pool_t *scratch = svn_pool_create(pool); + + /* get the two I/O atomics for this thread */ + SVN_ERR(svn_atomic_namespace__create(&ns, TEST_NAMESPACE, scratch)); + SVN_ERR(svn_named_atomic__get(&atomic, ns, ATOMIC_NAME, TRUE)); + SVN_ERR(svn_named_atomic__write(NULL, 0, atomic)); + SVN_ERR(svn_named_atomic__get(&atomic, ns, ATOMIC_NAME "1", TRUE)); + SVN_ERR(svn_named_atomic__write(NULL, 0, atomic)); + SVN_ERR(svn_named_atomic__get(&atomic, ns, ATOMIC_NAME "2", TRUE)); + SVN_ERR(svn_named_atomic__write(NULL, 0, atomic)); + + apr_pool_clear(scratch); + + SVN_ERR(svn_atomic_namespace__create(&ns, TEST_NAMESPACE "1", scratch)); + SVN_ERR(svn_named_atomic__get(&atomic, ns, ATOMIC_NAME, TRUE)); + SVN_ERR(svn_named_atomic__write(NULL, 0, atomic)); + apr_pool_clear(scratch); + + SVN_ERR(svn_atomic_namespace__create(&ns, TEST_NAMESPACE "2", scratch)); + SVN_ERR(svn_named_atomic__get(&atomic, ns, ATOMIC_NAME, TRUE)); + SVN_ERR(svn_named_atomic__write(NULL, 0, atomic)); + apr_pool_clear(scratch); + + /* done */ + + return SVN_NO_ERROR; +} + +/* Prepare the shared memory for a run with COUNT workers. + */ +static svn_error_t * +init_concurrency_test_shm(apr_pool_t *pool, int count) +{ + svn_atomic_namespace__t *ns; + svn_named_atomic__t *atomic; + apr_pool_t *scratch = svn_pool_create(pool); + int i; + + /* get the two I/O atomics for this thread */ + SVN_ERR(svn_atomic_namespace__create(&ns, TEST_NAMESPACE, scratch)); + + /* reset the I/O atomics for all threads */ + for (i = 0; i < count; ++i) + { + SVN_ERR(svn_named_atomic__get(&atomic, + ns, + apr_pstrcat(scratch, + ATOMIC_NAME, + apr_itoa(scratch, i), + NULL), + TRUE)); + SVN_ERR(svn_named_atomic__write(NULL, 0, atomic)); + } + + SVN_ERR(svn_named_atomic__get(&atomic, ns, "counter", TRUE)); + SVN_ERR(svn_named_atomic__write(NULL, 0, atomic)); + + apr_pool_clear(scratch); + + return SVN_NO_ERROR; +} + +#ifdef APR_HAS_THREADS + +/* our thread function type + */ +typedef svn_error_t *(*thread_func_t)(int, int, int, apr_pool_t *); + +/* Per-thread input and output data. + */ +struct thread_baton +{ + int thread_count; + int thread_no; + int iterations; + svn_error_t *result; + thread_func_t func; +}; + +/* APR thread function implementation: A wrapper around baton->func that + * handles the svn_error_t return value. + */ +static void * +APR_THREAD_FUNC test_thread(apr_thread_t *thread, void *baton) +{ + struct thread_baton *params = baton; + apr_pool_t *pool = svn_pool_create_ex(NULL, NULL); + + params->result = (*params->func)(params->thread_no, + params->thread_count, + params->iterations, + pool); + apr_pool_destroy(pool); + + return NULL; +} + +/* Runs FUNC in COUNT concurrent threads ITERATION times and combines the + * results. + */ static svn_error_t * -fail(apr_pool_t *pool, const char *fmt, ...) +run_threads(apr_pool_t *pool, int count, int iterations, thread_func_t func) { - va_list ap; - char *msg; + apr_status_t status; + int i; + svn_error_t *error = SVN_NO_ERROR; - va_start(ap, fmt); - msg = apr_pvsprintf(pool, fmt, ap); - va_end(ap); + /* all threads and their I/O data */ + apr_thread_t **threads = apr_palloc(pool, count * sizeof(*threads)); + struct thread_baton *batons = apr_palloc(pool, count * sizeof(*batons)); + + /* start threads */ + for (i = 0; i < count; ++i) + { + batons[i].thread_count = count; + batons[i].thread_no = i; + batons[i].iterations = iterations; + batons[i].func = func; + + status = apr_thread_create(&threads[i], + NULL, + test_thread, + &batons[i], + pool); + if (status != APR_SUCCESS) + SVN_ERR(svn_error_wrap_apr(status, "could not create a thread")); + } - return svn_error_create(SVN_ERR_TEST_FAILED, 0, msg); + /* Wait for threads to finish and return result. */ + for (i = 0; i < count; ++i) + { + apr_status_t retval; + status = apr_thread_join(&retval, threads[i]); + if (status != APR_SUCCESS) + SVN_ERR(svn_error_wrap_apr(status, "waiting for thread's end failed")); + + if (batons[i].result) + error = svn_error_compose_create (error, svn_error_quick_wrap + (batons[i].result, apr_psprintf(pool, "Thread %d failed", i))); + } + + return error; +} +#endif + +/* Runs PROC in COUNT concurrent worker processes and check the results. + */ +static svn_error_t * +run_procs(apr_pool_t *pool, const char *proc, int count, int iterations) +{ + int i; + svn_error_t *error = SVN_NO_ERROR; + + /* all processes and their I/O data */ + apr_proc_t *process = apr_palloc(pool, count * sizeof(*process)); + + /* start threads */ + for (i = 0; i < count; ++i) + { + const char * args[5] = + { + proc, + apr_itoa(pool, i), + apr_itoa(pool, count), + apr_itoa(pool, iterations), + NULL + }; + + SVN_ERR(svn_io_start_cmd3(&process[i], + NULL, /* path */ + args[0], + args, + NULL, /* environment */ + FALSE, /* no handle inheritance */ + FALSE, /* no STDIN pipe */ + NULL, + FALSE, /* no STDOUT pipe */ + NULL, + FALSE, /* no STDERR pipe */ + NULL, + pool)); + } + + /* Wait for threads to finish and return result. */ + for (i = 0; i < count; ++i) + { + const char *cmd = apr_psprintf(pool, + "named_atomic-test-proc %d %d %d", + i, count, iterations); + SVN_ERR(svn_io_wait_for_cmd(&process[i], cmd, NULL, NULL, pool)); + } + + return error; +} + +/* Set SUGGESTED_ITERATIONS to a value that COUNT workers will take + * about 2 seconds to execute. + */ +static svn_error_t * +calibrate_iterations(apr_pool_t *pool, int count) +{ + apr_time_t start; + apr_time_t overhead; + int calib_iterations; + double taken = 0.0; + + /* measure start-up overhead */ + + SVN_ERR(init_concurrency_test_shm(pool, count)); + + start = apr_time_now(); + SVN_ERR(run_procs(pool, TEST_PROC, count, 10)); + overhead = apr_time_now() - start; + + /* increase iterations until we pass the 10ms mark */ + + for (calib_iterations = 100; taken < 10000.0; calib_iterations *= 2) + { + SVN_ERR(init_concurrency_test_shm(pool, count)); + + start = apr_time_now(); + SVN_ERR(run_procs(pool, TEST_PROC, count, calib_iterations + 10)); + + taken = (int)(apr_time_now() - start - overhead); + } + + /* scale that to 2s */ + + suggested_iterations = (int)(2000000.0 / taken * calib_iterations); + + return SVN_NO_ERROR; +} + +/* Find out how far the system will scale, i.e. how many workers can be + * run concurrently without experiencing significant slowdowns. + * Sets HW_THREAD_COUNT to a value of 2 .. 32 (limit the system impact in + * case our heuristics fail) and determines the number of iterations. + * Can be called multiple times but will skip the calculations after the + * first successful run. + */ +static svn_error_t * +calibrate_concurrency(apr_pool_t *pool) +{ + if (hw_thread_count == 0) + { + SVN_ERR(calibrate_iterations(pool, 2)); + for (hw_thread_count = 2; hw_thread_count < 32; hw_thread_count *= 2) + { + int saved_suggestion = suggested_iterations; + SVN_ERR(calibrate_iterations(pool, hw_thread_count * 2)); + if (suggested_iterations < 10000) + { + suggested_iterations = saved_suggestion; + break; + } + } + } + + return SVN_NO_ERROR; } /* The individual tests */ @@ -77,6 +342,8 @@ test_basics(apr_pool_t *pool) svn_named_atomic__t *atomic; apr_int64_t value; + init_test_shm(pool); + /* Use a separate namespace for our tests isolate them from production */ SVN_ERR(svn_atomic_namespace__create(&ns, TEST_NAMESPACE, pool)); @@ -311,237 +578,15 @@ test_namespaces(apr_pool_t *pool) return SVN_NO_ERROR; } -/* Bring shared memory to a defined state. This is very useful in case of - * lingering problems from previous tests or test runs. - */ -static svn_error_t * -init_test_shm(apr_pool_t *pool) -{ - svn_atomic_namespace__t *ns; - svn_named_atomic__t *atomic; - apr_pool_t *scratch = svn_pool_create(pool); - - /* get the two I/O atomics for this thread */ - SVN_ERR(svn_atomic_namespace__create(&ns, TEST_NAMESPACE, scratch)); - - SVN_ERR(svn_named_atomic__get(&atomic, ns, ATOMIC_NAME, TRUE)); - SVN_ERR(svn_named_atomic__write(NULL, 0, atomic)); - SVN_ERR(svn_named_atomic__get(&atomic, ns, ATOMIC_NAME "0", TRUE)); - SVN_ERR(svn_named_atomic__write(NULL, 0, atomic)); - SVN_ERR(svn_named_atomic__get(&atomic, ns, ATOMIC_NAME "1", TRUE)); - SVN_ERR(svn_named_atomic__write(NULL, 0, atomic)); - SVN_ERR(svn_named_atomic__get(&atomic, ns, ATOMIC_NAME "2", TRUE)); - SVN_ERR(svn_named_atomic__write(NULL, 0, atomic)); - SVN_ERR(svn_named_atomic__get(&atomic, ns, ATOMIC_NAME "3", TRUE)); - SVN_ERR(svn_named_atomic__write(NULL, 0, atomic)); - SVN_ERR(svn_named_atomic__get(&atomic, ns, "counter", TRUE)); - SVN_ERR(svn_named_atomic__write(NULL, 0, atomic)); - - apr_pool_clear(scratch); - - SVN_ERR(svn_atomic_namespace__create(&ns, TEST_NAMESPACE "1", scratch)); - SVN_ERR(svn_named_atomic__get(&atomic, ns, ATOMIC_NAME, TRUE)); - SVN_ERR(svn_named_atomic__write(NULL, 0, atomic)); - apr_pool_clear(scratch); - - SVN_ERR(svn_atomic_namespace__create(&ns, TEST_NAMESPACE "2", scratch)); - SVN_ERR(svn_named_atomic__get(&atomic, ns, ATOMIC_NAME, TRUE)); - SVN_ERR(svn_named_atomic__write(NULL, 0, atomic)); - apr_pool_clear(scratch); - - /* done */ - - return SVN_NO_ERROR; -} - #ifdef APR_HAS_THREADS - -/* Pass tokens around in a ring buffer with each station being handled - * by a separate thread. Try to provoke token loss caused by faulty sync. - */ - -/* our thread function type - */ -typedef svn_error_t *(*thread_func_t)(int, int, int, apr_pool_t *); - -/* Per-thread input and output data. - */ -struct thread_baton -{ - int thread_count; - int thread_no; - int iterations; - svn_error_t *result; - thread_func_t func; -}; - -/* APR thread function implementation: A wrapper around baton->func that - * handles the svn_error_t return value. - */ -static void * -APR_THREAD_FUNC test_thread(apr_thread_t *thread, void *baton) -{ - struct thread_baton *params = baton; - apr_pool_t *pool = svn_pool_create_ex(NULL, NULL); - - params->result = (*params->func)(params->thread_no, - params->thread_count, - params->iterations, - pool); - apr_pool_destroy(pool); - - return NULL; -} - -/* Runs FUNC in THREAD_COUNT concurrent threads and combine the results. - */ -static svn_error_t * -run_threads(apr_pool_t *pool, int iterations, thread_func_t func) -{ - apr_status_t status; - int i; - svn_error_t *error = SVN_NO_ERROR; - - /* all threads and their I/O data */ - apr_thread_t *threads[THREAD_COUNT]; - struct thread_baton batons[THREAD_COUNT]; - - /* start threads */ - for (i = 0; i < THREAD_COUNT; ++i) - { - batons[i].thread_count = THREAD_COUNT; - batons[i].thread_no = i; - batons[i].iterations = iterations; - batons[i].func = func; - - status = apr_thread_create(&threads[i], - NULL, - test_thread, - &batons[i], - pool); - if (status != APR_SUCCESS) - SVN_ERR(svn_error_wrap_apr(status, "could not create a thread")); - } - - /* Wait for threads to finish and return result. */ - for (i = 0; i < THREAD_COUNT; ++i) - { - apr_status_t retval; - status = apr_thread_join(&retval, threads[i]); - if (status != APR_SUCCESS) - SVN_ERR(svn_error_wrap_apr(status, "waiting for thread's end failed")); - - if (batons[i].result) - error = svn_error_compose_create (error, svn_error_quick_wrap - (batons[i].result, apr_psprintf(pool, "Thread %d failed", i))); - } - - return error; -} - -/* test thread code: thread 0 initializes the data; all threads have one - * one input and one output bucket that form a ring spanning all threads. - */ -static svn_error_t * -test_pipeline_thread(int thread_no, int thread_count, int iterations, apr_pool_t *pool) -{ - svn_atomic_namespace__t *ns; - svn_named_atomic__t *atomicIn; - svn_named_atomic__t *atomicOut; - svn_named_atomic__t *atomicCounter; - apr_int64_t value, old_value, last_value = 0; - apr_int64_t i, counter; - - /* get the two I/O atomics for this thread */ - SVN_ERR(svn_atomic_namespace__create(&ns, TEST_NAMESPACE, pool)); - SVN_ERR(svn_named_atomic__get(&atomicIn, - ns, - apr_pstrcat(pool, - ATOMIC_NAME, - apr_itoa(pool, - thread_no), - NULL), - TRUE)); - SVN_ERR(svn_named_atomic__get(&atomicOut, - ns, - apr_pstrcat(pool, - ATOMIC_NAME, - apr_itoa(pool, - (thread_no + 1) % thread_count), - NULL), - TRUE)); - - SVN_ERR(svn_named_atomic__get(&atomicCounter, ns, "counter", TRUE)); - - if (thread_no == 0) - { - /* Initialize values in thread 0, pass them along in other threads */ - - for (i = 1; i <= thread_count; ++i) - do - /* Generate new token (once the old one has been removed)*/ - SVN_ERR(svn_named_atomic__cmpxchg(&old_value, - i, - 0, - atomicOut)); - while (old_value != 0); - } - - /* Pass the tokens along */ - - do - { - /* Wait for and consume incoming token. */ - do - { - SVN_ERR(svn_named_atomic__write(&value, 0, atomicIn)); - SVN_ERR(svn_named_atomic__read(&counter, atomicCounter)); - } - while ((value == 0) && (counter < iterations)); - - /* All tokes must come in in the same order */ - if (counter < iterations) - SVN_TEST_ASSERT((last_value % thread_count) == (value - 1)); - last_value = value; - - /* Wait for the target atomic to become vacant and write the token */ - do - { - SVN_ERR(svn_named_atomic__cmpxchg(&old_value, - value, - 0, - atomicOut)); - SVN_ERR(svn_named_atomic__read(&counter, atomicCounter)); - } - while ((old_value != 0) && (counter < iterations)); - - /* Count the number of operations */ - SVN_ERR(svn_named_atomic__add(&counter, 1, atomicCounter)); - } - while (counter < iterations); - - /* done */ - - return SVN_NO_ERROR; -} - -/* test interface */ static svn_error_t * test_multithreaded(apr_pool_t *pool) { - apr_time_t start; - int iterations; - - SVN_ERR(init_test_shm(pool)); + SVN_ERR(calibrate_concurrency(pool)); - /* calibrate */ - start = apr_time_now(); - SVN_ERR(run_threads(pool, 100, test_pipeline_thread)); - iterations = 2000000 / (int)((apr_time_now() - start) / 100 + 1); - - /* run test for 2 seconds */ - SVN_ERR(init_test_shm(pool)); - SVN_ERR(run_threads(pool, iterations, test_pipeline_thread)); + printf("%d %d\n", hw_thread_count, suggested_iterations); + SVN_ERR(init_concurrency_test_shm(pool, hw_thread_count)); + SVN_ERR(run_threads(pool, hw_thread_count, suggested_iterations, test_pipeline)); return SVN_NO_ERROR; } @@ -550,7 +595,13 @@ test_multithreaded(apr_pool_t *pool) static svn_error_t * test_multiprocess(apr_pool_t *pool) { - return fail(pool, "Not implemented"); + SVN_ERR(calibrate_concurrency(pool)); + + printf("%d %d\n", hw_thread_count, suggested_iterations); + SVN_ERR(init_concurrency_test_shm(pool, hw_thread_count)); + SVN_ERR(run_procs(pool, TEST_PROC, hw_thread_count, suggested_iterations)); + + return SVN_NO_ERROR; } /* @@ -563,8 +614,7 @@ test_multiprocess(apr_pool_t *pool) /* An array of all test functions */ struct svn_test_descriptor_t test_funcs[] = { - SVN_TEST_PASS2(init_test_shm, - "initialization"), + SVN_TEST_NULL, SVN_TEST_PASS2(test_basics, "basic r/w access to a single atomic"), SVN_TEST_PASS2(test_bignums, @@ -577,7 +627,7 @@ struct svn_test_descriptor_t test_funcs[ SVN_TEST_PASS2(test_multithreaded, "multithreaded access to atomics"), #endif - SVN_TEST_XFAIL2(test_multiprocess, + SVN_TEST_PASS2(test_multiprocess, "multi-process access to atomics"), SVN_TEST_NULL };