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 2014/04/11 01:35:46 UTC
svn commit: r1586505 -
/subversion/branches/thunder/subversion/libsvn_fs_util/thunder.c
Author: stefan2
Date: Thu Apr 10 23:35:45 2014
New Revision: 1586505
URL: http://svn.apache.org/r1586505
Log:
On the thunder branch: Document internal data structures.
Also, be sure to serialize all access to ACCESS_T members.
* subversion/libsvn_fs_util/thunder.c
(access_t,
svn_fs__thunder_t,
svn_fs__thunder_access_t): In-depth documentation of the data structs.
(set_access): Modify all members in this serialized function instead of
within its parent.
(get_access): Write access_t members via set_access.
(reset_access): Move up.
(remove_access,
release_access): Mark access_t as unused in the same locking context
as in get_access / set_access.
Modified:
subversion/branches/thunder/subversion/libsvn_fs_util/thunder.c
Modified: subversion/branches/thunder/subversion/libsvn_fs_util/thunder.c
URL: http://svn.apache.org/viewvc/subversion/branches/thunder/subversion/libsvn_fs_util/thunder.c?rev=1586505&r1=1586504&r2=1586505&view=diff
==============================================================================
--- subversion/branches/thunder/subversion/libsvn_fs_util/thunder.c (original)
+++ subversion/branches/thunder/subversion/libsvn_fs_util/thunder.c Thu Apr 10 23:35:45 2014
@@ -44,33 +44,95 @@
#if APR_HAS_THREADS
+/* Internal data structure describing a single access-in-progress.
+ * Instances of this object are relatively expensive to create (mainly due
+ * to the sync objects). So, we should recycle unused instances.
+ *
+ * Also, never destroying these instances for the lifetime of the registry,
+ * prevents synchronization / destruction hazards. Same goes for the
+ * string buffer in KEY. Setting it to different values and expanding it
+ * by re-allocation will never render the previous memory location
+ * inaccessible. */
typedef struct access_t
{
+ /* Key identifying the data location being accessed. If this is empty,
+ * the entry is unused, i.e. the respective access has completed.
+ * Must not be modified while being used as key in THUNDER_T->IN_ACCESS. */
svn_stringbuf_t *key;
+ /* Timestamp of when this instance has been added to the in-access list. */
apr_time_t started;
+
+ /* This will be signaled when the access completes. */
apr_thread_cond_t *condition;
+
+ /* Mutex to be used with CONDITION. Must also be used when accessing
+ * any other member of this struct. */
svn_mutex__t *mutex;
+ /* ID of the thread performing the access, i.e. the one that others may
+ * wait for. Only valid while the instance is in use. */
apr_os_thread_t owning_thread;
} access_t;
+/* The registry. */
struct svn_fs__thunder_t
{
+ /* Sync. object for all modifiable members (i.e. containers). */
svn_mutex__t *mutex;
+
+ /* Our own root pool. Since we use it in multiple threads, we can't use
+ * OWNING_POOL because we don't know whether that is thread-safe.
+ * This pool isn't thread-safe either, but we make sure to serialize
+ * all access to it - implicitly by serializing all container changes.
+ */
apr_pool_t *pool;
+
+ /* The pool that the registry got allocated in.
+ * We use it to kill APR pool cleanup notifications as necessary. */
apr_pool_t *owning_pool;
+ /* Timeout in musecs for all threads waiting for accesses to complete.
+ * The timeout is measured from the start of the access instead the
+ * begin of the wait. Hence, entries in IN_ACCESS may time out long
+ * before there is even a second attempt. */
apr_time_t timeout;
+ /* ACCESS_T.KEY -> ACCESS_T* map containing all accesses currently
+ * "in progress". These are all that we handed out tokens for
+ * (svn_fs__thunder_begin_access) which have not been returned yet
+ * (svn_fs__thunder_end_access). Entries that have already timed out
+ * will only be detected and removed by those functions. */
apr_hash_t *in_access;
+
+ /* Collection of all unused ACCESS_T instances. All of them have been
+ * used at some point in the past and their total number is implicitly
+ * limited to the maximum number of concurrent accesses - roughly the
+ * number of concurrent threads or a small multiple of it. */
apr_array_header_t *recyler;
};
+/* Access token returned to callers. It contains all references to internal
+ * structs required to release it in svn_fs__thunder_end_access. */
struct svn_fs__thunder_access_t
{
+ /* The registry. */
svn_fs__thunder_t *thunder;
+
+ /* The internal access object that we acquired. Because double release
+ * is safe as per API contract, this access object may have been released
+ * and re-used already. Use KEY to check that.
+ * Remains valid as long as THUNDER is valid. */
access_t *access;
+
+ /* Value of ACCESS->KEY when we acquired this token. If they don't match
+ * anymore, this is a second release attempt.
+ *
+ * In case ACCESS got reused for the exact same location, we will not be
+ * able to detect the difference and signal the access completed. The
+ * result is similar to a timeout - i.e. an efficiency but will not cause
+ * correctness issues.
+ */
svn_stringbuf_t *key;
};
@@ -177,6 +239,8 @@ set_access(access_t *access,
svn_stringbuf_t *key)
{
svn_stringbuf_set(access->key, key->data);
+ access->started = apr_time_now();
+ access->owning_thread = apr_os_thread_current();
return SVN_NO_ERROR;
}
@@ -211,16 +275,12 @@ get_access(access_t **access,
if (thunder->recyler->nelts)
{
result = *(access_t **)apr_array_pop(thunder->recyler);
-
- /* Make sure that access to the key (also acting as a usage marker)
- * gets serialized. */
- SVN_MUTEX__WITH_LOCK(result->mutex, set_access(result, key));
}
else
{
apr_status_t status;
result = apr_pcalloc(thunder->pool, sizeof(*result));
- result->key = svn_stringbuf_dup(key, thunder->pool);
+ result->key = svn_stringbuf_create_ensure(key->len, thunder->pool);
SVN_ERR(svn_mutex__init(&result->mutex, TRUE, thunder->pool));
status = apr_thread_cond_create(&result->condition, thunder->pool);
if (status != APR_SUCCESS)
@@ -229,8 +289,7 @@ get_access(access_t **access,
}
/* Start the access and remember which thread we will be waiting for. */
- result->started = apr_time_now();
- result->owning_thread = apr_os_thread_current();
+ SVN_MUTEX__WITH_LOCK(result->mutex, set_access(result, key));
/* Add it to the list of accesses currently under way. */
apr_hash_set(thunder->in_access, result->key->data, key->len, result);
@@ -240,6 +299,18 @@ get_access(access_t **access,
return SVN_NO_ERROR;
}
+/* Mark ACCESS as unused.
+ *
+ * Callers must serialize for ACCESS.
+ */
+static svn_error_t *
+reset_access(access_t *access)
+{
+ svn_stringbuf_setempty(access->key);
+
+ return SVN_NO_ERROR;
+}
+
/* Remove *ACCESS from THUNDER's list of accesses currently in progress.
* This is a no-op when *ACCESS is not the current entry for KEY. In that
* case, we set *ACCESS to NULL.
@@ -256,6 +327,9 @@ remove_access(access_t **access,
{
/* remove entry from hash */
apr_hash_set(thunder->in_access, key->data, key->len, NULL);
+
+ /* Sync with the time-out test in svn_fs__thunder_begin_access. */
+ SVN_MUTEX__WITH_LOCK((*access)->mutex, reset_access(*access));
}
else
{
@@ -267,18 +341,6 @@ remove_access(access_t **access,
return SVN_NO_ERROR;
}
-/* Mark ACCESS as unused.
- *
- * Callers must serialize for ACCESS.
- */
-static svn_error_t *
-reset_access(access_t *access)
-{
- svn_stringbuf_setempty(access->key);
-
- return SVN_NO_ERROR;
-}
-
/* Move the unused ACCESS to the list of recyclable objects in THUNDER.
*
* Callers must serialize for THUNDER.
@@ -307,16 +369,11 @@ release_access(svn_fs__thunder_t *thunde
* releasing ACCESS. */
if (access)
{
- apr_status_t status;
-
- /* Sync with the time-out test in svn_fs__thunder_begin_access. */
- SVN_MUTEX__WITH_LOCK(access->mutex, reset_access(access));
-
/* At this point, no thread will attempt to wait for this access,
* so we only have to wake up those who already wait. */
/* Tell / wake everybody that the access has been completed now. */
- status = apr_thread_cond_broadcast(access->condition);
+ apr_status_t status = apr_thread_cond_broadcast(access->condition);
if (status != APR_SUCCESS)
return svn_error_trace(svn_error_wrap_apr(status,
_("Can't signal condition")));