#include <assert.h>
#include <apr_time.h>
#include "svn_pools.h"
#include "private/svn_atomic.h"
#include "private/svn_mutex.h"
#define SVN_ATOMIC_UNINITIALIZED 0
#define SVN_ATOMIC_START_INIT 1
#define SVN_ATOMIC_INIT_FAILED 2
#define SVN_ATOMIC_INITIALIZED 3
typedef struct init_baton_t init_baton_t;
typedef svn_boolean_t (*init_func_t)(init_baton_t *init_baton);
static svn_boolean_t
init_once(volatile svn_atomic_t *global_status,
init_func_t init_func, init_baton_t *init_baton)
{
svn_atomic_t status = svn_atomic_cas(global_status,
SVN_ATOMIC_START_INIT,
SVN_ATOMIC_UNINITIALIZED);
for (;;)
{
switch (status)
{
case SVN_ATOMIC_UNINITIALIZED:
{
const svn_boolean_t result = init_func(init_baton);
const svn_atomic_t init_state = (result
? SVN_ATOMIC_INITIALIZED
: SVN_ATOMIC_INIT_FAILED);
svn_atomic_cas(global_status, init_state,
SVN_ATOMIC_START_INIT);
return result;
}
case SVN_ATOMIC_START_INIT:
apr_sleep(APR_USEC_PER_SEC / 1000);
status = svn_atomic_cas(global_status,
SVN_ATOMIC_UNINITIALIZED,
SVN_ATOMIC_UNINITIALIZED);
continue;
case SVN_ATOMIC_INIT_FAILED:
return FALSE;
case SVN_ATOMIC_INITIALIZED:
return TRUE;
default:
abort();
}
}
}
struct init_baton_t
{
svn_atomic__err_init_func_t err_init_func;
svn_error_t *err;
apr_pool_t *pool;
svn_atomic__str_init_func_t str_init_func;
const char *errstr;
void *baton;
};
static svn_boolean_t err_init_func_wrapper(init_baton_t *init_baton)
{
init_baton->err = init_baton->err_init_func(init_baton->baton,
init_baton->pool);
return (init_baton->err == SVN_NO_ERROR);
}
svn_error_t *
svn_atomic__init_once(volatile svn_atomic_t *global_status,
svn_atomic__err_init_func_t err_init_func,
void *baton,
apr_pool_t* pool)
{
init_baton_t init_baton;
init_baton.err_init_func = err_init_func;
init_baton.err = NULL;
init_baton.pool = pool;
init_baton.baton = baton;
if (init_once(global_status, err_init_func_wrapper, &init_baton))
return SVN_NO_ERROR;
return svn_error_create(SVN_ERR_ATOMIC_INIT_FAILURE, init_baton.err,
"Couldn't perform atomic initialization");
}
static svn_boolean_t str_init_func_wrapper(init_baton_t *init_baton)
{
init_baton->errstr = init_baton->str_init_func(init_baton->baton);
return (init_baton->errstr == NULL);
}
const char *
svn_atomic__init_once_no_error(volatile svn_atomic_t *global_status,
svn_atomic__str_init_func_t str_init_func,
void *baton)
{
init_baton_t init_baton;
init_baton.str_init_func = str_init_func;
init_baton.errstr = NULL;
init_baton.baton = baton;
if (init_once(global_status, str_init_func_wrapper, &init_baton))
return NULL;
if (!init_baton.errstr)
return "Couldn't perform atomic initialization";
else
return init_baton.errstr;
}
static apr_uint64_t uniqiue_counter = 0;
static volatile svn_atomic_t counter_status = SVN_ATOMIC_UNINITIALIZED;
static svn_mutex__t *counter_mutex = NULL;
static svn_error_t *
init_unique_counter(void *null_baton,
apr_pool_t *null_pool)
{
SVN_ERR(svn_mutex__init(&counter_mutex, TRUE, svn_pool_create(NULL)));
return SVN_NO_ERROR;
}
static svn_error_t *
read_unique_counter(apr_uint64_t *value)
{
*value = ++uniqiue_counter;
return SVN_NO_ERROR;
}
svn_error_t *
svn_atomic__unique_counter(apr_uint64_t *value)
{
SVN_ERR(svn_atomic__init_once(&counter_status, init_unique_counter, NULL,
NULL));
SVN_MUTEX__WITH_LOCK(counter_mutex, read_unique_counter(value));
return SVN_NO_ERROR;
}