You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by st...@apache.org on 2012/12/16 22:56:17 UTC
svn commit: r1422699 - in /subversion/branches/cache-server/subversion:
include/private/svn_slot_lock.h libsvn_subr/slot_lock.c
Author: stefan2
Date: Sun Dec 16 21:56:05 2012
New Revision: 1422699
URL: http://svn.apache.org/viewvc?rev=1422699&view=rev
Log:
Implement the low-level part of the cache access synchronization mechanism
and declare a SVN-private API for it.
* subversion/include/private/svn_slot_lock.h
(): new header
(svn__slot_lock_token_t,
svn__slot_lock_t): declare new data types
(svn__slot_lock_size,
svn__slot_lock_initialize,
svn__slot_lock_create,
svn__slot_lock_try_get_shared_lock,
svn__slot_lock_get_shared_lock,
svn__slot_lock_release_shared_lock,
svn__slot_lock_try_get_exclusive_lock,
svn__slot_lock_get_exclusive_lock,
svn__slot_lock_release_exclusive_lock): declare new API
* subversion/libsvn_subr/slot_lock.c
(): new source file
(NO_TOKEN,
NO_LOCK,
MAX_BUSY_RETRIES): new constants
(svn__slot_lock_t): define data structure
(retry_policy): new utility function
(svn__slot_lock_size,
svn__slot_lock_initialize,
svn__slot_lock_create,
svn__slot_lock_try_get_shared_lock,
svn__slot_lock_get_shared_lock,
svn__slot_lock_release_shared_lock,
svn__slot_lock_try_get_exclusive_lock,
svn__slot_lock_get_exclusive_lock,
svn__slot_lock_release_exclusive_lock): implement new API
Added:
subversion/branches/cache-server/subversion/include/private/svn_slot_lock.h
subversion/branches/cache-server/subversion/libsvn_subr/slot_lock.c
Added: subversion/branches/cache-server/subversion/include/private/svn_slot_lock.h
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/subversion/include/private/svn_slot_lock.h?rev=1422699&view=auto
==============================================================================
--- subversion/branches/cache-server/subversion/include/private/svn_slot_lock.h (added)
+++ subversion/branches/cache-server/subversion/include/private/svn_slot_lock.h Sun Dec 16 21:56:05 2012
@@ -0,0 +1,150 @@
+/*
+ * svn_slot_lock.h: routines for low-overhead machine-wide locks
+ *
+ * ====================================================================
+ * 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_atomic.h"
+#include "svn_pools.h"
+
+/**
+ * The lock data type defined here allows for low-overhead reader / writer
+ * locks that will also work cross-process. Their intended use is in shared
+ * memory data structures.
+ *
+ * Every lock object has a pre-defined number of slots. A user may either
+ * acquire one or all of these slots. In the first case, the lock is a
+ * "shared lock", otherwise we call them "exclusive locks". A shared lock
+ * merely prevents exclusive locks to be acquired. This provides the
+ * many readers / one writer exclusion scheme.
+ *
+ * To handle situations where locks need to be broken because the locking
+ * process is no longer available, we only provide API to remove those
+ * locks but no way to identify them. This must be implemented by external
+ * logic.
+ */
+
+/**
+ * Values of this type will identify the owner of a lock.
+ */
+typedef svn_atomic_t svn__slot_lock_token_t;
+
+/**
+ * Slot locks are of this type.
+ */
+typedef struct svn__slot_lock_t svn__slot_lock_t;
+
+/**
+ * Returns the size in bytes of a slot lock a @a capacity number of slots
+ * (i.e. the maximum number of shared locks it may held at a time).
+ */
+apr_size_t
+svn__slot_lock_size(apr_size_t capacity);
+
+/**
+ * Initialize the lock structure at @a lock with @a capacity slots.
+ * It must have been allocated with room for at least @a capacity slots.
+ *
+ * @note Use this only to initialize data structures not created with
+ * #svn__slot_lock_create such as static ones.
+ */
+void
+svn__slot_lock_initialize(svn__slot_lock_t *lock, apr_size_t capacity);
+
+/**
+ * Allocate and initialize a slot lock with @a capacity slots in @a pool.
+ * Return the ready-to-use data structure.
+ */
+svn__slot_lock_t *
+svn__slot_lock_create(apr_size_t capacity, apr_pool_t *pool);
+
+/**
+ * Attempt to get a shared lock (i.e. acquire 1 slot) to the @a lock
+ * structure. Use @a token to identify the lock owner. If successful,
+ * return the slot that was used, and 0 otherwise. If @a token is 0,
+ * the lock attempt will fail and 0 is being returned.
+ */
+apr_size_t
+svn__slot_lock_try_get_shared_lock(svn__slot_lock_t *lock,
+ svn__slot_lock_token_t token);
+
+/**
+ * Get a shared lock (i.e. acquire 1 slot) to the @a lock structure.
+ * Use @a token to identify the lock owner. Return the slot > 0 that was
+ * used. If @a token is 0, the lock attempt will fail and 0 is being
+ * returned.
+ */
+apr_size_t
+svn__slot_lock_get_shared_lock(svn__slot_lock_t *lock,
+ svn__slot_lock_token_t token);
+
+/**
+ * Release the shared lock at @a slot in the @a lock structure. @a token
+ * must match the one being used when this slot was locked. Return TRUE,
+ * if @a slot is valid ans was as still held by @a token and FALSE otherwise.
+ *
+ * @note A FALSE result for a previously existing shared lock means that
+ * the original token has been invalided by some external logic. In turn,
+ * since the lock owner must still exist to make this call, one must assume
+ * that the data being protected by this lock got modified while we were
+ * reading it.
+ */
+svn_boolean_t
+svn__slot_lock_release_shared_lock(svn__slot_lock_t *lock,
+ apr_size_t slot,
+ svn__slot_lock_token_t token);
+
+/**
+ * Attempt to get an exclusive lock (i.e. acquire all slots) to the @a lock
+ * structure. Use @a token to identify the lock owner. Return TRUE, when
+ * successful and 0 otherwise. If @a token is 0, the lock attempt will
+ * fail and 0 is being returned.
+ */
+svn_boolean_t
+svn__slot_lock_try_get_exclusive_lock(svn__slot_lock_t *lock,
+ svn__slot_lock_token_t token);
+
+/**
+ * Get a exclusive lock (i.e. acquire all slots) to the @a lock structure.
+ * Use @a token to identify the lock owner. Return the slot > 0 that was
+ * used. If @a token is 0, this will be a no-op.
+ */
+void
+svn__slot_lock_get_exclusive_lock(svn__slot_lock_t *lock,
+ svn__slot_lock_token_t token);
+
+/**
+ * Release the exclusive lock from @a lock. @a token must match the one
+ * being used when the look was acquired. The function will return TRUE,
+ * if all slots were held by @a token.
+ * @note It is safe but less efficient to use this function to free a
+ * shared lock. It will simply release all slots held by the given @a token.
+ *
+ * @note Use this function to revoke locks held by the given @a token e.g.
+ * when the owner of that token has crashed.
+ *
+ * @note A FALSE result for an exclusive lock means that the original
+ * token has been invalided by some external logic. In turn, since the
+ * lock owner must still exist to make this call, one must assume that
+ * the data being protected by this lock has been compromised.
+ */
+svn_boolean_t
+svn__slot_lock_release_exclusive_lock(svn__slot_lock_t *lock,
+ svn__slot_lock_token_t token);
Added: subversion/branches/cache-server/subversion/libsvn_subr/slot_lock.c
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/subversion/libsvn_subr/slot_lock.c?rev=1422699&view=auto
==============================================================================
--- subversion/branches/cache-server/subversion/libsvn_subr/slot_lock.c (added)
+++ subversion/branches/cache-server/subversion/libsvn_subr/slot_lock.c Sun Dec 16 21:56:05 2012
@@ -0,0 +1,189 @@
+/*
+ * slot_lock.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_slot_lock.h"
+
+#include "svn_private_config.h"
+
+enum
+{
+ NO_TOKEN = 0,
+ NO_LOCK = 0,
+ MAX_BUSY_RETRIES = 1000
+};
+
+struct svn__slot_lock_t
+{
+ svn_atomic_t capacity;
+ volatile svn__slot_lock_token_t slots[];
+};
+
+apr_size_t
+svn__slot_lock_size(apr_size_t capacity)
+{
+ return sizeof(svn_atomic_t) + capacity * sizeof(svn__slot_lock_token_t);
+}
+
+void
+svn__slot_lock_initialize(svn__slot_lock_t *lock, apr_size_t capacity)
+{
+ svn_atomic_t i;
+ lock->capacity = (svn_atomic_t)capacity;
+
+ for (i = 0; i < capacity; ++i)
+ lock->slots[i] = NO_TOKEN;
+}
+
+svn__slot_lock_t *
+svn__slot_lock_create(apr_size_t capacity, apr_pool_t *pool)
+{
+ svn__slot_lock_t *lock = apr_palloc(pool, svn__slot_lock_size(capacity));
+ svn__slot_lock_initialize(lock, capacity);
+
+ return lock;
+}
+
+apr_size_t
+svn__slot_lock_try_get_shared_lock(svn__slot_lock_t *lock,
+ svn__slot_lock_token_t token)
+{
+ apr_size_t capacity = lock->capacity;
+ apr_size_t i = 0;
+ if (token == NO_TOKEN)
+ return NO_LOCK;
+
+ for (i = 0; i < capacity; ++i)
+ if (svn_atomic_cas(&lock->slots[i], token, NO_TOKEN) == NO_TOKEN)
+ return i + 1;
+
+ return NO_LOCK;
+}
+
+static void
+retry_policy(apr_size_t *retry_count)
+{
+ /* after some retries, wait for a milli-second to keep CPU load low */
+ if (*retry_count > MAX_BUSY_RETRIES)
+ apr_sleep(1000);
+
+ ++*retry_count;
+}
+
+apr_size_t
+svn__slot_lock_get_shared_lock(svn__slot_lock_t *lock,
+ svn__slot_lock_token_t token)
+{
+ apr_size_t result = NO_LOCK;
+ apr_size_t retry_count = 0;
+
+ if (token == NO_TOKEN)
+ return result;
+
+ while (result == NO_LOCK)
+ {
+ retry_policy(&retry_count);
+ result = svn__slot_lock_try_get_shared_lock(lock, token);
+ }
+
+ return result;
+}
+
+svn_boolean_t
+svn__slot_lock_release_shared_lock(svn__slot_lock_t *lock,
+ apr_size_t slot,
+ svn__slot_lock_token_t token)
+{
+ if (slot == 0 || slot > lock->capacity)
+ return FALSE;
+
+ return svn_atomic_cas(&lock->slots[slot-1], NO_TOKEN, token) == token
+ ? TRUE
+ : FALSE;
+}
+
+svn_boolean_t
+svn__slot_lock_try_get_exclusive_lock(svn__slot_lock_t *lock,
+ svn__slot_lock_token_t token)
+{
+ apr_size_t capacity = lock->capacity;
+ apr_size_t i = 0;
+ if (token == NO_TOKEN)
+ return FALSE;
+
+ for (i = 0; i < capacity; ++i)
+ if (svn_atomic_cas(&lock->slots[i], token, NO_TOKEN) != NO_TOKEN)
+ {
+ svn__slot_lock_release_exclusive_lock(lock, token);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+void
+svn__slot_lock_get_exclusive_lock(svn__slot_lock_t *lock,
+ svn__slot_lock_token_t token)
+{
+ apr_size_t retry_count = 0;
+ apr_size_t slots_locked = 0;
+ apr_size_t capacity = lock->capacity;
+ apr_size_t i = 0;
+
+ if (token == NO_TOKEN)
+ return;
+
+ /* try get slot 0 first. This is important because this way we sync
+ * multiple (concurrent) exclusive lock attempts.
+ */
+ while (svn_atomic_cas(&lock->slots[0], token, NO_TOKEN) != NO_TOKEN)
+ retry_policy(&retry_count);
+
+ /* slot 0 has been locked*/
+ slots_locked++;
+
+ /* get a locks for all other slots */
+ while (slots_locked < capacity)
+ {
+ retry_policy(&retry_count);
+
+ for (i = 1; i < capacity; ++i)
+ if (svn_atomic_cas(&lock->slots[i], token, NO_TOKEN) == NO_TOKEN)
+ ++slots_locked;
+ }
+}
+
+svn_boolean_t
+svn__slot_lock_release_exclusive_lock(svn__slot_lock_t *lock,
+ svn__slot_lock_token_t token)
+{
+ apr_size_t capacity = lock->capacity;
+ apr_size_t i = 0;
+ svn_boolean_t result = TRUE;
+
+ if (token != NO_TOKEN)
+ for (i = 0; i < capacity; ++i)
+ if (svn_atomic_cas(&lock->slots[i], NO_TOKEN, token) != token)
+ result = FALSE;
+
+ return result;
+}