mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-29 06:20:56 +00:00
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.
This commit is contained in:
parent
5360786885
commit
e976b56dda
@ -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;
|
||||||
|
132
src/repository.c
132
src/repository.c
@ -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)
|
|||||||
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)
|
|||||||
|
|
||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
|
|
||||||
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 {
|
|||||||
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)
|
|||||||
#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)
|
|||||||
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__ */
|
||||||
|
19
src/util.h
19
src/util.h
@ -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__ */
|
||||||
|
Loading…
Reference in New Issue
Block a user