Commit e976b56d by Russell Belfer Committed by Vicent Marti

Add git__compare_and_swap and use it

This removes the lock from the repository object and changes the
internals to use the new atomic git__compare_and_swap to update
the _odb, _config, _index, and _refdb variables in a threadsafe
manner.
parent 53607868
...@@ -10,14 +10,6 @@ ...@@ -10,14 +10,6 @@
#include "mwindow.h" #include "mwindow.h"
#include "hash.h" #include "hash.h"
#if defined(GIT_THREADS) && defined(_MSC_VER)
# define GIT_MEMORY_BARRIER MemoryBarrier()
#elif defined(GIT_THREADS)
# define GIT_MEMORY_BARRIER __sync_synchronize()
#else
# define GIT_MEMORY_BARRIER /* noop */
#endif
typedef struct { typedef struct {
git_error *last_error; git_error *last_error;
git_error error_t; git_error error_t;
......
...@@ -32,43 +32,57 @@ ...@@ -32,43 +32,57 @@
#define GIT_TEMPLATE_DIR "/usr/share/git-core/templates" #define GIT_TEMPLATE_DIR "/usr/share/git-core/templates"
#define repo_swap_ptr(repo,item_ptr,new_value) \ static void set_odb(git_repository *repo, git_odb *odb)
git__swap(&repo->lock, (void **)item_ptr, new_value)
static void drop_odb(git_repository *repo)
{ {
git_odb *dropme = repo_swap_ptr(repo, &repo->_odb, NULL); if (odb) {
if (dropme != NULL) { GIT_REFCOUNT_OWN(odb, repo);
GIT_REFCOUNT_OWN(dropme, NULL); GIT_REFCOUNT_INC(odb);
git_odb_free(dropme); }
if ((odb = git__swap(repo->_odb, odb)) != NULL) {
GIT_REFCOUNT_OWN(odb, NULL);
git_odb_free(odb);
} }
} }
static void drop_refdb(git_repository *repo) static void set_refdb(git_repository *repo, git_refdb *refdb)
{ {
git_refdb *dropme = repo_swap_ptr(repo, &repo->_refdb, NULL); if (refdb) {
if (dropme != NULL) { GIT_REFCOUNT_OWN(refdb, repo);
GIT_REFCOUNT_OWN(dropme, NULL); GIT_REFCOUNT_INC(refdb);
git_refdb_free(dropme); }
if ((refdb = git__swap(repo->_refdb, refdb)) != NULL) {
GIT_REFCOUNT_OWN(refdb, NULL);
git_refdb_free(refdb);
} }
} }
static void drop_config(git_repository *repo) static void set_config(git_repository *repo, git_config *config)
{ {
git_config *dropme = repo_swap_ptr(repo, &repo->_config, NULL); if (config) {
if (dropme != NULL) { GIT_REFCOUNT_OWN(config, repo);
GIT_REFCOUNT_OWN(dropme, NULL); GIT_REFCOUNT_INC(config);
git_config_free(dropme);
git_repository__cvar_cache_clear(repo);
} }
if ((config = git__swap(repo->_config, config)) != NULL) {
GIT_REFCOUNT_OWN(config, NULL);
git_config_free(config);
}
git_repository__cvar_cache_clear(repo);
} }
static void drop_index(git_repository *repo) static void set_index(git_repository *repo, git_index *index)
{ {
git_index *dropme = repo_swap_ptr(repo, &repo->_index, NULL); if (index) {
if (dropme != NULL) { GIT_REFCOUNT_OWN(index, repo);
GIT_REFCOUNT_OWN(dropme, NULL); GIT_REFCOUNT_INC(index);
git_index_free(dropme); }
if ((index = git__swap(repo->_index, index)) != NULL) {
GIT_REFCOUNT_OWN(index, NULL);
git_index_free(index);
} }
} }
...@@ -81,15 +95,14 @@ void git_repository_free(git_repository *repo) ...@@ -81,15 +95,14 @@ void git_repository_free(git_repository *repo)
git_attr_cache_flush(repo); git_attr_cache_flush(repo);
git_submodule_config_free(repo); git_submodule_config_free(repo);
drop_config(repo); set_config(repo, NULL);
drop_index(repo); set_index(repo, NULL);
drop_odb(repo); set_odb(repo, NULL);
drop_refdb(repo); set_refdb(repo, NULL);
git__free(repo->path_repository); git__free(repo->path_repository);
git__free(repo->workdir); git__free(repo->workdir);
git_mutex_free(&repo->lock);
git__free(repo); git__free(repo);
} }
...@@ -122,8 +135,6 @@ static git_repository *repository_alloc(void) ...@@ -122,8 +135,6 @@ static git_repository *repository_alloc(void)
memset(repo, 0x0, sizeof(git_repository)); memset(repo, 0x0, sizeof(git_repository));
git_mutex_init(&repo->lock);
if (git_cache_init(&repo->objects) < 0) { if (git_cache_init(&repo->objects) < 0) {
git__free(repo); git__free(repo);
return NULL; return NULL;
...@@ -581,7 +592,7 @@ int git_repository_config__weakptr(git_config **out, git_repository *repo) ...@@ -581,7 +592,7 @@ int git_repository_config__weakptr(git_config **out, git_repository *repo)
if (!error) { if (!error) {
GIT_REFCOUNT_OWN(config, repo); GIT_REFCOUNT_OWN(config, repo);
config = repo_swap_ptr(repo, &repo->_config, config); config = git__compare_and_swap(&repo->_config, NULL, config);
if (config != NULL) { if (config != NULL) {
GIT_REFCOUNT_OWN(config, NULL); GIT_REFCOUNT_OWN(config, NULL);
git_config_free(config); git_config_free(config);
...@@ -609,17 +620,7 @@ int git_repository_config(git_config **out, git_repository *repo) ...@@ -609,17 +620,7 @@ int git_repository_config(git_config **out, git_repository *repo)
void git_repository_set_config(git_repository *repo, git_config *config) void git_repository_set_config(git_repository *repo, git_config *config)
{ {
assert(repo && config); assert(repo && config);
set_config(repo, config);
GIT_REFCOUNT_OWN(config, repo);
GIT_REFCOUNT_INC(config);
config = repo_swap_ptr(repo, &repo->_config, config);
if (config != NULL) {
GIT_REFCOUNT_OWN(config, NULL);
git_config_free(config);
}
git_repository__cvar_cache_clear(repo);
} }
int git_repository_odb__weakptr(git_odb **out, git_repository *repo) int git_repository_odb__weakptr(git_odb **out, git_repository *repo)
...@@ -638,7 +639,7 @@ int git_repository_odb__weakptr(git_odb **out, git_repository *repo) ...@@ -638,7 +639,7 @@ int git_repository_odb__weakptr(git_odb **out, git_repository *repo)
if (!error) { if (!error) {
GIT_REFCOUNT_OWN(odb, repo); GIT_REFCOUNT_OWN(odb, repo);
odb = repo_swap_ptr(repo, &repo->_odb, odb); odb = git__compare_and_swap(&repo->_odb, NULL, odb);
if (odb != NULL) { if (odb != NULL) {
GIT_REFCOUNT_OWN(odb, NULL); GIT_REFCOUNT_OWN(odb, NULL);
git_odb_free(odb); git_odb_free(odb);
...@@ -664,15 +665,7 @@ int git_repository_odb(git_odb **out, git_repository *repo) ...@@ -664,15 +665,7 @@ int git_repository_odb(git_odb **out, git_repository *repo)
void git_repository_set_odb(git_repository *repo, git_odb *odb) void git_repository_set_odb(git_repository *repo, git_odb *odb)
{ {
assert(repo && odb); assert(repo && odb);
set_odb(repo, odb);
GIT_REFCOUNT_OWN(odb, repo);
GIT_REFCOUNT_INC(odb);
odb = repo_swap_ptr(repo, &repo->_odb, odb);
if (odb != NULL) {
GIT_REFCOUNT_OWN(odb, NULL);
git_odb_free(odb);
}
} }
int git_repository_refdb__weakptr(git_refdb **out, git_repository *repo) int git_repository_refdb__weakptr(git_refdb **out, git_repository *repo)
...@@ -688,7 +681,7 @@ int git_repository_refdb__weakptr(git_refdb **out, git_repository *repo) ...@@ -688,7 +681,7 @@ int git_repository_refdb__weakptr(git_refdb **out, git_repository *repo)
if (!error) { if (!error) {
GIT_REFCOUNT_OWN(refdb, repo); GIT_REFCOUNT_OWN(refdb, repo);
refdb = repo_swap_ptr(repo, &repo->_refdb, refdb); refdb = git__compare_and_swap(&repo->_refdb, NULL, refdb);
if (refdb != NULL) { if (refdb != NULL) {
GIT_REFCOUNT_OWN(refdb, NULL); GIT_REFCOUNT_OWN(refdb, NULL);
git_refdb_free(refdb); git_refdb_free(refdb);
...@@ -712,15 +705,7 @@ int git_repository_refdb(git_refdb **out, git_repository *repo) ...@@ -712,15 +705,7 @@ int git_repository_refdb(git_refdb **out, git_repository *repo)
void git_repository_set_refdb(git_repository *repo, git_refdb *refdb) void git_repository_set_refdb(git_repository *repo, git_refdb *refdb)
{ {
assert(repo && refdb); assert(repo && refdb);
set_refdb(repo, refdb);
GIT_REFCOUNT_OWN(refdb, repo);
GIT_REFCOUNT_INC(refdb);
refdb = repo_swap_ptr(repo, &repo->_refdb, refdb);
if (refdb != NULL) {
GIT_REFCOUNT_OWN(refdb, NULL);
git_refdb_free(refdb);
}
} }
int git_repository_index__weakptr(git_index **out, git_repository *repo) int git_repository_index__weakptr(git_index **out, git_repository *repo)
...@@ -739,7 +724,7 @@ int git_repository_index__weakptr(git_index **out, git_repository *repo) ...@@ -739,7 +724,7 @@ int git_repository_index__weakptr(git_index **out, git_repository *repo)
if (!error) { if (!error) {
GIT_REFCOUNT_OWN(index, repo); GIT_REFCOUNT_OWN(index, repo);
index = repo_swap_ptr(repo, &repo->_index, index); index = git__compare_and_swap(&repo->_index, NULL, index);
if (index != NULL) { if (index != NULL) {
GIT_REFCOUNT_OWN(index, NULL); GIT_REFCOUNT_OWN(index, NULL);
git_index_free(index); git_index_free(index);
...@@ -767,15 +752,7 @@ int git_repository_index(git_index **out, git_repository *repo) ...@@ -767,15 +752,7 @@ int git_repository_index(git_index **out, git_repository *repo)
void git_repository_set_index(git_repository *repo, git_index *index) void git_repository_set_index(git_repository *repo, git_index *index)
{ {
assert(repo && index); assert(repo && index);
set_index(repo, index);
GIT_REFCOUNT_OWN(index, repo);
GIT_REFCOUNT_INC(index);
index = repo_swap_ptr(repo, &repo->_index, index);
if (index != NULL) {
GIT_REFCOUNT_OWN(index, NULL);
git_index_free(index);
}
} }
static int check_repositoryformatversion(git_config *config) static int check_repositoryformatversion(git_config *config)
...@@ -1465,14 +1442,13 @@ static int at_least_one_cb(const char *refname, void *payload) ...@@ -1465,14 +1442,13 @@ static int at_least_one_cb(const char *refname, void *payload)
static int repo_contains_no_reference(git_repository *repo) static int repo_contains_no_reference(git_repository *repo)
{ {
int error; int error = git_reference_foreach(repo, GIT_REF_LISTALL, at_least_one_cb, NULL);
error = git_reference_foreach(repo, GIT_REF_LISTALL, at_least_one_cb, NULL);
if (error == GIT_EUSER) if (error == GIT_EUSER)
return 0; return 0;
if (!error)
return error == 0 ? 1 : error; return 1;
return error;
} }
int git_repository_is_empty(git_repository *repo) int git_repository_is_empty(git_repository *repo)
......
...@@ -83,7 +83,6 @@ struct git_repository { ...@@ -83,7 +83,6 @@ struct git_repository {
git_refdb *_refdb; git_refdb *_refdb;
git_config *_config; git_config *_config;
git_index *_index; git_index *_index;
git_mutex lock;
git_cache objects; git_cache objects;
git_attr_cache attrcache; git_attr_cache attrcache;
......
...@@ -68,6 +68,21 @@ GIT_INLINE(int) git_atomic_dec(git_atomic *a) ...@@ -68,6 +68,21 @@ GIT_INLINE(int) git_atomic_dec(git_atomic *a)
#endif #endif
} }
GIT_INLINE(void *) git___compare_and_swap(
volatile void **ptr, void *oldval, void *newval)
{
bool swapped;
#if defined(GIT_WIN32)
swapped = ((LONGLONG)oldval == InterlockedCompareExchange64(
(LONGLONG volatile *)ptr, (LONGLONG)newval, (LONGLONG)oldval));
#elif defined(__GNUC__)
swapped = (__sync_val_compare_and_swap(ptr, oldval, newval) == oldval);
#else
# error "Unsupported architecture for atomic operations"
#endif
return swapped ? oldval : newval;
}
#else #else
#define git_thread unsigned int #define git_thread unsigned int
...@@ -101,8 +116,34 @@ GIT_INLINE(int) git_atomic_dec(git_atomic *a) ...@@ -101,8 +116,34 @@ GIT_INLINE(int) git_atomic_dec(git_atomic *a)
return --a->val; return --a->val;
} }
GIT_INLINE(void *) git___compare_and_swap(
volatile void **ptr, void *oldval, void *newval)
{
if (*ptr == oldval)
*ptr = newval;
else
oldval = newval;
return oldval;
}
#endif #endif
/* Atomically replace oldval with newval
* @return oldval if it was replaced or newval if it was not
*/
#define git__compare_and_swap(P,O,N) \
git___compare_and_swap((volatile void **)P, O, N)
#define git__swap(ptr, val) git__compare_and_swap(&ptr, ptr, val)
extern int git_online_cpus(void); extern int git_online_cpus(void);
#if defined(GIT_THREADS) && defined(GIT_WIN32)
# define GIT_MEMORY_BARRIER MemoryBarrier()
#elif defined(GIT_THREADS)
# define GIT_MEMORY_BARRIER __sync_synchronize()
#else
# define GIT_MEMORY_BARRIER /* noop */
#endif
#endif /* INCLUDE_thread_utils_h__ */ #endif /* INCLUDE_thread_utils_h__ */
...@@ -313,23 +313,4 @@ int git__date_parse(git_time_t *out, const char *date); ...@@ -313,23 +313,4 @@ int git__date_parse(git_time_t *out, const char *date);
*/ */
extern size_t git__unescape(char *str); extern size_t git__unescape(char *str);
/*
* Swap a pointer with thread safety, returning old value.
*/
GIT_INLINE(void *) git__swap(git_mutex *lock, void **ptr_ptr, void *new_ptr)
{
void *old_ptr;
if (*ptr_ptr == new_ptr)
return NULL;
if (git_mutex_lock(lock) < 0)
return new_ptr;
old_ptr = *ptr_ptr;
*ptr_ptr = new_ptr;
git_mutex_unlock(lock);
return old_ptr;
}
#endif /* INCLUDE_util_h__ */ #endif /* INCLUDE_util_h__ */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment