Unverified Commit 34e685c0 by Edward Thomson Committed by GitHub

Merge pull request #5747 from lhchavez/atomic-tests

Homogenize semantics for atomic-related functions
parents 3f8bf8be 74708a81
...@@ -127,7 +127,7 @@ static int attr_cache_remove(git_attr_cache *cache, git_attr_file *file) ...@@ -127,7 +127,7 @@ static int attr_cache_remove(git_attr_cache *cache, git_attr_file *file)
{ {
int error = 0; int error = 0;
git_attr_file_entry *entry; git_attr_file_entry *entry;
git_attr_file *old = NULL; git_attr_file *oldfile = NULL;
if (!file) if (!file)
return 0; return 0;
...@@ -136,13 +136,13 @@ static int attr_cache_remove(git_attr_cache *cache, git_attr_file *file) ...@@ -136,13 +136,13 @@ static int attr_cache_remove(git_attr_cache *cache, git_attr_file *file)
return error; return error;
if ((entry = attr_cache_lookup_entry(cache, file->entry->path)) != NULL) if ((entry = attr_cache_lookup_entry(cache, file->entry->path)) != NULL)
old = git_atomic_compare_and_swap(&entry->file[file->source.type], file, NULL); oldfile = git_atomic_compare_and_swap(&entry->file[file->source.type], file, NULL);
attr_cache_unlock(cache); attr_cache_unlock(cache);
if (old) { if (oldfile == file) {
GIT_REFCOUNT_OWN(old, NULL); GIT_REFCOUNT_OWN(file, NULL);
git_attr_file__free(old); git_attr_file__free(file);
} }
return error; return error;
...@@ -401,8 +401,7 @@ int git_attr_cache__init(git_repository *repo) ...@@ -401,8 +401,7 @@ int git_attr_cache__init(git_repository *repo)
(ret = git_pool_init(&cache->pool, 1)) < 0) (ret = git_pool_init(&cache->pool, 1)) < 0)
goto cancel; goto cancel;
cache = git_atomic_compare_and_swap(&repo->attrcache, NULL, cache); if (git_atomic_compare_and_swap(&repo->attrcache, NULL, cache) != NULL)
if (cache)
goto cancel; /* raced with another thread, free this but no error */ goto cancel; /* raced with another thread, free this but no error */
git_config_free(cfg); git_config_free(cfg);
......
...@@ -141,18 +141,23 @@ static int diff_driver_funcname(const git_config_entry *entry, void *payload) ...@@ -141,18 +141,23 @@ static int diff_driver_funcname(const git_config_entry *entry, void *payload)
static git_diff_driver_registry *git_repository_driver_registry( static git_diff_driver_registry *git_repository_driver_registry(
git_repository *repo) git_repository *repo)
{ {
if (!repo->diff_drivers) { git_diff_driver_registry *reg = git_atomic_load(repo->diff_drivers), *newreg;
git_diff_driver_registry *reg = git_diff_driver_registry_new(); if (reg)
reg = git_atomic_compare_and_swap(&repo->diff_drivers, NULL, reg); return reg;
if (reg != NULL) /* if we race, free losing allocation */ newreg = git_diff_driver_registry_new();
git_diff_driver_registry_free(reg); if (!newreg) {
}
if (!repo->diff_drivers)
git_error_set(GIT_ERROR_REPOSITORY, "unable to create diff driver registry"); git_error_set(GIT_ERROR_REPOSITORY, "unable to create diff driver registry");
return newreg;
return repo->diff_drivers; }
reg = git_atomic_compare_and_swap(&repo->diff_drivers, NULL, newreg);
if (!reg) {
reg = newreg;
} else {
/* if we race, free losing allocation */
git_diff_driver_registry_free(newreg);
}
return reg;
} }
static int diff_driver_alloc( static int diff_driver_alloc(
......
...@@ -1093,8 +1093,7 @@ int git_repository_config__weakptr(git_config **out, git_repository *repo) ...@@ -1093,8 +1093,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 = git_atomic_compare_and_swap(&repo->_config, NULL, config); if (git_atomic_compare_and_swap(&repo->_config, NULL, config) != NULL) {
if (config != NULL) {
GIT_REFCOUNT_OWN(config, NULL); GIT_REFCOUNT_OWN(config, NULL);
git_config_free(config); git_config_free(config);
} }
...@@ -1164,8 +1163,7 @@ int git_repository_odb__weakptr(git_odb **out, git_repository *repo) ...@@ -1164,8 +1163,7 @@ int git_repository_odb__weakptr(git_odb **out, git_repository *repo)
return error; return error;
} }
odb = git_atomic_compare_and_swap(&repo->_odb, NULL, odb); if (git_atomic_compare_and_swap(&repo->_odb, NULL, odb) != NULL) {
if (odb != NULL) {
GIT_REFCOUNT_OWN(odb, NULL); GIT_REFCOUNT_OWN(odb, NULL);
git_odb_free(odb); git_odb_free(odb);
} }
...@@ -1209,8 +1207,7 @@ int git_repository_refdb__weakptr(git_refdb **out, git_repository *repo) ...@@ -1209,8 +1207,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 = git_atomic_compare_and_swap(&repo->_refdb, NULL, refdb); if (git_atomic_compare_and_swap(&repo->_refdb, NULL, refdb) != NULL) {
if (refdb != NULL) {
GIT_REFCOUNT_OWN(refdb, NULL); GIT_REFCOUNT_OWN(refdb, NULL);
git_refdb_free(refdb); git_refdb_free(refdb);
} }
...@@ -1257,8 +1254,7 @@ int git_repository_index__weakptr(git_index **out, git_repository *repo) ...@@ -1257,8 +1254,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 = git_atomic_compare_and_swap(&repo->_index, NULL, index); if (git_atomic_compare_and_swap(&repo->_index, NULL, index) != NULL) {
if (index != NULL) {
GIT_REFCOUNT_OWN(index, NULL); GIT_REFCOUNT_OWN(index, NULL);
git_index_free(index); git_index_free(index);
} }
......
...@@ -74,6 +74,9 @@ typedef git_atomic32 git_atomic_ssize; ...@@ -74,6 +74,9 @@ typedef git_atomic32 git_atomic_ssize;
# include "unix/pthread.h" # include "unix/pthread.h"
#endif #endif
/*
* Atomically sets the contents of *a to be val.
*/
GIT_INLINE(void) git_atomic32_set(git_atomic32 *a, int val) GIT_INLINE(void) git_atomic32_set(git_atomic32 *a, int val)
{ {
#if defined(GIT_WIN32) #if defined(GIT_WIN32)
...@@ -87,6 +90,10 @@ GIT_INLINE(void) git_atomic32_set(git_atomic32 *a, int val) ...@@ -87,6 +90,10 @@ GIT_INLINE(void) git_atomic32_set(git_atomic32 *a, int val)
#endif #endif
} }
/*
* Atomically increments the contents of *a by 1, and stores the result back into *a.
* @return the result of the operation.
*/
GIT_INLINE(int) git_atomic32_inc(git_atomic32 *a) GIT_INLINE(int) git_atomic32_inc(git_atomic32 *a)
{ {
#if defined(GIT_WIN32) #if defined(GIT_WIN32)
...@@ -100,10 +107,14 @@ GIT_INLINE(int) git_atomic32_inc(git_atomic32 *a) ...@@ -100,10 +107,14 @@ GIT_INLINE(int) git_atomic32_inc(git_atomic32 *a)
#endif #endif
} }
/*
* Atomically adds the contents of *a and addend, and stores the result back into *a.
* @return the result of the operation.
*/
GIT_INLINE(int) git_atomic32_add(git_atomic32 *a, int32_t addend) GIT_INLINE(int) git_atomic32_add(git_atomic32 *a, int32_t addend)
{ {
#if defined(GIT_WIN32) #if defined(GIT_WIN32)
return InterlockedExchangeAdd(&a->val, addend); return InterlockedAdd(&a->val, addend);
#elif defined(GIT_BUILTIN_ATOMIC) #elif defined(GIT_BUILTIN_ATOMIC)
return __atomic_add_fetch(&a->val, addend, __ATOMIC_SEQ_CST); return __atomic_add_fetch(&a->val, addend, __ATOMIC_SEQ_CST);
#elif defined(GIT_BUILTIN_SYNC) #elif defined(GIT_BUILTIN_SYNC)
...@@ -113,6 +124,10 @@ GIT_INLINE(int) git_atomic32_add(git_atomic32 *a, int32_t addend) ...@@ -113,6 +124,10 @@ GIT_INLINE(int) git_atomic32_add(git_atomic32 *a, int32_t addend)
#endif #endif
} }
/*
* Atomically decrements the contents of *a by 1, and stores the result back into *a.
* @return the result of the operation.
*/
GIT_INLINE(int) git_atomic32_dec(git_atomic32 *a) GIT_INLINE(int) git_atomic32_dec(git_atomic32 *a)
{ {
#if defined(GIT_WIN32) #if defined(GIT_WIN32)
...@@ -126,6 +141,10 @@ GIT_INLINE(int) git_atomic32_dec(git_atomic32 *a) ...@@ -126,6 +141,10 @@ GIT_INLINE(int) git_atomic32_dec(git_atomic32 *a)
#endif #endif
} }
/*
* Atomically gets the contents of *a.
* @return the contents of *a.
*/
GIT_INLINE(int) git_atomic32_get(git_atomic32 *a) GIT_INLINE(int) git_atomic32_get(git_atomic32 *a)
{ {
#if defined(GIT_WIN32) #if defined(GIT_WIN32)
...@@ -143,16 +162,13 @@ GIT_INLINE(void *) git_atomic__compare_and_swap( ...@@ -143,16 +162,13 @@ GIT_INLINE(void *) git_atomic__compare_and_swap(
void * volatile *ptr, void *oldval, void *newval) void * volatile *ptr, void *oldval, void *newval)
{ {
#if defined(GIT_WIN32) #if defined(GIT_WIN32)
volatile void *foundval; return InterlockedCompareExchangePointer((volatile PVOID *)ptr, newval, oldval);
foundval = InterlockedCompareExchangePointer((volatile PVOID *)ptr, newval, oldval);
return (foundval == oldval) ? oldval : newval;
#elif defined(GIT_BUILTIN_ATOMIC) #elif defined(GIT_BUILTIN_ATOMIC)
bool success = __atomic_compare_exchange(ptr, &oldval, &newval, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); void *foundval = oldval;
return success ? oldval : newval; __atomic_compare_exchange(ptr, &foundval, &newval, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
return foundval;
#elif defined(GIT_BUILTIN_SYNC) #elif defined(GIT_BUILTIN_SYNC)
volatile void *foundval; return __sync_val_compare_and_swap(ptr, oldval, newval);
foundval = __sync_val_compare_and_swap(ptr, oldval, newval);
return (foundval == oldval) ? oldval : newval;
#else #else
# error "Unsupported architecture for atomic operations" # error "Unsupported architecture for atomic operations"
#endif #endif
...@@ -164,11 +180,11 @@ GIT_INLINE(volatile void *) git_atomic__swap( ...@@ -164,11 +180,11 @@ GIT_INLINE(volatile void *) git_atomic__swap(
#if defined(GIT_WIN32) #if defined(GIT_WIN32)
return InterlockedExchangePointer(ptr, newval); return InterlockedExchangePointer(ptr, newval);
#elif defined(GIT_BUILTIN_ATOMIC) #elif defined(GIT_BUILTIN_ATOMIC)
void * volatile foundval; void * volatile foundval = NULL;
__atomic_exchange(ptr, &newval, &foundval, __ATOMIC_SEQ_CST); __atomic_exchange(ptr, &newval, &foundval, __ATOMIC_SEQ_CST);
return foundval; return foundval;
#elif defined(GIT_BUILTIN_SYNC) #elif defined(GIT_BUILTIN_SYNC)
return __sync_lock_test_and_set(ptr, newval); return (volatile void *)__sync_lock_test_and_set(ptr, newval);
#else #else
# error "Unsupported architecture for atomic operations" # error "Unsupported architecture for atomic operations"
#endif #endif
...@@ -178,9 +194,7 @@ GIT_INLINE(volatile void *) git_atomic__load(void * volatile *ptr) ...@@ -178,9 +194,7 @@ GIT_INLINE(volatile void *) git_atomic__load(void * volatile *ptr)
{ {
#if defined(GIT_WIN32) #if defined(GIT_WIN32)
void *newval = NULL, *oldval = NULL; void *newval = NULL, *oldval = NULL;
volatile void *foundval = NULL; return (volatile void *)InterlockedCompareExchangePointer((volatile PVOID *)ptr, newval, oldval);
foundval = InterlockedCompareExchangePointer((volatile PVOID *)ptr, newval, oldval);
return foundval;
#elif defined(GIT_BUILTIN_ATOMIC) #elif defined(GIT_BUILTIN_ATOMIC)
return (volatile void *)__atomic_load_n(ptr, __ATOMIC_SEQ_CST); return (volatile void *)__atomic_load_n(ptr, __ATOMIC_SEQ_CST);
#elif defined(GIT_BUILTIN_SYNC) #elif defined(GIT_BUILTIN_SYNC)
...@@ -192,10 +206,14 @@ GIT_INLINE(volatile void *) git_atomic__load(void * volatile *ptr) ...@@ -192,10 +206,14 @@ GIT_INLINE(volatile void *) git_atomic__load(void * volatile *ptr)
#ifdef GIT_ARCH_64 #ifdef GIT_ARCH_64
/*
* Atomically adds the contents of *a and addend, and stores the result back into *a.
* @return the result of the operation.
*/
GIT_INLINE(int64_t) git_atomic64_add(git_atomic64 *a, int64_t addend) GIT_INLINE(int64_t) git_atomic64_add(git_atomic64 *a, int64_t addend)
{ {
#if defined(GIT_WIN32) #if defined(GIT_WIN32)
return InterlockedExchangeAdd64(&a->val, addend); return InterlockedAdd64(&a->val, addend);
#elif defined(GIT_BUILTIN_ATOMIC) #elif defined(GIT_BUILTIN_ATOMIC)
return __atomic_add_fetch(&a->val, addend, __ATOMIC_SEQ_CST); return __atomic_add_fetch(&a->val, addend, __ATOMIC_SEQ_CST);
#elif defined(GIT_BUILTIN_SYNC) #elif defined(GIT_BUILTIN_SYNC)
...@@ -205,6 +223,9 @@ GIT_INLINE(int64_t) git_atomic64_add(git_atomic64 *a, int64_t addend) ...@@ -205,6 +223,9 @@ GIT_INLINE(int64_t) git_atomic64_add(git_atomic64 *a, int64_t addend)
#endif #endif
} }
/*
* Atomically sets the contents of *a to be val.
*/
GIT_INLINE(void) git_atomic64_set(git_atomic64 *a, int64_t val) GIT_INLINE(void) git_atomic64_set(git_atomic64 *a, int64_t val)
{ {
#if defined(GIT_WIN32) #if defined(GIT_WIN32)
...@@ -218,6 +239,10 @@ GIT_INLINE(void) git_atomic64_set(git_atomic64 *a, int64_t val) ...@@ -218,6 +239,10 @@ GIT_INLINE(void) git_atomic64_set(git_atomic64 *a, int64_t val)
#endif #endif
} }
/*
* Atomically gets the contents of *a.
* @return the contents of *a.
*/
GIT_INLINE(int64_t) git_atomic64_get(git_atomic64 *a) GIT_INLINE(int64_t) git_atomic64_get(git_atomic64 *a)
{ {
#if defined(GIT_WIN32) #if defined(GIT_WIN32)
...@@ -297,11 +322,10 @@ GIT_INLINE(int) git_atomic32_get(git_atomic32 *a) ...@@ -297,11 +322,10 @@ GIT_INLINE(int) git_atomic32_get(git_atomic32 *a)
GIT_INLINE(void *) git_atomic__compare_and_swap( GIT_INLINE(void *) git_atomic__compare_and_swap(
void * volatile *ptr, void *oldval, void *newval) void * volatile *ptr, void *oldval, void *newval)
{ {
if (*ptr == oldval) void *foundval = *ptr;
if (foundval == oldval)
*ptr = newval; *ptr = newval;
else return foundval;
oldval = newval;
return oldval;
} }
GIT_INLINE(volatile void *) git_atomic__swap( GIT_INLINE(volatile void *) git_atomic__swap(
...@@ -339,17 +363,50 @@ GIT_INLINE(int64_t) git_atomic64_get(git_atomic64 *a) ...@@ -339,17 +363,50 @@ GIT_INLINE(int64_t) git_atomic64_get(git_atomic64 *a)
#endif #endif
/* Atomically replace oldval with newval /*
* @return oldval if it was replaced or newval if it was not * Atomically replace the contents of *ptr (if they are equal to oldval) with
* newval. ptr must point to a pointer or a value that is the same size as a
* pointer. This is semantically compatible with:
*
* #define git_atomic_compare_and_swap(ptr, oldval, newval) \
* ({ \
* void *foundval = *ptr; \
* if (foundval == oldval) \
* *ptr = newval; \
* foundval; \
* })
*
* @return the original contents of *ptr.
*/ */
#define git_atomic_compare_and_swap(P,O,N) \ #define git_atomic_compare_and_swap(ptr, oldval, newval) \
git_atomic__compare_and_swap((void * volatile *)P, O, N) git_atomic__compare_and_swap((void * volatile *)ptr, oldval, newval)
#define git_atomic_swap(ptr, val) \ /*
(void *)git_atomic__swap((void * volatile *)&ptr, val) * Atomically replace the contents of v with newval. v must be the same size as
* a pointer. This is semantically compatible with:
*
* #define git_atomic_swap(v, newval) \
* ({ \
* volatile void *old = v; \
* v = newval; \
* old; \
* })
*
* @return the original contents of v.
*/
#define git_atomic_swap(v, newval) \
(void *)git_atomic__swap((void * volatile *)&(v), newval)
#define git_atomic_load(ptr) \ /*
(void *)git_atomic__load((void * volatile *)&ptr) * Atomically reads the contents of v. v must be the same size as a pointer.
* This is semantically compatible with:
*
* #define git_atomic_load(v) v
*
* @return the contents of v.
*/
#define git_atomic_load(v) \
(void *)git_atomic__load((void * volatile *)&(v))
#if defined(GIT_THREADS) #if defined(GIT_THREADS)
......
#include "clar_libgit2.h"
void test_threads_atomic__atomic32_set(void)
{
git_atomic32 v = {0};
git_atomic32_set(&v, 1);
cl_assert_equal_i(v.val, 1);
}
void test_threads_atomic__atomic32_get(void)
{
git_atomic32 v = {1};
cl_assert_equal_i(git_atomic32_get(&v), 1);
}
void test_threads_atomic__atomic32_inc(void)
{
git_atomic32 v = {0};
cl_assert_equal_i(git_atomic32_inc(&v), 1);
cl_assert_equal_i(v.val, 1);
}
void test_threads_atomic__atomic32_add(void)
{
git_atomic32 v = {0};
cl_assert_equal_i(git_atomic32_add(&v, 1), 1);
cl_assert_equal_i(v.val, 1);
}
void test_threads_atomic__atomic32_dec(void)
{
git_atomic32 v = {1};
cl_assert_equal_i(git_atomic32_dec(&v), 0);
cl_assert_equal_i(v.val, 0);
}
void test_threads_atomic__atomic64_set(void)
{
#ifndef GIT_ARCH_64
cl_skip();
#else
git_atomic64 v = {0};
git_atomic64_set(&v, 1);
cl_assert_equal_i(v.val, 1);
#endif
}
void test_threads_atomic__atomic64_get(void)
{
#ifndef GIT_ARCH_64
cl_skip();
#else
git_atomic64 v = {1};
cl_assert_equal_i(git_atomic64_get(&v), 1);
#endif
}
void test_threads_atomic__atomic64_add(void)
{
#ifndef GIT_ARCH_64
cl_skip();
#else
git_atomic64 v = {0};
cl_assert_equal_i(git_atomic64_add(&v, 1), 1);
cl_assert_equal_i(v.val, 1);
#endif
}
void test_threads_atomic__cas_pointer(void)
{
int *value = NULL;
int newvalue1 = 1, newvalue2 = 2;
/* value is updated */
cl_assert_equal_p(git_atomic_compare_and_swap(&value, NULL, &newvalue1), NULL);
cl_assert_equal_p(value, &newvalue1);
/* value is not updated */
cl_assert_equal_p(git_atomic_compare_and_swap(&value, NULL, &newvalue2), &newvalue1);
cl_assert_equal_p(value, &newvalue1);
}
void test_threads_atomic__cas_intptr(void)
{
intptr_t value = 0;
intptr_t oldvalue;
intptr_t newvalue;
/* value is updated */
oldvalue = 0;
newvalue = 1;
cl_assert_equal_i((intptr_t)git_atomic_compare_and_swap(&value, (void *)oldvalue, (void *)newvalue), 0);
cl_assert_equal_i(value, 1);
/* value is not updated */
oldvalue = 0;
newvalue = 2;
cl_assert_equal_i((intptr_t)git_atomic_compare_and_swap(&value, (void *)oldvalue, (void *)newvalue), 1);
cl_assert_equal_i(value, 1);
}
void test_threads_atomic__swap(void)
{
int *value = NULL;
int newvalue = 1;
cl_assert_equal_p(git_atomic_swap(value, &newvalue), NULL);
cl_assert_equal_p(value, &newvalue);
cl_assert_equal_p(git_atomic_swap(value, NULL), &newvalue);
cl_assert_equal_p(value, NULL);
}
void test_threads_atomic__load_ptr(void)
{
int value = 1;
int *ptr = &value;
cl_assert_equal_p(git_atomic_load(ptr), &value);
}
void test_threads_atomic__load_intptr(void)
{
intptr_t value = 1;
cl_assert_equal_i((intptr_t)git_atomic_load(value), 1);
}
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