Commit 4b99b8f5 by Carlos Martín Nieto

config: refcount the values map

This is mostly groundwork to let us re-use the map in the snapshots.
parent 8c1f4ab4
...@@ -87,8 +87,15 @@ struct reader { ...@@ -87,8 +87,15 @@ struct reader {
}; };
typedef struct { typedef struct {
git_config_backend parent; git_atomic refcount;
git_strmap *values; git_strmap *values;
} refcounted_strmap;
typedef struct {
git_config_backend parent;
/* mutex to coordinate accessing the values */
git_mutex values_mutex;
refcounted_strmap *values;
int readonly; int readonly;
} diskfile_header; } diskfile_header;
...@@ -139,18 +146,6 @@ static void cvar_free(cvar_t *var) ...@@ -139,18 +146,6 @@ static void cvar_free(cvar_t *var)
git__free(var); git__free(var);
} }
static int cvar_length(cvar_t *var)
{
int length = 0;
while (var) {
length++;
var = var->next;
}
return length;
}
int git_config_file_normalize_section(char *start, char *end) int git_config_file_normalize_section(char *start, char *end)
{ {
char *scan; char *scan;
...@@ -215,6 +210,58 @@ static void free_vars(git_strmap *values) ...@@ -215,6 +210,58 @@ static void free_vars(git_strmap *values)
git_strmap_free(values); git_strmap_free(values);
} }
static void refcounted_strmap_free(refcounted_strmap *map)
{
if (!map)
return;
if (git_atomic_dec(&map->refcount) != 0)
return;
free_vars(map->values);
git__free(map);
}
/**
* Take the current values map from the backend and increase its
* refcount. This is its own function to make sure we use the mutex to
* avoid the map pointer from changing under us.
*/
static refcounted_strmap *refcounted_strmap_take(diskfile_header *h)
{
refcounted_strmap *map;
git_mutex_lock(&h->values_mutex);
map = h->values;
git_atomic_inc(&map->refcount);
git_mutex_unlock(&h->values_mutex);
return map;
}
static int refcounted_strmap_alloc(refcounted_strmap **out)
{
refcounted_strmap *map;
int error;
map = git__calloc(1, sizeof(refcounted_strmap));
if (!map) {
giterr_set_oom();
return -1;
}
git_atomic_set(&map->refcount, 1);
if ((error = git_strmap_alloc(&map->values)) < 0) {
git__free(map);
return error;
}
*out = map;
return error;
}
static int config_open(git_config_backend *cfg, git_config_level_t level) static int config_open(git_config_backend *cfg, git_config_level_t level)
{ {
int res; int res;
...@@ -223,13 +270,14 @@ static int config_open(git_config_backend *cfg, git_config_level_t level) ...@@ -223,13 +270,14 @@ static int config_open(git_config_backend *cfg, git_config_level_t level)
b->level = level; b->level = level;
if ((res = git_strmap_alloc(&b->header.values)) < 0) if ((res = refcounted_strmap_alloc(&b->header.values)) < 0)
return res; return res;
git_mutex_init(&b->header.values_mutex);
git_array_init(b->readers); git_array_init(b->readers);
reader = git_array_alloc(b->readers); reader = git_array_alloc(b->readers);
if (!reader) { if (!reader) {
git_strmap_free(b->header.values); refcounted_strmap_free(b->header.values);
return -1; return -1;
} }
memset(reader, 0, sizeof(struct reader)); memset(reader, 0, sizeof(struct reader));
...@@ -245,8 +293,8 @@ static int config_open(git_config_backend *cfg, git_config_level_t level) ...@@ -245,8 +293,8 @@ static int config_open(git_config_backend *cfg, git_config_level_t level)
if (res == GIT_ENOTFOUND) if (res == GIT_ENOTFOUND)
return 0; return 0;
if (res < 0 || (res = config_parse(b->header.values, b, reader, level, 0)) < 0) { if (res < 0 || (res = config_parse(b->header.values->values, b, reader, level, 0)) < 0) {
free_vars(b->header.values); refcounted_strmap_free(b->header.values);
b->header.values = NULL; b->header.values = NULL;
} }
...@@ -259,23 +307,29 @@ static int config_open(git_config_backend *cfg, git_config_level_t level) ...@@ -259,23 +307,29 @@ static int config_open(git_config_backend *cfg, git_config_level_t level)
/* The meat of the refresh, as we want to use it in different places */ /* The meat of the refresh, as we want to use it in different places */
static int config__refresh(git_config_backend *cfg) static int config__refresh(git_config_backend *cfg)
{ {
git_strmap *values = NULL; refcounted_strmap *values = NULL, *tmp;
diskfile_backend *b = (diskfile_backend *)cfg; diskfile_backend *b = (diskfile_backend *)cfg;
struct reader *reader = NULL; struct reader *reader = NULL;
int error = 0; int error = 0;
if ((error = git_strmap_alloc(&values)) < 0) if ((error = refcounted_strmap_alloc(&values)) < 0)
goto out; goto out;
reader = git_array_get(b->readers, git_array_size(b->readers) - 1); reader = git_array_get(b->readers, git_array_size(b->readers) - 1);
if ((error = config_parse(values, b, reader, b->level, 0)) < 0) if ((error = config_parse(values->values, b, reader, b->level, 0)) < 0)
goto out; goto out;
values = git__swap(b->header.values, values); git_mutex_lock(&b->header.values_mutex);
tmp = b->header.values;
b->header.values = values;
values = tmp;
git_mutex_unlock(&b->header.values_mutex);
out: out:
free_vars(values); refcounted_strmap_free(values);
git_buf_free(&reader->buffer); git_buf_free(&reader->buffer);
return error; return error;
} }
...@@ -321,7 +375,7 @@ static void backend_free(git_config_backend *_backend) ...@@ -321,7 +375,7 @@ static void backend_free(git_config_backend *_backend)
git_array_clear(backend->readers); git_array_clear(backend->readers);
git__free(backend->file_path); git__free(backend->file_path);
free_vars(backend->header.values); refcounted_strmap_free(backend->header.values);
git__free(backend); git__free(backend);
} }
...@@ -338,7 +392,7 @@ static int config_iterator_next( ...@@ -338,7 +392,7 @@ static int config_iterator_next(
{ {
git_config_file_iter *it = (git_config_file_iter *) iter; git_config_file_iter *it = (git_config_file_iter *) iter;
diskfile_header *h = (diskfile_header *) it->parent.backend; diskfile_header *h = (diskfile_header *) it->parent.backend;
git_strmap *values = h->values; git_strmap *values = h->values->values;
int err = 0; int err = 0;
cvar_t * var; cvar_t * var;
...@@ -397,7 +451,8 @@ static int config_iterator_new( ...@@ -397,7 +451,8 @@ static int config_iterator_new(
static int config_set(git_config_backend *cfg, const char *name, const char *value) static int config_set(git_config_backend *cfg, const char *name, const char *value)
{ {
diskfile_backend *b = (diskfile_backend *)cfg; diskfile_backend *b = (diskfile_backend *)cfg;
git_strmap *values = b->header.values; refcounted_strmap *map;
git_strmap *values;
char *key, *esc_value = NULL; char *key, *esc_value = NULL;
khiter_t pos; khiter_t pos;
int rval, ret; int rval, ret;
...@@ -405,6 +460,9 @@ static int config_set(git_config_backend *cfg, const char *name, const char *val ...@@ -405,6 +460,9 @@ static int config_set(git_config_backend *cfg, const char *name, const char *val
if ((rval = git_config__normalize_name(name, &key)) < 0) if ((rval = git_config__normalize_name(name, &key)) < 0)
return rval; return rval;
map = refcounted_strmap_take(&b->header);
values = map->values;
/* /*
* Try to find it in the existing values and update it if it * Try to find it in the existing values and update it if it
* only has one value. * only has one value.
...@@ -414,17 +472,17 @@ static int config_set(git_config_backend *cfg, const char *name, const char *val ...@@ -414,17 +472,17 @@ static int config_set(git_config_backend *cfg, const char *name, const char *val
cvar_t *existing = git_strmap_value_at(values, pos); cvar_t *existing = git_strmap_value_at(values, pos);
if (existing->next != NULL) { if (existing->next != NULL) {
git__free(key);
giterr_set(GITERR_CONFIG, "Multivar incompatible with simple set"); giterr_set(GITERR_CONFIG, "Multivar incompatible with simple set");
return -1; ret = -1;
goto out;
} }
/* don't update if old and new values already match */ /* don't update if old and new values already match */
if ((!existing->entry->value && !value) || if ((!existing->entry->value && !value) ||
(existing->entry->value && value && (existing->entry->value && value &&
!strcmp(existing->entry->value, value))) { !strcmp(existing->entry->value, value))) {
git__free(key); ret = 0;
return 0; goto out;
} }
} }
...@@ -441,6 +499,7 @@ static int config_set(git_config_backend *cfg, const char *name, const char *val ...@@ -441,6 +499,7 @@ static int config_set(git_config_backend *cfg, const char *name, const char *val
ret = config_refresh(cfg); ret = config_refresh(cfg);
out: out:
refcounted_strmap_free(map);
git__free(esc_value); git__free(esc_value);
git__free(key); git__free(key);
return ret; return ret;
...@@ -452,6 +511,7 @@ out: ...@@ -452,6 +511,7 @@ out:
static int config_get(git_config_backend *cfg, const char *key, const git_config_entry **out) static int config_get(git_config_backend *cfg, const char *key, const git_config_entry **out)
{ {
diskfile_header *h = (diskfile_header *)cfg; diskfile_header *h = (diskfile_header *)cfg;
refcounted_strmap *map;
git_strmap *values; git_strmap *values;
khiter_t pos; khiter_t pos;
cvar_t *var; cvar_t *var;
...@@ -460,17 +520,22 @@ static int config_get(git_config_backend *cfg, const char *key, const git_config ...@@ -460,17 +520,22 @@ static int config_get(git_config_backend *cfg, const char *key, const git_config
if (!h->readonly && ((error = config_refresh(cfg)) < 0)) if (!h->readonly && ((error = config_refresh(cfg)) < 0))
return error; return error;
values = h->values; map = refcounted_strmap_take(h);
values = map->values;
pos = git_strmap_lookup_index(values, key); pos = git_strmap_lookup_index(values, key);
/* no error message; the config system will write one */ /* no error message; the config system will write one */
if (!git_strmap_valid_index(values, pos)) if (!git_strmap_valid_index(values, pos)) {
refcounted_strmap_free(map);
return GIT_ENOTFOUND; return GIT_ENOTFOUND;
}
var = git_strmap_value_at(values, pos); var = git_strmap_value_at(values, pos);
while (var->next) while (var->next)
var = var->next; var = var->next;
refcounted_strmap_free(map);
*out = var->entry; *out = var->entry;
return 0; return 0;
} }
...@@ -479,7 +544,8 @@ static int config_set_multivar( ...@@ -479,7 +544,8 @@ static int config_set_multivar(
git_config_backend *cfg, const char *name, const char *regexp, const char *value) git_config_backend *cfg, const char *name, const char *regexp, const char *value)
{ {
diskfile_backend *b = (diskfile_backend *)cfg; diskfile_backend *b = (diskfile_backend *)cfg;
git_strmap *values = b->header.values; refcounted_strmap *map;
git_strmap *values;
char *key; char *key;
regex_t preg; regex_t preg;
int result; int result;
...@@ -490,20 +556,23 @@ static int config_set_multivar( ...@@ -490,20 +556,23 @@ static int config_set_multivar(
if ((result = git_config__normalize_name(name, &key)) < 0) if ((result = git_config__normalize_name(name, &key)) < 0)
return result; return result;
map = refcounted_strmap_take(&b->header);
values = b->header.values->values;
pos = git_strmap_lookup_index(values, key); pos = git_strmap_lookup_index(values, key);
if (!git_strmap_valid_index(values, pos)) { if (!git_strmap_valid_index(values, pos)) {
/* If we don't have it, behave like a normal set */ /* If we don't have it, behave like a normal set */
result = config_set(cfg, name, value); result = config_set(cfg, name, value);
refcounted_strmap_free(map);
git__free(key); git__free(key);
return result; return result;
} }
result = regcomp(&preg, regexp, REG_EXTENDED); result = regcomp(&preg, regexp, REG_EXTENDED);
if (result < 0) { if (result < 0) {
git__free(key);
giterr_set_regex(&preg, result); giterr_set_regex(&preg, result);
regfree(&preg); result = -1;
return -1; goto out;
} }
/* If we do have it, set call config_write() and reload */ /* If we do have it, set call config_write() and reload */
...@@ -513,6 +582,7 @@ static int config_set_multivar( ...@@ -513,6 +582,7 @@ static int config_set_multivar(
result = config_refresh(cfg); result = config_refresh(cfg);
out: out:
refcounted_strmap_free(map);
git__free(key); git__free(key);
regfree(&preg); regfree(&preg);
...@@ -523,7 +593,7 @@ static int config_delete(git_config_backend *cfg, const char *name) ...@@ -523,7 +593,7 @@ static int config_delete(git_config_backend *cfg, const char *name)
{ {
cvar_t *var; cvar_t *var;
diskfile_backend *b = (diskfile_backend *)cfg; diskfile_backend *b = (diskfile_backend *)cfg;
git_strmap *values = b->header.values; refcounted_strmap *map; git_strmap *values;
char *key; char *key;
int result; int result;
khiter_t pos; khiter_t pos;
...@@ -531,15 +601,20 @@ static int config_delete(git_config_backend *cfg, const char *name) ...@@ -531,15 +601,20 @@ static int config_delete(git_config_backend *cfg, const char *name)
if ((result = git_config__normalize_name(name, &key)) < 0) if ((result = git_config__normalize_name(name, &key)) < 0)
return result; return result;
map = refcounted_strmap_take(&b->header);
values = b->header.values->values;
pos = git_strmap_lookup_index(values, key); pos = git_strmap_lookup_index(values, key);
git__free(key); git__free(key);
if (!git_strmap_valid_index(values, pos)) { if (!git_strmap_valid_index(values, pos)) {
refcounted_strmap_free(map);
giterr_set(GITERR_CONFIG, "Could not find key '%s' to delete", name); giterr_set(GITERR_CONFIG, "Could not find key '%s' to delete", name);
return GIT_ENOTFOUND; return GIT_ENOTFOUND;
} }
var = git_strmap_value_at(values, pos); var = git_strmap_value_at(values, pos);
refcounted_strmap_free(map);
if (var->next != NULL) { if (var->next != NULL) {
giterr_set(GITERR_CONFIG, "Cannot delete multivar with a single delete"); giterr_set(GITERR_CONFIG, "Cannot delete multivar with a single delete");
...@@ -555,7 +630,8 @@ static int config_delete(git_config_backend *cfg, const char *name) ...@@ -555,7 +630,8 @@ static int config_delete(git_config_backend *cfg, const char *name)
static int config_delete_multivar(git_config_backend *cfg, const char *name, const char *regexp) static int config_delete_multivar(git_config_backend *cfg, const char *name, const char *regexp)
{ {
diskfile_backend *b = (diskfile_backend *)cfg; diskfile_backend *b = (diskfile_backend *)cfg;
git_strmap *values = b->header.values; refcounted_strmap *map;
git_strmap *values;
char *key; char *key;
regex_t preg; regex_t preg;
int result; int result;
...@@ -564,14 +640,20 @@ static int config_delete_multivar(git_config_backend *cfg, const char *name, con ...@@ -564,14 +640,20 @@ static int config_delete_multivar(git_config_backend *cfg, const char *name, con
if ((result = git_config__normalize_name(name, &key)) < 0) if ((result = git_config__normalize_name(name, &key)) < 0)
return result; return result;
map = refcounted_strmap_take(&b->header);
values = b->header.values->values;
pos = git_strmap_lookup_index(values, key); pos = git_strmap_lookup_index(values, key);
if (!git_strmap_valid_index(values, pos)) { if (!git_strmap_valid_index(values, pos)) {
refcounted_strmap_free(map);
git__free(key); git__free(key);
giterr_set(GITERR_CONFIG, "Could not find key '%s' to delete", name); giterr_set(GITERR_CONFIG, "Could not find key '%s' to delete", name);
return GIT_ENOTFOUND; return GIT_ENOTFOUND;
} }
refcounted_strmap_free(map);
result = regcomp(&preg, regexp, REG_EXTENDED); result = regcomp(&preg, regexp, REG_EXTENDED);
if (result < 0) { if (result < 0) {
giterr_set_regex(&preg, result); giterr_set_regex(&preg, result);
...@@ -676,7 +758,7 @@ static void backend_readonly_free(git_config_backend *_backend) ...@@ -676,7 +758,7 @@ static void backend_readonly_free(git_config_backend *_backend)
if (backend == NULL) if (backend == NULL)
return; return;
free_vars(backend->header.values); refcounted_strmap_free(backend->header.values);
git__free(backend); git__free(backend);
} }
...@@ -702,8 +784,8 @@ static int config_readonly_open(git_config_backend *cfg, git_config_level_t leve ...@@ -702,8 +784,8 @@ static int config_readonly_open(git_config_backend *cfg, git_config_level_t leve
{ {
diskfile_readonly_backend *b = (diskfile_readonly_backend *) cfg; diskfile_readonly_backend *b = (diskfile_readonly_backend *) cfg;
diskfile_backend *src = b->snapshot_from; diskfile_backend *src = b->snapshot_from;
git_strmap *src_values = src->header.values; refcounted_strmap *src_map;
git_strmap *values; git_strmap *src_values, *values;
git_strmap_iter i; git_strmap_iter i;
cvar_t *src_var; cvar_t *src_var;
int error; int error;
...@@ -711,10 +793,12 @@ static int config_readonly_open(git_config_backend *cfg, git_config_level_t leve ...@@ -711,10 +793,12 @@ static int config_readonly_open(git_config_backend *cfg, git_config_level_t leve
/* We're just copying data, don't care about the level */ /* We're just copying data, don't care about the level */
GIT_UNUSED(level); GIT_UNUSED(level);
if ((error = git_strmap_alloc(&b->header.values)) < 0) if ((error = refcounted_strmap_alloc(&b->header.values)) < 0)
return error; return error;
values = b->header.values; src_map = refcounted_strmap_take(&src->header);
src_values = src->header.values->values;
values = b->header.values->values;
i = git_strmap_begin(src_values); i = git_strmap_begin(src_values);
while ((error = git_strmap_next((void **) &src_var, &i, src_values)) == 0) { while ((error = git_strmap_next((void **) &src_var, &i, src_values)) == 0) {
...@@ -725,8 +809,11 @@ static int config_readonly_open(git_config_backend *cfg, git_config_level_t leve ...@@ -725,8 +809,11 @@ static int config_readonly_open(git_config_backend *cfg, git_config_level_t leve
var = git__calloc(1, sizeof(cvar_t)); var = git__calloc(1, sizeof(cvar_t));
GITERR_CHECK_ALLOC(var); GITERR_CHECK_ALLOC(var);
if (config_entry_dup(&entry, src_var->entry) < 0) if (config_entry_dup(&entry, src_var->entry) < 0) {
refcounted_strmap_free(b->header.values);
refcounted_strmap_free(src_map);
return -1; return -1;
}
var->entry = entry; var->entry = entry;
...@@ -738,6 +825,7 @@ static int config_readonly_open(git_config_backend *cfg, git_config_level_t leve ...@@ -738,6 +825,7 @@ static int config_readonly_open(git_config_backend *cfg, git_config_level_t leve
if (error == GIT_ITEROVER) if (error == GIT_ITEROVER)
error = 0; error = 0;
refcounted_strmap_free(src_map);
return error; return error;
} }
......
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