Alien-SVN

 view release on metacpan or  search on metacpan

src/subversion/subversion/libsvn_subr/named_atomic.c  view on Meta::CPAN

/*
 * 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 <apr_global_mutex.h>
#include <apr_mmap.h>

#include "svn_private_config.h"
#include "private/svn_atomic.h"
#include "private/svn_mutex.h"
#include "svn_pools.h"
#include "svn_dirent_uri.h"
#include "svn_io.h"

/* Implementation aspects.
 *
 * We use a single shared memory block (memory mapped file) 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 MMAP object as well as adding new array entries
 * is being guarded by an APR global mutex. Since releasing the MMAP
 * structure and closing the underlying does not affect other users of the
 * same, cleanup will not be synchronized.
 *
 * 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 remainder is used to
 * store the NUL-terminated name.
 *
 * Must not be smaller than SVN_NAMED_ATOMIC__MAX_NAME_LENGTH.
 */
#define MAX_NAME_LENGTH (CACHE_LINE_LENGTH - sizeof(apr_int64_t) - 1)

/* Particle that will be appended to the namespace name to form the
 * name of the mutex / lock file used for that namespace.
 */
#define MUTEX_NAME_SUFFIX ".mutex"

/* Particle that will be appended to the namespace name to form the
 * name of the shared memory file that backs that namespace.
 */
#define SHM_NAME_SUFFIX ".shm"

/* Platform-dependent implementations of our basic atomic operations.
 * NA_SYNCHRONIZE(op) will ensure that the OP gets executed atomically.
 * This will be zero-overhead if OP itself is already atomic.
 *
 * (We don't call it SYNCHRONIZE because Windows has a preprocess macro by
 * that name.)
 *
 * 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.
 */
#if defined(WIN32) && ((_WIN32_WINNT >= 0x0502) || defined(InterlockedExchangeAdd64))

/* Interlocked API / intrinsics guarantee full data synchronization
 */
#define synched_read(mem) *mem
#define synched_write(mem, value) InterlockedExchange64(mem, value)
#define synched_add(mem, delta) InterlockedExchangeAdd64(mem, delta)
#define synched_cmpxchg(mem, value, comperand) \
  InterlockedCompareExchange64(mem, value, comperand)

#define NA_SYNCHRONIZE(_atomic,op) op;
#define NA_SYNCHRONIZE_IS_FAST TRUE

#elif SVN_HAS_ATOMIC_BUILTINS

/* GCC provides atomic intrinsics for most common CPU types
 */
#define synched_read(mem) *mem
#define synched_write(mem, value) __sync_lock_test_and_set(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 NA_SYNCHRONIZE(_atomic,op) op;
#define NA_SYNCHRONIZE_IS_FAST TRUE

#else

/* Default implementation
 */
static apr_int64_t
synched_read(volatile apr_int64_t *mem)
{
  return *mem;
}

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;
}



( run in 0.784 second using v1.01-cache-2.11-cpan-d7f47b0818f )