diff options
Diffstat (limited to 'subversion/tests/libsvn_subr/named_atomic-test-common.h')
| -rw-r--r-- | subversion/tests/libsvn_subr/named_atomic-test-common.h | 245 |
1 files changed, 245 insertions, 0 deletions
diff --git a/subversion/tests/libsvn_subr/named_atomic-test-common.h b/subversion/tests/libsvn_subr/named_atomic-test-common.h new file mode 100644 index 0000000..2ada4ee --- /dev/null +++ b/subversion/tests/libsvn_subr/named_atomic-test-common.h @@ -0,0 +1,245 @@ +/* + * named_atomic-test-common.h: shared function implementations for + * named_atomic-test + * + * ==================================================================== + * 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. + * ==================================================================== + */ + + + +#include "../svn_test.h" +#include "svn_pools.h" +#include "private/svn_named_atomic.h" + +/* Some constants that we will use in our tests */ + +/* All our atomics start with that name */ +#define ATOMIC_NAME "MyTestAtomic" + +/* Factor used to create non-trivial 64 bit numbers */ +#define HUGE_VALUE 1234567890123456ll + +/* to separate this code from any production environment */ +const char *name_namespace = NULL; +const char *name_namespace1 = NULL; +const char *name_namespace2 = NULL; + +/* data structure containing all information we need to check for + * a) passing some deadline + * b) reaching the maximum iteration number + */ +typedef struct watchdog_t +{ + apr_time_t deadline; + svn_named_atomic__t *atomic_counter; + int iterations; + int call_count; /* don't call apr_time_now() too often '*/ +} watchdog_t; + +/* init the WATCHDOG data structure for checking ATOMIC_COUNTER to reach + * ITERATIONS and for the system time to pass a deadline MAX_DURATION + * microsecs in the future. + */ +static void +init_watchdog(watchdog_t *watchdog, + svn_named_atomic__t *atomic_counter, + int iterations, + apr_time_t max_duration) +{ + watchdog->deadline = apr_time_now() + max_duration; + watchdog->atomic_counter = atomic_counter; + watchdog->iterations = iterations; + watchdog->call_count = 0; +} + +/* test for watchdog conditions */ +static svn_error_t * +check_watchdog(watchdog_t *watchdog, svn_boolean_t *done) +{ + apr_int64_t counter = 0; + + /* check for normal end of loop. + * We are a watchdog, so don't check for errors. */ + *done = FALSE; + svn_error_clear(svn_named_atomic__read(&counter, + watchdog->atomic_counter)); + if (counter >= watchdog->iterations) + { + *done = TRUE; + return SVN_NO_ERROR; + } + + /* Check the system time and indicate when deadline has passed */ + if (++watchdog->call_count > 100) + { + watchdog->call_count = 100; + if (apr_time_now() > watchdog->deadline) + return svn_error_createf(SVN_ERR_TEST_FAILED, + 0, + "Deadline has passed at iteration %d/%d", + (int)counter, watchdog->iterations); + } + + /* no problem so far */ + return SVN_NO_ERROR; +} + +/* "pipeline" test: initialization code executed by the worker with ID 0. + * Pushes COUNT tokens into ATOMIC_OUT and checks for ATOMIC_COUNTER not to + * exceed ITERATIONS (early termination). + */ +static svn_error_t * +test_pipeline_prepare(svn_named_atomic__t *atomic_out, + int count, + watchdog_t *watchdog) +{ + apr_int64_t value = 0; + int i; + svn_boolean_t done = FALSE; + + /* Initialize values in thread 0, pass them along in other threads */ + + for (i = 1; i <= count; ++i) + do + { + /* Generate new token (once the old one has been removed)*/ + SVN_ERR(svn_named_atomic__cmpxchg(&value, + i, + 0, + atomic_out)); + SVN_ERR(check_watchdog(watchdog, &done)); + if (done) return SVN_NO_ERROR; + } + while (value != 0); + + return SVN_NO_ERROR; +} + +/* "pipeline" test: the main loop. Each one of the COUNT workers receives + * data in its ATOMIC_IN and passes it on to ATOMIC_OUT until ATOMIC_COUNTER + * exceeds ITERATIONS. + */ +static svn_error_t * +test_pipeline_loop(svn_named_atomic__t *atomic_in, + svn_named_atomic__t *atomic_out, + svn_named_atomic__t *atomic_counter, + int count, + int iterations, + watchdog_t *watchdog) +{ + apr_int64_t value = 0, old_value, last_value = 0; + apr_int64_t counter; + svn_boolean_t done = FALSE; + + /* Pass the tokens along */ + + do + { + /* Wait for and consume incoming token. */ + do + { + SVN_ERR(svn_named_atomic__write(&value, 0, atomic_in)); + SVN_ERR(check_watchdog(watchdog, &done)); + if (done) return SVN_NO_ERROR; + } + while (value == 0); + + /* All tokes must come in in the same order */ + SVN_TEST_ASSERT((last_value % 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, + atomic_out)); + SVN_ERR(check_watchdog(watchdog, &done)); + if (done) return SVN_NO_ERROR; + } + while (old_value != 0); + + /* Count the number of operations */ + SVN_ERR(svn_named_atomic__add(&counter, 1, atomic_counter)); + } + while (counter < iterations); + + /* done */ + + return SVN_NO_ERROR; +} + +/* "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 * +test_pipeline(int id, int count, int iterations, apr_pool_t *pool) +{ + svn_atomic_namespace__t *ns; + svn_named_atomic__t *atomic_in; + svn_named_atomic__t *atomic_out; + svn_named_atomic__t *atomic_counter; + svn_error_t *err = SVN_NO_ERROR; + watchdog_t watchdog; + + /* get the two I/O atomics for this thread */ + SVN_ERR(svn_atomic_namespace__create(&ns, name_namespace, pool)); + SVN_ERR(svn_named_atomic__get(&atomic_in, + ns, + apr_pstrcat(pool, + ATOMIC_NAME, + apr_itoa(pool, + id), + NULL), + FALSE)); + SVN_ERR(svn_named_atomic__get(&atomic_out, + ns, + apr_pstrcat(pool, + ATOMIC_NAME, + apr_itoa(pool, + (id + 1) % count), + NULL), + FALSE)); + + /* our iteration counter */ + SVN_ERR(svn_named_atomic__get(&atomic_counter, ns, "counter", FALSE)); + + /* safeguard our execution time. Limit it to 20s */ + init_watchdog(&watchdog, atomic_counter, iterations, 20000000); + + /* fill pipeline */ + if (id == 0) + err = test_pipeline_prepare(atomic_out, count, &watchdog); + + /* Pass the tokens along */ + if (!err) + err = test_pipeline_loop(atomic_in, atomic_out, atomic_counter, + count, iterations, &watchdog); + + /* if we experienced an error, cause everybody to exit */ + if (err) + svn_error_clear(svn_named_atomic__write(NULL, iterations, atomic_counter)); + + /* done */ + + return err; +} |
