Return-Path: X-Original-To: apmail-subversion-commits-archive@minotaur.apache.org Delivered-To: apmail-subversion-commits-archive@minotaur.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 8884A9964 for ; Thu, 8 Mar 2012 21:19:08 +0000 (UTC) Received: (qmail 6529 invoked by uid 500); 8 Mar 2012 21:19:08 -0000 Delivered-To: apmail-subversion-commits-archive@subversion.apache.org Received: (qmail 6459 invoked by uid 500); 8 Mar 2012 21:19:08 -0000 Mailing-List: contact commits-help@subversion.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@subversion.apache.org Delivered-To: mailing list commits@subversion.apache.org Received: (qmail 6449 invoked by uid 99); 8 Mar 2012 21:19:08 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 08 Mar 2012 21:19:08 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=5.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 08 Mar 2012 21:19:06 +0000 Received: from eris.apache.org (localhost [127.0.0.1]) by eris.apache.org (Postfix) with ESMTP id 06B552388865; Thu, 8 Mar 2012 21:18:46 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1298587 - in /subversion/branches/revprop-cache: build.conf subversion/include/private/svn_named_atomic.h subversion/include/svn_error_codes.h subversion/libsvn_subr/svn_named_atomic.c Date: Thu, 08 Mar 2012 21:18:45 -0000 To: commits@subversion.apache.org From: stefan2@apache.org X-Mailer: svnmailer-1.0.8-patched Message-Id: <20120308211846.06B552388865@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Author: stefan2 Date: Thu Mar 8 21:18:45 2012 New Revision: 1298587 URL: http://svn.apache.org/viewvc?rev=1298587&view=rev Log: Define and implement svn_named_atomic__t and associated API. * subversion/include/private/svn_named_atomic.h new file, containing the new svn_named_atomic API * subversion/libsvn_subr/svn_named_atomic.c new file, implements the new API * subversion/include/svn_error_codes.h (SVN_ERR_BAD_ATOMIC): new error code used throughout the new API * build.conf (libsvn_subr): add new header to exports Added: subversion/branches/revprop-cache/subversion/include/private/svn_named_atomic.h subversion/branches/revprop-cache/subversion/libsvn_subr/svn_named_atomic.c Modified: subversion/branches/revprop-cache/build.conf subversion/branches/revprop-cache/subversion/include/svn_error_codes.h Modified: subversion/branches/revprop-cache/build.conf URL: http://svn.apache.org/viewvc/subversion/branches/revprop-cache/build.conf?rev=1298587&r1=1298586&r2=1298587&view=diff ============================================================================== --- subversion/branches/revprop-cache/build.conf (original) +++ subversion/branches/revprop-cache/build.conf Thu Mar 8 21:18:45 2012 @@ -331,7 +331,7 @@ msvc-export = private\svn_token.h private\svn_adler32.h private\svn_temp_serializer.h private\svn_io_private.h private\svn_string_private.h private\svn_magic.h - private\svn_subr_private.h private\svn_mutex.h + private\svn_subr_private.h private\svn_mutex.h private\svn_named_atomic.h # Working copy management lib [libsvn_wc] Added: subversion/branches/revprop-cache/subversion/include/private/svn_named_atomic.h URL: http://svn.apache.org/viewvc/subversion/branches/revprop-cache/subversion/include/private/svn_named_atomic.h?rev=1298587&view=auto ============================================================================== --- subversion/branches/revprop-cache/subversion/include/private/svn_named_atomic.h (added) +++ subversion/branches/revprop-cache/subversion/include/private/svn_named_atomic.h Thu Mar 8 21:18:45 2012 @@ -0,0 +1,107 @@ +/** + * @copyright + * ==================================================================== + * 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. + * ==================================================================== + * @endcopyright + * + * @file svn_named_atomics.h + * @brief Strutures and functions for machine-wide named atomics + */ + +#ifndef SVN_NAMED_ATOMICS_H +#define SVN_NAMED_ATOMICS_H + +#include "svn_error.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** An opaque structure that represents a named, system-wide visible + * 64 bit integer with atomic access routines. + */ +typedef struct svn_named_atomic__t svn_named_atomic__t; + +#define SVN_NAMED_ATOMIC__MAX_NAME_LENGTH 30 + +/** Find the atomic with the specified @a name and return it in @a *atomic. + * If no object with that name can be found, the behavior depends on + * @a auto_create. If it is @c FALSE, @a *atomic will be set to @c NULL. + * Otherwise, a new atomic will be created, its value set to 0 and the + * access structure be returned in @a *atomic. + * + * Note that @a name must be short and should not exceeed + * @ref SVN_NAMED_ATOMIC__MAX_NAME_LENGTH characters. The actual limit is + * implementation-dependent and may change in the future. This function + * will return an error if the specified name is longer than supported. + * + * This function will automatically initialize the shared memory region, + * if that hadn't been attempted before. Therefore, this may fail with + * a variety of errors. + */ +svn_error_t * +svn_named_atomic__get(svn_named_atomic__t **atomic, + const char *name, + svn_boolean_t auto_create); + +/** Read the @a atomic and return its current @a *value. + * An error will be returned if @a atomic is @c NULL. + */ +svn_error_t * +svn_named_atomic__read(apr_int64_t *value, + svn_named_atomic__t *atomic); + +/** Set the data in @a atomic to @a NEW_VALUE and return its old content + * in @a OLD_VALUE. @a OLD_VALUE may be NULL. + * + * An error will be returned if @a atomic is @c NULL. + */ +svn_error_t * +svn_named_atomic__write(apr_int64_t *old_value, + apr_int64_t new_value, + svn_named_atomic__t *atomic); + +/** Add @a delta to the data in @a atomic and return its new value in + * @a NEW_VALUE. @a NEW_VALUE may be NULL. + * + * An error will be returned if @a atomic is @c NULL. + */ +svn_error_t * +svn_named_atomic__add(apr_int64_t *new_value, + apr_int64_t delta, + svn_named_atomic__t *atomic); + +/** If the current data in @a atomic equals @a comperand, set it to + * @a NEW_VALUE. Return the initial value in @a OLD_VALUE. + * @a OLD_VALUE may be NULL. + * + * An error will be returned if @a atomic is @c NULL. + */ +svn_error_t * +svn_named_atomic__cmpxchg(apr_int64_t *old_value, + apr_int64_t new_value, + apr_int64_t comperand, + svn_named_atomic__t *atomic); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* SVN_NAMED_ATOMICS_H */ Modified: subversion/branches/revprop-cache/subversion/include/svn_error_codes.h URL: http://svn.apache.org/viewvc/subversion/branches/revprop-cache/subversion/include/svn_error_codes.h?rev=1298587&r1=1298586&r2=1298587&view=diff ============================================================================== --- subversion/branches/revprop-cache/subversion/include/svn_error_codes.h (original) +++ subversion/branches/revprop-cache/subversion/include/svn_error_codes.h Thu Mar 8 21:18:45 2012 @@ -224,6 +224,11 @@ SVN_ERROR_START SVN_ERR_BAD_CATEGORY_START + 14, "Invalid changelist name") + /** @since New in 1.8. */ + SVN_ERRDEF(SVN_ERR_BAD_ATOMIC, + SVN_ERR_BAD_CATEGORY_START + 15, + "Invalid atomic") + /* xml errors */ SVN_ERRDEF(SVN_ERR_XML_ATTRIB_NOT_FOUND, Added: subversion/branches/revprop-cache/subversion/libsvn_subr/svn_named_atomic.c URL: http://svn.apache.org/viewvc/subversion/branches/revprop-cache/subversion/libsvn_subr/svn_named_atomic.c?rev=1298587&view=auto ============================================================================== --- subversion/branches/revprop-cache/subversion/libsvn_subr/svn_named_atomic.c (added) +++ subversion/branches/revprop-cache/subversion/libsvn_subr/svn_named_atomic.c Thu Mar 8 21:18:45 2012 @@ -0,0 +1,454 @@ +/* + * svn_named_atomic.c: routines for machine-wide named atomics. + * + * ==================================================================== + * 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 "private/svn_named_atomic.h" + +#include +#include + +#include "svn_private_config.h" +#include "private/svn_atomic.h" +#include "svn_pools.h" + +/* Implementation aspects. + * + * We use a single shared memory block that will be created by the first + * user and merely mapped by all subsequent ones. The memory block contains + * an short header followed by a fixed-capacity array of named atomics. The + * number of entries currently in use is stored in the header part. + * + * Finding / creating the SHM object as well as additing new array entries + * is being guarded by an APR global mutex. + * + * The array is append-only. Once a process mapped the block into its + * address space, it may freely access any of the used entries. However, + * it must synchronize access to the volatile data within the entries. + * On Windows and where otherwise supported by GCC, lightweight "lock-free" + * synchronization will be used. Other targets serialize all access using + * a global mutex. + * + * Atomics will be identified by their name (a short string) and lookup + * takes linear time. But even that takes only about 10 microseconds for a + * full array scan -- which is in the same order of magnitude than e.g. a + * single global mutex lock / unlock pair. + */ + +/* Capacity of our shared memory object, i.e. max number of named atomics + * that may be created. Should have the form 2**N - 1. + */ +#define MAX_ATOMIC_COUNT 1023 + +/* We choose the size of a single named atomic object to fill a complete + * cache line (on most architectures). Thereby, we minimize the cache + * sync. overhead between different CPU cores. + */ +#define CACHE_LINE_LENGTH 64 + +/* We need 8 bytes for the actual value and the remainer is used to + * store the NUL-terminated name. + */ +#define MAX_NAME_LENGTH (CACHE_LINE_LENGTH - sizeof(apr_int64_t) - 1) + +/* Name of the global mutex that we will use. + */ +#define MUTEX_NAME "SvnNamedAtomicsMutex" + +/* Name of the shared memory file that we will use. + */ +#define SHM_NAME "SvnNamedAtomicsShm" + +/* Platform-dependent implementations of our basic atomic operations. + * SYNCHRONIZE(op) will ensure that the OP gets executed atomically. + * This will be zero-overhead if OP itself is already atomic. + * + * The default implementation will use the same mutex for initialization + * as well as any type of data access. This is quite expensive and we + * can do much better on most platforms. + */ +#ifdef _WIN32 + +/* Interlocked API / intrinsics guarantee full data synchronization + */ +#define synched_read(value) value +#define synched_write(mem, value) InterlockedExchange64(mem, value) +#define synched_add(mem, delta) InterlockedAdd64(mem, delta) +#define synched_cmpxchg(mem, value, comperand) \ + InterlockedCompareExchange64(mem, value, comperand) + +#define SYNCHRONIZE(op) op; + +#elif SVN_HAS_ATOMIC_BUILTINS + +/* GCC provides atomic intrinsics for most common CPU types + */ +#define synched_read(value) value +#define synched_write(mem, value) (*mem = value) +#define synched_add(mem, delta) __sync_add_and_fetch(mem, delta) +#define synched_cmpxchg(mem, value, comperand) \ + __sync_val_compare_and_swap(mem, comperand, value) + +#define SYNCHRONIZE(op) op; + +#else + +/* Default implementation + */ +static apr_int64_t +synched_read(apr_int64_t value) +{ + return value; +} + +static apr_int64_t +synched_write(volatile apr_int64_t *mem, apr_int64_t value) +{ + apr_int64_t old_value = *mem; + *mem = value; + + return old_value; +} + +static apr_int64_t +synched_add(volatile apr_int64_t *mem, apr_int64_t delta) +{ + return *mem += delta; +} + +static apr_int64_t +synched_cmpxchg(volatile apr_int64_t *mem, + apr_int64_t value, + apr_int64_t comperand) +{ + apr_int64_t old_value = *mem; + if (old_value == comperand) + *mem = value; + + return old_value; +} + +#define SYNCHRONIZE(op)\ + SVN_ERR(lock(&access));\ + op;\ + SVN_ERR(unlock(&access, SVN_NO_ERROR)); + +#endif + +/* Structure describing a single atomic: its VALUE and NAME. + */ +struct svn_named_atomic__t +{ + volatile apr_int64_t value; + char name[MAX_NAME_LENGTH + 1]; +}; + +/* Content of our shared memory buffer. COUNT is the number + * of used entries in ATOMICS. Insertion is append-only. + * PADDING is used to align the header information with the + * atomics to create a favourable data alignment. + */ +struct shared_data_t +{ + volatile apr_int32_t count; + char padding [sizeof(svn_named_atomic__t) - sizeof(apr_int32_t)]; + + svn_named_atomic__t atomics[MAX_ATOMIC_COUNT]; +}; + +/* This is intended to be a singleton struct. It contains all + * information necessary to initialize and access the shared + * memory. + */ +struct shared_mem_access_t +{ + /* Init flag used by svn_atomic__init_once */ + svn_atomic_t initialized; + + /* Pointer to the shared data mapped into our process */ + struct shared_data_t *data; + + /* Named mutex to control access to the shared mem */ + apr_global_mutex_t *mutex; + + /* Named APR shared memory object */ + apr_shm_t *shared_mem; + + /* Last time we checked, this was the number of used + * (i.e. fully initialized) items. I.e. we can read + * their names without futher sync. */ + volatile svn_atomic_t min_used; + + /* Pool for APR objects */ + apr_pool_t *pool; +}; + +/* Global / singleton instance to access our shared memory. + */ +static struct shared_mem_access_t access = {FALSE}; + +/* Utility that aquires a lock for ACCESS->MUTEX and converts + * error types when necessary. + */ +static svn_error_t * +lock(struct shared_mem_access_t *access) +{ + apr_status_t apr_err = apr_global_mutex_lock(access->mutex); + if (apr_err) + return svn_error_wrap_apr(apr_err, + _("Can't lock mutex for named atomics")); + + return SVN_NO_ERROR; +} + +/* Utility that releases a lock for ACCESS->MUTEX and converts + * error types when necessary. If the unlock succeeds, OUTER_ERR + * will be returned. If it fails, the unlock error will only be + * returned if the OUTER_ERR is NULL and ignored, otherwise. + */ +static svn_error_t * +unlock(struct shared_mem_access_t *access, svn_error_t * outer_err) +{ + apr_status_t apr_err = apr_global_mutex_unlock(access->mutex); + if (apr_err && !outer_err) + return svn_error_wrap_apr(apr_err, + _("Can't unlock mutex for named atomics")); + + return outer_err; +} + +/* Initialize the shared_mem_access_t given as BATON. POOL is unused. + */ +static svn_error_t * +initialize(void *baton, apr_pool_t *pool) +{ + apr_status_t apr_err; + struct shared_mem_access_t *access = baton; + + /* Since this function will be called through svn_atomic__init_once, + * providing a new root pool each time this is called, is not feasible. + * -> Create our own root pool here. + */ + access->pool = svn_pool_create(NULL); + + /* Next, we must ensure that no-one else tries to initialize the shared + * memory while we are about to do the same. + */ + apr_err = apr_global_mutex_create(&access->mutex, + MUTEX_NAME, + APR_LOCK_DEFAULT, + access->pool); + if (apr_err) + return svn_error_wrap_apr(apr_err, + _("Can't create global mutex for named atomics")); + + SVN_ERR(lock(access)); + + /* First, look for an existing shared memory object. If it doesn't + * exist, create one. + */ + apr_err = apr_shm_attach(&access->shared_mem, SHM_NAME, access->pool); + if (apr_err) + { + apr_err = apr_shm_create(&access->shared_mem, + sizeof(*access->data), + SHM_NAME, + access->pool); + if (apr_err) + return unlock(access, svn_error_wrap_apr(apr_err, + _("Can't get shared memory for named atomics"))); + + access->data = apr_shm_baseaddr_get(access->shared_mem); + + /* Zero all counters, values and names. + */ + memset(access->data, 0, sizeof(*access->data)); + } + else + access->data = apr_shm_baseaddr_get(access->shared_mem); + + /* Cache the number of existing, complete entries. There can't be incomplete + * onces from other processe because we hold the mutex. Our process may also + * not access this information since we are being called from within + * svn_atomic__init_once. + */ + access->min_used = access->data->count; + + /* Unlock to allow other processes may access the shared memory as well. + */ + return unlock(access, SVN_NO_ERROR); +} + +/* Validate the ATOMIC parameter, i.e it's address. + */ +static svn_error_t * +validate(svn_named_atomic__t *atomic) +{ + return atomic + && atomic >= &access.data->atomics[0] + && atomic < &access.data->atomics[access.data->count] + ? SVN_NO_ERROR + : svn_error_create(SVN_ERR_BAD_ATOMIC, 0, _("Not a valid atomic")); +} + +/* Implement API */ + +svn_error_t * +svn_named_atomic__get(svn_named_atomic__t **atomic, + const char *name, + svn_boolean_t auto_create) +{ + apr_int32_t i, count; + svn_error_t *error = SVN_NO_ERROR; + apr_size_t len = strlen(name); + + /* Check parameters and make sure we return a NULL atomic + * in case of failure. + */ + *atomic = NULL; + if (len > MAX_NAME_LENGTH) + return svn_error_create(SVN_ERR_BAD_ATOMIC, 0, + _("Atomic name too long.")); + + /* Auto-initialize our access to the shared memory + */ + SVN_ERR(svn_atomic__init_once(&access.initialized, + initialize, + &access, + NULL)); + + /* Optimistic lookup. + * Because we never change the name of existing atomics and may only + * append new ones, we can safely compare the name of existing ones + * with the name that we are looking for. + */ + for (i = 0, count = svn_atomic_read(&access.min_used); i < count; ++i) + if (strncmp(access.data->atomics[i].name, name, len + 1) == 0) + { + *atomic = &access.data->atomics[i]; + return SVN_NO_ERROR; + } + + /* Try harder: + * Serialize all lookup and insert the item, if necessary and allowed. + */ + SVN_ERR(lock(&access)); + + /* We only need to check for new entries. + */ + for (i = count; i < access.data->count; ++i) + if (strcmp(access.data->atomics[i].name, name)) + { + *atomic = &access.data->atomics[i]; + + /* Update our cached number of complete entries. */ + svn_atomic_set(&access.min_used, access.data->count); + + return unlock(&access, error); + } + + /* Not found. Append a new entry, if allowed & possible. + */ + if (auto_create) + { + if (access.data->count < MAX_ATOMIC_COUNT) + { + access.data->atomics[access.data->count].value = 0; + memcpy(access.data->atomics[access.data->count].name, name, len+1); + + *atomic = &access.data->atomics[access.data->count]; + ++access.data->count; + } + else + error = svn_error_create(SVN_ERR_BAD_ATOMIC, 0, + _("Out of slots for named atomic.")); + } + + /* We are mainly done here. Let others continue their work. + */ + SVN_ERR(unlock(&access, error)); + + /* Only now can we be sure that a full memory barrier has been set + * and that the new entry has been written to memory in full. + */ + svn_atomic_set(&access.min_used, access.data->count); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_named_atomic__read(apr_int64_t *value, + svn_named_atomic__t *atomic) +{ + SVN_ERR(validate(atomic)); + SYNCHRONIZE(*value = synched_read(atomic->value)); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_named_atomic__write(apr_int64_t *old_value, + apr_int64_t new_value, + svn_named_atomic__t *atomic) +{ + apr_int64_t temp; + + SVN_ERR(validate(atomic)); + SYNCHRONIZE(temp = synched_write(&atomic->value, new_value)); + + if (old_value) + *old_value = temp; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_named_atomic__add(apr_int64_t *new_value, + apr_int64_t delta, + svn_named_atomic__t *atomic) +{ + apr_int64_t temp; + + SVN_ERR(validate(atomic)); + SYNCHRONIZE(temp = synched_write(&atomic->value, delta)); + + if (new_value) + *new_value = temp; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_named_atomic__cmpxchg(apr_int64_t *old_value, + apr_int64_t new_value, + apr_int64_t comperand, + svn_named_atomic__t *atomic) +{ + apr_int64_t temp; + + SVN_ERR(validate(atomic)); + SYNCHRONIZE(temp = synched_cmpxchg(&atomic->value, new_value, comperand)); + + if (old_value) + *old_value = temp; + + return SVN_NO_ERROR; +}