Commit fe372740 by Russell Belfer

Rewrite refdb_fs using git_sortedcache object

This adds thread safety to the refdb_fs by using the new
git_sortedcache object and also by relaxing the handling of some
filesystem errors where the fs may be changed out from under us.

This also adds some new threading tests that hammer on the refdb.
parent 24c71f14
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include "refdb.h" #include "refdb.h"
#include "refdb_fs.h" #include "refdb_fs.h"
#include "iterator.h" #include "iterator.h"
#include "sortedcache.h"
#include <git2/tag.h> #include <git2/tag.h>
#include <git2/object.h> #include <git2/object.h>
...@@ -53,243 +54,149 @@ typedef struct refdb_fs_backend { ...@@ -53,243 +54,149 @@ typedef struct refdb_fs_backend {
git_repository *repo; git_repository *repo;
char *path; char *path;
git_refcache refcache; git_sortedcache *refcache;
int peeling_mode; int peeling_mode;
} refdb_fs_backend; } refdb_fs_backend;
static int reference_read( static int packref_cmp(const void *a_, const void *b_)
git_buf *file_content,
time_t *mtime,
const char *repo_path,
const char *ref_name,
int *updated)
{ {
git_buf path = GIT_BUF_INIT; const struct packref *a = a_, *b = b_;
int result; return strcmp(a->name, b->name);
assert(file_content && repo_path && ref_name);
/* Determine the full path of the file */
if (git_buf_joinpath(&path, repo_path, ref_name) < 0)
return -1;
result = git_futils_readbuffer_updated(file_content, path.ptr, mtime, NULL, updated);
git_buf_free(&path);
return result;
}
static int packed_parse_oid(
struct packref **ref_out,
const char **buffer_out,
const char *buffer_end)
{
struct packref *ref = NULL;
const char *buffer = *buffer_out;
const char *refname_begin, *refname_end;
size_t refname_len;
git_oid id;
refname_begin = (buffer + GIT_OID_HEXSZ + 1);
if (refname_begin >= buffer_end || refname_begin[-1] != ' ')
goto corrupt;
/* Is this a valid object id? */
if (git_oid_fromstr(&id, buffer) < 0)
goto corrupt;
refname_end = memchr(refname_begin, '\n', buffer_end - refname_begin);
if (refname_end == NULL)
refname_end = buffer_end;
if (refname_end[-1] == '\r')
refname_end--;
refname_len = refname_end - refname_begin;
ref = git__calloc(1, sizeof(struct packref) + refname_len + 1);
GITERR_CHECK_ALLOC(ref);
memcpy(ref->name, refname_begin, refname_len);
ref->name[refname_len] = 0;
git_oid_cpy(&ref->oid, &id);
*ref_out = ref;
*buffer_out = refname_end + 1;
return 0;
corrupt:
git__free(ref);
giterr_set(GITERR_REFERENCE, "The packed references file is corrupted");
return -1;
}
static int packed_parse_peel(
struct packref *tag_ref,
const char **buffer_out,
const char *buffer_end)
{
const char *buffer = *buffer_out + 1;
assert(buffer[-1] == '^');
/* Ensure it's not the first entry of the file */
if (tag_ref == NULL)
goto corrupt;
if (buffer + GIT_OID_HEXSZ > buffer_end)
goto corrupt;
/* Is this a valid object id? */
if (git_oid_fromstr(&tag_ref->peel, buffer) < 0)
goto corrupt;
buffer = buffer + GIT_OID_HEXSZ;
if (*buffer == '\r')
buffer++;
if (buffer != buffer_end) {
if (*buffer == '\n')
buffer++;
else
goto corrupt;
}
tag_ref->flags |= PACKREF_HAS_PEEL;
*buffer_out = buffer;
return 0;
corrupt:
giterr_set(GITERR_REFERENCE, "The packed references file is corrupted");
return -1;
} }
static int packed_load(refdb_fs_backend *backend) static int packed_reload(refdb_fs_backend *backend)
{ {
int result, updated; int error;
git_buf packfile = GIT_BUF_INIT; git_buf packedrefs = GIT_BUF_INIT;
const char *buffer_start, *buffer_end; char *scan, *eof, *eol;
git_refcache *ref_cache = &backend->refcache;
/* First we make sure we have allocated the hash table */
if (ref_cache->packfile == NULL) {
ref_cache->packfile = git_strmap_alloc();
GITERR_CHECK_ALLOC(ref_cache->packfile);
}
if (backend->path == NULL) if (!backend->path)
return 0; return 0;
result = reference_read(&packfile, &ref_cache->packfile_time, error = git_sortedcache_lockandload(backend->refcache, &packedrefs);
backend->path, GIT_PACKEDREFS_FILE, &updated);
/* /*
* If we couldn't find the file, we need to clear the table and * If we can't find the packed-refs, clear table and return.
* return. On any other error, we return that error. If everything * Any other error just gets passed through.
* went fine and the file wasn't updated, then there's nothing new * If no error, and file wasn't changed, just return.
* for us here, so just return. Anything else means we need to * Anything else means we need to refresh the packed refs.
* refresh the packed refs.
*/ */
if (result == GIT_ENOTFOUND) { if (error <= 0) {
git_strmap_clear(ref_cache->packfile); if (error == GIT_ENOTFOUND) {
return 0; git_sortedcache_clear(backend->refcache, true);
giterr_clear();
error = 0;
}
return error;
} }
if (result < 0) /* At this point, refresh the packed refs from the loaded buffer. */
return -1;
if (!updated) git_sortedcache_clear(backend->refcache, false);
return 0;
/* scan = (char *)packedrefs.ptr;
* At this point, we want to refresh the packed refs. We already eof = scan + packedrefs.size;
* have the contents in our buffer.
*/
git_strmap_clear(ref_cache->packfile);
buffer_start = (const char *)packfile.ptr;
buffer_end = (const char *)(buffer_start) + packfile.size;
backend->peeling_mode = PEELING_NONE; backend->peeling_mode = PEELING_NONE;
if (buffer_start[0] == '#') { if (*scan == '#') {
static const char *traits_header = "# pack-refs with: "; static const char *traits_header = "# pack-refs with: ";
if (git__prefixcmp(buffer_start, traits_header) == 0) { if (git__prefixcmp(scan, traits_header) == 0) {
char *traits = (char *)buffer_start + strlen(traits_header); scan += strlen(traits_header);
char *traits_end = strchr(traits, '\n'); eol = strchr(scan, '\n');
if (traits_end == NULL) if (!eol)
goto parse_failed; goto parse_failed;
*eol = '\0';
*traits_end = '\0'; if (strstr(scan, " fully-peeled ") != NULL) {
if (strstr(traits, " fully-peeled ") != NULL) {
backend->peeling_mode = PEELING_FULL; backend->peeling_mode = PEELING_FULL;
} else if (strstr(traits, " peeled ") != NULL) { } else if (strstr(scan, " peeled ") != NULL) {
backend->peeling_mode = PEELING_STANDARD; backend->peeling_mode = PEELING_STANDARD;
} }
buffer_start = traits_end + 1; scan = eol + 1;
} }
} }
while (buffer_start < buffer_end && buffer_start[0] == '#') { while (scan < eof && *scan == '#') {
buffer_start = strchr(buffer_start, '\n'); if (!(eol = strchr(scan, '\n')))
if (buffer_start == NULL)
goto parse_failed; goto parse_failed;
scan = eol + 1;
buffer_start++;
} }
while (buffer_start < buffer_end) { while (scan < eof) {
int err; struct packref *ref;
struct packref *ref = NULL; git_oid oid;
/* parse "<OID> <refname>\n" */
if (git_oid_fromstr(&oid, scan) < 0)
goto parse_failed;
scan += GIT_OID_HEXSZ;
if (*scan++ != ' ')
goto parse_failed;
if (!(eol = strchr(scan, '\n')))
goto parse_failed;
*eol = '\0';
if (eol[-1] == '\r')
eol[-1] = '\0';
if (git_sortedcache_upsert((void **)&ref, backend->refcache, scan) < 0)
goto parse_failed;
scan = eol + 1;
git_oid_cpy(&ref->oid, &oid);
/* look for optional "^<OID>\n" */
if (packed_parse_oid(&ref, &buffer_start, buffer_end) < 0) if (*scan == '^') {
if (git_oid_fromstr(&oid, scan + 1) < 0)
goto parse_failed; goto parse_failed;
scan += GIT_OID_HEXSZ + 1;
if (buffer_start[0] == '^') { if (scan < eof) {
if (packed_parse_peel(ref, &buffer_start, buffer_end) < 0) if (!(eol = strchr(scan, '\n')))
goto parse_failed; goto parse_failed;
} else if (backend->peeling_mode == PEELING_FULL || scan = eol + 1;
}
git_oid_cpy(&ref->peel, &oid);
ref->flags |= PACKREF_HAS_PEEL;
}
else if (backend->peeling_mode == PEELING_FULL ||
(backend->peeling_mode == PEELING_STANDARD && (backend->peeling_mode == PEELING_STANDARD &&
git__prefixcmp(ref->name, GIT_REFS_TAGS_DIR) == 0)) { git__prefixcmp(ref->name, GIT_REFS_TAGS_DIR) == 0))
ref->flags |= PACKREF_CANNOT_PEEL; ref->flags |= PACKREF_CANNOT_PEEL;
} }
git_strmap_insert(ref_cache->packfile, ref->name, ref, err); git_sortedcache_unlock(backend->refcache);
if (err < 0) git_buf_free(&packedrefs);
goto parse_failed;
}
git_buf_free(&packfile);
return 0; return 0;
parse_failed: parse_failed:
git_strmap_free(ref_cache->packfile); giterr_set(GITERR_REFERENCE, "Corrupted packed references file");
ref_cache->packfile = NULL;
git_buf_free(&packfile); git_sortedcache_clear(backend->refcache, false);
git_sortedcache_unlock(backend->refcache);
git_buf_free(&packedrefs);
return -1; return -1;
} }
static int loose_parse_oid(git_oid *oid, const char *filename, git_buf *file_content) static int loose_parse_oid(
git_oid *oid, const char *filename, git_buf *file_content)
{ {
size_t len; const char *str = git_buf_cstr(file_content);
const char *str;
len = git_buf_len(file_content); if (git_buf_len(file_content) < GIT_OID_HEXSZ)
if (len < GIT_OID_HEXSZ)
goto corrupted; goto corrupted;
/* str is guranteed to be zero-terminated */
str = git_buf_cstr(file_content);
/* we need to get 40 OID characters from the file */ /* we need to get 40 OID characters from the file */
if (git_oid_fromstr(oid, git_buf_cstr(file_content)) < 0) if (git_oid_fromstr(oid, str) < 0)
goto corrupted; goto corrupted;
/* If the file is longer than 40 chars, the 41st must be a space */ /* If the file is longer than 40 chars, the 41st must be a space */
...@@ -302,77 +209,71 @@ corrupted: ...@@ -302,77 +209,71 @@ corrupted:
return -1; return -1;
} }
static int loose_lookup_to_packfile( static int loose_readbuffer(git_buf *buf, const char *base, const char *path)
struct packref **ref_out, {
refdb_fs_backend *backend, int error;
const char *name)
/* build full path to file */
if ((error = git_buf_joinpath(buf, base, path)) < 0 ||
(error = git_futils_readbuffer(buf, buf->ptr)) < 0)
git_buf_free(buf);
return error;
}
static int loose_lookup_to_packfile(refdb_fs_backend *backend, const char *name)
{ {
int error = 0;
git_buf ref_file = GIT_BUF_INIT; git_buf ref_file = GIT_BUF_INIT;
struct packref *ref = NULL; struct packref *ref = NULL;
size_t name_len; git_oid oid;
*ref_out = NULL;
if (reference_read(&ref_file, NULL, backend->path, name, NULL) < 0) /* if we fail to load the loose reference, assume someone changed
return -1; * the filesystem under us and skip it...
*/
if (loose_readbuffer(&ref_file, backend->path, name) < 0) {
giterr_clear();
goto done;
}
/* skip symbolic refs */ /* skip symbolic refs */
if (!git__prefixcmp(git_buf_cstr(&ref_file), GIT_SYMREF)) { if (!git__prefixcmp(git_buf_cstr(&ref_file), GIT_SYMREF))
git_buf_free(&ref_file); goto done;
return 0;
}
git_buf_rtrim(&ref_file); /* parse OID from file */
if ((error = loose_parse_oid(&oid, name, &ref_file)) < 0)
goto done;
name_len = strlen(name); git_sortedcache_lock(backend->refcache);
ref = git__calloc(1, sizeof(struct packref) + name_len + 1);
GITERR_CHECK_ALLOC(ref);
memcpy(ref->name, name, name_len); if (!(error = git_sortedcache_upsert(
ref->name[name_len] = 0; (void **)&ref, backend->refcache, name))) {
if (loose_parse_oid(&ref->oid, name, &ref_file) < 0) { git_oid_cpy(&ref->oid, &oid);
git_buf_free(&ref_file); ref->flags = PACKREF_WAS_LOOSE;
git__free(ref);
return -1;
} }
ref->flags = PACKREF_WAS_LOOSE; git_sortedcache_unlock(backend->refcache);
*ref_out = ref; done:
git_buf_free(&ref_file); git_buf_free(&ref_file);
return 0; return error;
} }
static int _dirent_loose_load(void *data, git_buf *full_path) static int _dirent_loose_load(void *data, git_buf *full_path)
{ {
refdb_fs_backend *backend = (refdb_fs_backend *)data; refdb_fs_backend *backend = (refdb_fs_backend *)data;
void *old_ref = NULL;
struct packref *ref;
const char *file_path; const char *file_path;
int err;
if (git_path_isdir(full_path->ptr) == true) if (git__suffixcmp(full_path->ptr, ".lock") == 0)
return 0;
if (git_path_isdir(full_path->ptr))
return git_path_direach(full_path, _dirent_loose_load, backend); return git_path_direach(full_path, _dirent_loose_load, backend);
file_path = full_path->ptr + strlen(backend->path); file_path = full_path->ptr + strlen(backend->path);
if (loose_lookup_to_packfile(&ref, backend, file_path) < 0) return loose_lookup_to_packfile(backend, file_path);
return -1;
if (!ref)
return 0;
git_strmap_insert2(
backend->refcache.packfile, ref->name, ref, old_ref, err);
if (err < 0) {
git__free(ref);
return -1;
}
git__free(old_ref);
return 0;
} }
/* /*
...@@ -383,11 +284,8 @@ static int _dirent_loose_load(void *data, git_buf *full_path) ...@@ -383,11 +284,8 @@ static int _dirent_loose_load(void *data, git_buf *full_path)
*/ */
static int packed_loadloose(refdb_fs_backend *backend) static int packed_loadloose(refdb_fs_backend *backend)
{ {
int error;
git_buf refs_path = GIT_BUF_INIT; git_buf refs_path = GIT_BUF_INIT;
int result;
/* the packfile must have been previously loaded! */
assert(backend->refcache.packfile);
if (git_buf_joinpath(&refs_path, backend->path, GIT_REFS_DIR) < 0) if (git_buf_joinpath(&refs_path, backend->path, GIT_REFS_DIR) < 0)
return -1; return -1;
...@@ -397,10 +295,11 @@ static int packed_loadloose(refdb_fs_backend *backend) ...@@ -397,10 +295,11 @@ static int packed_loadloose(refdb_fs_backend *backend)
* This will overwrite any old packed entries with their * This will overwrite any old packed entries with their
* updated loose versions * updated loose versions
*/ */
result = git_path_direach(&refs_path, _dirent_loose_load, backend); error = git_path_direach(&refs_path, _dirent_loose_load, backend);
git_buf_free(&refs_path); git_buf_free(&refs_path);
return result; return error;
} }
static int refdb_fs_backend__exists( static int refdb_fs_backend__exists(
...@@ -408,23 +307,17 @@ static int refdb_fs_backend__exists( ...@@ -408,23 +307,17 @@ static int refdb_fs_backend__exists(
git_refdb_backend *_backend, git_refdb_backend *_backend,
const char *ref_name) const char *ref_name)
{ {
refdb_fs_backend *backend; refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
git_buf ref_path = GIT_BUF_INIT; git_buf ref_path = GIT_BUF_INIT;
assert(_backend); assert(backend);
backend = (refdb_fs_backend *)_backend;
if (packed_load(backend) < 0)
return -1;
if (git_buf_joinpath(&ref_path, backend->path, ref_name) < 0) if (packed_reload(backend) < 0 ||
git_buf_joinpath(&ref_path, backend->path, ref_name) < 0)
return -1; return -1;
if (git_path_isfile(ref_path.ptr) == true || *exists = git_path_isfile(ref_path.ptr) ||
git_strmap_exists(backend->refcache.packfile, ref_path.ptr)) git_sortedcache_lookup(backend->refcache, ref_name);
*exists = 1;
else
*exists = 0;
git_buf_free(&ref_path); git_buf_free(&ref_path);
return 0; return 0;
...@@ -456,69 +349,39 @@ static int loose_lookup( ...@@ -456,69 +349,39 @@ static int loose_lookup(
refdb_fs_backend *backend, refdb_fs_backend *backend,
const char *ref_name) const char *ref_name)
{ {
const char *target;
git_oid oid;
git_buf ref_file = GIT_BUF_INIT; git_buf ref_file = GIT_BUF_INIT;
int error = 0; int error = 0;
if (out) if (out)
*out = NULL; *out = NULL;
error = reference_read(&ref_file, NULL, backend->path, ref_name, NULL); if ((error = loose_readbuffer(&ref_file, backend->path, ref_name)) < 0)
/* cannot read loose ref file - gah */;
if (error < 0) else if (git__prefixcmp(git_buf_cstr(&ref_file), GIT_SYMREF) == 0) {
goto done; const char *target;
if (git__prefixcmp(git_buf_cstr(&ref_file), GIT_SYMREF) == 0) {
git_buf_rtrim(&ref_file); git_buf_rtrim(&ref_file);
if ((target = loose_parse_symbolic(&ref_file)) == NULL) { if (!(target = loose_parse_symbolic(&ref_file)))
error = -1; error = -1;
goto done; else if (out != NULL)
}
if (out)
*out = git_reference__alloc_symbolic(ref_name, target); *out = git_reference__alloc_symbolic(ref_name, target);
} else { } else {
if ((error = loose_parse_oid(&oid, ref_name, &ref_file)) < 0) git_oid oid;
goto done;
if (out) if (!(error = loose_parse_oid(&oid, ref_name, &ref_file)) &&
out != NULL)
*out = git_reference__alloc(ref_name, &oid, NULL); *out = git_reference__alloc(ref_name, &oid, NULL);
} }
if (out && *out == NULL)
error = -1;
done:
git_buf_free(&ref_file); git_buf_free(&ref_file);
return error; return error;
} }
static int packed_map_entry( static int ref_error_notfound(const char *name)
struct packref **entry,
khiter_t *pos,
refdb_fs_backend *backend,
const char *ref_name)
{ {
git_strmap *packfile_refs; giterr_set(GITERR_REFERENCE, "Reference '%s' not found", name);
if (packed_load(backend) < 0)
return -1;
/* Look up on the packfile */
packfile_refs = backend->refcache.packfile;
*pos = git_strmap_lookup_index(packfile_refs, ref_name);
if (!git_strmap_valid_index(packfile_refs, *pos)) {
giterr_set(GITERR_REFERENCE, "Reference '%s' not found", ref_name);
return GIT_ENOTFOUND; return GIT_ENOTFOUND;
}
*entry = git_strmap_value_at(packfile_refs, *pos);
return 0;
} }
static int packed_lookup( static int packed_lookup(
...@@ -526,18 +389,25 @@ static int packed_lookup( ...@@ -526,18 +389,25 @@ static int packed_lookup(
refdb_fs_backend *backend, refdb_fs_backend *backend,
const char *ref_name) const char *ref_name)
{ {
struct packref *entry;
khiter_t pos;
int error = 0; int error = 0;
struct packref *entry;
if ((error = packed_map_entry(&entry, &pos, backend, ref_name)) < 0) if (packed_reload(backend) < 0)
return error;
if ((*out = git_reference__alloc(ref_name,
&entry->oid, &entry->peel)) == NULL)
return -1; return -1;
return 0; git_sortedcache_lock(backend->refcache);
entry = git_sortedcache_lookup(backend->refcache, ref_name);
if (!entry) {
error = ref_error_notfound(ref_name);
} else {
*out = git_reference__alloc(ref_name, &entry->oid, &entry->peel);
if (!*out)
error = -1;
}
git_sortedcache_unlock(backend->refcache);
return error;
} }
static int refdb_fs_backend__lookup( static int refdb_fs_backend__lookup(
...@@ -545,24 +415,22 @@ static int refdb_fs_backend__lookup( ...@@ -545,24 +415,22 @@ static int refdb_fs_backend__lookup(
git_refdb_backend *_backend, git_refdb_backend *_backend,
const char *ref_name) const char *ref_name)
{ {
refdb_fs_backend *backend; refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
int result; int error;
assert(_backend);
backend = (refdb_fs_backend *)_backend; assert(backend);
if ((result = loose_lookup(out, backend, ref_name)) == 0) if (!(error = loose_lookup(out, backend, ref_name)))
return 0; return 0;
/* only try to lookup this reference on the packfile if it /* only try to lookup this reference on the packfile if it
* wasn't found on the loose refs; not if there was a critical error */ * wasn't found on the loose refs; not if there was a critical error */
if (result == GIT_ENOTFOUND) { if (error == GIT_ENOTFOUND) {
giterr_clear(); giterr_clear();
result = packed_lookup(out, backend, ref_name); error = packed_lookup(out, backend, ref_name);
} }
return result; return error;
} }
typedef struct { typedef struct {
...@@ -573,8 +441,8 @@ typedef struct { ...@@ -573,8 +441,8 @@ typedef struct {
git_pool pool; git_pool pool;
git_vector loose; git_vector loose;
unsigned int loose_pos; size_t loose_pos;
khiter_t packed_pos; size_t packed_pos;
} refdb_fs_iter; } refdb_fs_iter;
static void refdb_fs_backend__iterator_free(git_reference_iterator *_iter) static void refdb_fs_backend__iterator_free(git_reference_iterator *_iter)
...@@ -589,7 +457,6 @@ static void refdb_fs_backend__iterator_free(git_reference_iterator *_iter) ...@@ -589,7 +457,6 @@ static void refdb_fs_backend__iterator_free(git_reference_iterator *_iter)
static int iter_load_loose_paths(refdb_fs_backend *backend, refdb_fs_iter *iter) static int iter_load_loose_paths(refdb_fs_backend *backend, refdb_fs_iter *iter)
{ {
int error = 0; int error = 0;
git_strmap *packfile = backend->refcache.packfile;
git_buf path = GIT_BUF_INIT; git_buf path = GIT_BUF_INIT;
git_iterator *fsit = NULL; git_iterator *fsit = NULL;
const git_index_entry *entry = NULL; const git_index_entry *entry = NULL;
...@@ -597,18 +464,18 @@ static int iter_load_loose_paths(refdb_fs_backend *backend, refdb_fs_iter *iter) ...@@ -597,18 +464,18 @@ static int iter_load_loose_paths(refdb_fs_backend *backend, refdb_fs_iter *iter)
if (!backend->path) /* do nothing if no path for loose refs */ if (!backend->path) /* do nothing if no path for loose refs */
return 0; return 0;
if (git_buf_printf(&path, "%s/refs", backend->path) < 0) if ((error = git_buf_printf(&path, "%s/refs", backend->path)) < 0 ||
return -1; (error = git_iterator_for_filesystem(
&fsit, git_buf_cstr(&path), 0, NULL, NULL)) < 0) {
git_buf_free(&path);
return error;
}
if ((error = git_iterator_for_filesystem( error = git_buf_sets(&path, GIT_REFS_DIR);
&fsit, git_buf_cstr(&path), 0, NULL, NULL)) < 0 ||
(error = git_vector_init(&iter->loose, 8, NULL)) < 0 ||
(error = git_buf_sets(&path, GIT_REFS_DIR)) < 0)
goto cleanup;
while (!git_iterator_advance(&entry, fsit)) { while (!error && !git_iterator_advance(&entry, fsit)) {
const char *ref_name; const char *ref_name;
khiter_t pos; struct packref *ref;
char *ref_dup; char *ref_dup;
git_buf_truncate(&path, strlen(GIT_REFS_DIR)); git_buf_truncate(&path, strlen(GIT_REFS_DIR));
...@@ -619,26 +486,23 @@ static int iter_load_loose_paths(refdb_fs_backend *backend, refdb_fs_iter *iter) ...@@ -619,26 +486,23 @@ static int iter_load_loose_paths(refdb_fs_backend *backend, refdb_fs_iter *iter)
(iter->glob && p_fnmatch(iter->glob, ref_name, 0) != 0)) (iter->glob && p_fnmatch(iter->glob, ref_name, 0) != 0))
continue; continue;
pos = git_strmap_lookup_index(packfile, ref_name); git_sortedcache_lock(backend->refcache);
if (git_strmap_valid_index(packfile, pos)) { ref = git_sortedcache_lookup(backend->refcache, ref_name);
struct packref *ref = git_strmap_value_at(packfile, pos); if (ref)
ref->flags |= PACKREF_SHADOWED; ref->flags |= PACKREF_SHADOWED;
} git_sortedcache_unlock(backend->refcache);
if (!(ref_dup = git_pool_strdup(&iter->pool, ref_name))) { ref_dup = git_pool_strdup(&iter->pool, ref_name);
if (!ref_dup)
error = -1; error = -1;
goto cleanup; else
} error = git_vector_insert(&iter->loose, ref_dup);
if ((error = git_vector_insert(&iter->loose, ref_dup)) < 0)
goto cleanup;
} }
cleanup:
git_iterator_free(fsit); git_iterator_free(fsit);
git_buf_free(&path); git_buf_free(&path);
return 0; return error;
} }
static int refdb_fs_backend__iterator_next( static int refdb_fs_backend__iterator_next(
...@@ -646,7 +510,7 @@ static int refdb_fs_backend__iterator_next( ...@@ -646,7 +510,7 @@ static int refdb_fs_backend__iterator_next(
{ {
refdb_fs_iter *iter = (refdb_fs_iter *)_iter; refdb_fs_iter *iter = (refdb_fs_iter *)_iter;
refdb_fs_backend *backend = (refdb_fs_backend *)iter->parent.db->backend; refdb_fs_backend *backend = (refdb_fs_backend *)iter->parent.db->backend;
git_strmap *packfile = backend->refcache.packfile; struct packref *ref;
while (iter->loose_pos < iter->loose.length) { while (iter->loose_pos < iter->loose.length) {
const char *path = git_vector_get(&iter->loose, iter->loose_pos++); const char *path = git_vector_get(&iter->loose, iter->loose_pos++);
...@@ -657,17 +521,12 @@ static int refdb_fs_backend__iterator_next( ...@@ -657,17 +521,12 @@ static int refdb_fs_backend__iterator_next(
giterr_clear(); giterr_clear();
} }
while (iter->packed_pos < kh_end(packfile)) { git_sortedcache_lock(backend->refcache);
struct packref *ref = NULL;
while (!kh_exist(packfile, iter->packed_pos)) {
iter->packed_pos++;
if (iter->packed_pos == kh_end(packfile))
return GIT_ITEROVER;
}
ref = kh_val(packfile, iter->packed_pos); while (iter->packed_pos < git_sortedcache_entrycount(backend->refcache)) {
iter->packed_pos++; ref = git_sortedcache_entry(backend->refcache, iter->packed_pos++);
if (!ref) /* stop now if another thread deleted refs and we past end */
break;
if (ref->flags & PACKREF_SHADOWED) if (ref->flags & PACKREF_SHADOWED)
continue; continue;
...@@ -676,12 +535,11 @@ static int refdb_fs_backend__iterator_next( ...@@ -676,12 +535,11 @@ static int refdb_fs_backend__iterator_next(
continue; continue;
*out = git_reference__alloc(ref->name, &ref->oid, &ref->peel); *out = git_reference__alloc(ref->name, &ref->oid, &ref->peel);
if (*out == NULL) git_sortedcache_unlock(backend->refcache);
return -1; return (*out != NULL) ? 0 : -1;
return 0;
} }
git_sortedcache_unlock(backend->refcache);
return GIT_ITEROVER; return GIT_ITEROVER;
} }
...@@ -690,14 +548,10 @@ static int refdb_fs_backend__iterator_next_name( ...@@ -690,14 +548,10 @@ static int refdb_fs_backend__iterator_next_name(
{ {
refdb_fs_iter *iter = (refdb_fs_iter *)_iter; refdb_fs_iter *iter = (refdb_fs_iter *)_iter;
refdb_fs_backend *backend = (refdb_fs_backend *)iter->parent.db->backend; refdb_fs_backend *backend = (refdb_fs_backend *)iter->parent.db->backend;
git_strmap *packfile = backend->refcache.packfile;
while (iter->loose_pos < iter->loose.length) { while (iter->loose_pos < iter->loose.length) {
const char *path = git_vector_get(&iter->loose, iter->loose_pos++); const char *path = git_vector_get(&iter->loose, iter->loose_pos++);
if (git_strmap_exists(packfile, path))
continue;
if (loose_lookup(NULL, backend, path) != 0) { if (loose_lookup(NULL, backend, path) != 0) {
giterr_clear(); giterr_clear();
continue; continue;
...@@ -707,22 +561,25 @@ static int refdb_fs_backend__iterator_next_name( ...@@ -707,22 +561,25 @@ static int refdb_fs_backend__iterator_next_name(
return 0; return 0;
} }
while (iter->packed_pos < kh_end(packfile)) { git_sortedcache_lock(backend->refcache);
while (!kh_exist(packfile, iter->packed_pos)) {
iter->packed_pos++; while (iter->packed_pos < git_sortedcache_entrycount(backend->refcache)) {
if (iter->packed_pos == kh_end(packfile)) struct packref *ref =
return GIT_ITEROVER; git_sortedcache_entry(backend->refcache, iter->packed_pos++);
}
*out = kh_key(packfile, iter->packed_pos); if (ref->flags & PACKREF_SHADOWED)
iter->packed_pos++; continue;
*out = ref->name;
if (iter->glob && p_fnmatch(iter->glob, *out, 0) != 0) if (iter->glob && p_fnmatch(iter->glob, *out, 0) != 0)
continue; continue;
git_sortedcache_unlock(backend->refcache);
return 0; return 0;
} }
git_sortedcache_unlock(backend->refcache);
return GIT_ITEROVER; return GIT_ITEROVER;
} }
...@@ -730,18 +587,18 @@ static int refdb_fs_backend__iterator( ...@@ -730,18 +587,18 @@ static int refdb_fs_backend__iterator(
git_reference_iterator **out, git_refdb_backend *_backend, const char *glob) git_reference_iterator **out, git_refdb_backend *_backend, const char *glob)
{ {
refdb_fs_iter *iter; refdb_fs_iter *iter;
refdb_fs_backend *backend; refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
assert(_backend); assert(backend);
backend = (refdb_fs_backend *)_backend;
if (packed_load(backend) < 0) if (packed_reload(backend) < 0)
return -1; return -1;
iter = git__calloc(1, sizeof(refdb_fs_iter)); iter = git__calloc(1, sizeof(refdb_fs_iter));
GITERR_CHECK_ALLOC(iter); GITERR_CHECK_ALLOC(iter);
if (git_pool_init(&iter->pool, 1, 0) < 0) if (git_pool_init(&iter->pool, 1, 0) < 0 ||
git_vector_init(&iter->loose, 8, NULL) < 0)
goto fail; goto fail;
if (glob != NULL && if (glob != NULL &&
...@@ -786,15 +643,16 @@ static int reference_path_available( ...@@ -786,15 +643,16 @@ static int reference_path_available(
const char* old_ref, const char* old_ref,
int force) int force)
{ {
struct packref *this_ref; size_t i;
if (packed_load(backend) < 0) if (packed_reload(backend) < 0)
return -1; return -1;
if (!force) { if (!force) {
int exists; int exists;
if (refdb_fs_backend__exists(&exists, (git_refdb_backend *)backend, new_ref) < 0) if (refdb_fs_backend__exists(
&exists, (git_refdb_backend *)backend, new_ref) < 0)
return -1; return -1;
if (exists) { if (exists) {
...@@ -805,14 +663,21 @@ static int reference_path_available( ...@@ -805,14 +663,21 @@ static int reference_path_available(
} }
} }
git_strmap_foreach_value(backend->refcache.packfile, this_ref, { git_sortedcache_lock(backend->refcache);
for (i = 0; i < git_sortedcache_entrycount(backend->refcache); ++i) {
struct packref *this_ref =
git_sortedcache_entry(backend->refcache, i);
if (!ref_is_available(old_ref, new_ref, this_ref->name)) { if (!ref_is_available(old_ref, new_ref, this_ref->name)) {
git_sortedcache_unlock(backend->refcache);
giterr_set(GITERR_REFERENCE, giterr_set(GITERR_REFERENCE,
"The path to reference '%s' collides with an existing one", new_ref); "The path to reference '%s' collides with an existing one", new_ref);
return -1; return -1;
} }
}); }
git_sortedcache_unlock(backend->refcache);
return 0; return 0;
} }
...@@ -839,12 +704,9 @@ static int loose_write(refdb_fs_backend *backend, const git_reference *ref) ...@@ -839,12 +704,9 @@ static int loose_write(refdb_fs_backend *backend, const git_reference *ref)
if (ref->type == GIT_REF_OID) { if (ref->type == GIT_REF_OID) {
char oid[GIT_OID_HEXSZ + 1]; char oid[GIT_OID_HEXSZ + 1];
git_oid_nfmt(oid, sizeof(oid), &ref->target.oid);
git_oid_fmt(oid, &ref->target.oid);
oid[GIT_OID_HEXSZ] = '\0';
git_filebuf_printf(&file, "%s\n", oid); git_filebuf_printf(&file, "%s\n", oid);
} else if (ref->type == GIT_REF_SYMBOLIC) { } else if (ref->type == GIT_REF_SYMBOLIC) {
git_filebuf_printf(&file, GIT_SYMREF "%s\n", ref->target.symbolic); git_filebuf_printf(&file, GIT_SYMREF "%s\n", ref->target.symbolic);
} else { } else {
...@@ -854,14 +716,6 @@ static int loose_write(refdb_fs_backend *backend, const git_reference *ref) ...@@ -854,14 +716,6 @@ static int loose_write(refdb_fs_backend *backend, const git_reference *ref)
return git_filebuf_commit(&file, GIT_REFS_FILE_MODE); return git_filebuf_commit(&file, GIT_REFS_FILE_MODE);
} }
static int packed_sort(const void *a, const void *b)
{
const struct packref *ref_a = (const struct packref *)a;
const struct packref *ref_b = (const struct packref *)b;
return strcmp(ref_a->name, ref_b->name);
}
/* /*
* Find out what object this reference resolves to. * Find out what object this reference resolves to.
* *
...@@ -914,9 +768,7 @@ static int packed_find_peel(refdb_fs_backend *backend, struct packref *ref) ...@@ -914,9 +768,7 @@ static int packed_find_peel(refdb_fs_backend *backend, struct packref *ref)
static int packed_write_ref(struct packref *ref, git_filebuf *file) static int packed_write_ref(struct packref *ref, git_filebuf *file)
{ {
char oid[GIT_OID_HEXSZ + 1]; char oid[GIT_OID_HEXSZ + 1];
git_oid_nfmt(oid, sizeof(oid), &ref->oid);
git_oid_fmt(oid, &ref->oid);
oid[GIT_OID_HEXSZ] = 0;
/* /*
* For references that peel to an object in the repo, we must * For references that peel to an object in the repo, we must
...@@ -930,8 +782,7 @@ static int packed_write_ref(struct packref *ref, git_filebuf *file) ...@@ -930,8 +782,7 @@ static int packed_write_ref(struct packref *ref, git_filebuf *file)
*/ */
if (ref->flags & PACKREF_HAS_PEEL) { if (ref->flags & PACKREF_HAS_PEEL) {
char peel[GIT_OID_HEXSZ + 1]; char peel[GIT_OID_HEXSZ + 1];
git_oid_fmt(peel, &ref->peel); git_oid_nfmt(peel, sizeof(peel), &ref->peel);
peel[GIT_OID_HEXSZ] = 0;
if (git_filebuf_printf(file, "%s %s\n^%s\n", oid, ref->name, peel) < 0) if (git_filebuf_printf(file, "%s %s\n^%s\n", oid, ref->name, peel) < 0)
return -1; return -1;
...@@ -954,16 +805,16 @@ static int packed_write_ref(struct packref *ref, git_filebuf *file) ...@@ -954,16 +805,16 @@ static int packed_write_ref(struct packref *ref, git_filebuf *file)
* is well-written, because we are destructing references * is well-written, because we are destructing references
* here otherwise. * here otherwise.
*/ */
static int packed_remove_loose( static int packed_remove_loose(refdb_fs_backend *backend)
refdb_fs_backend *backend,
git_vector *packing_list)
{ {
size_t i; size_t i;
git_buf full_path = GIT_BUF_INIT; git_buf full_path = GIT_BUF_INIT;
int failed = 0; int failed = 0;
for (i = 0; i < packing_list->length; ++i) { /* backend->refcache is already locked when this is called */
struct packref *ref = git_vector_get(packing_list, i);
for (i = 0; i < git_sortedcache_entrycount(backend->refcache); ++i) {
struct packref *ref = git_sortedcache_entry(backend->refcache, i);
if ((ref->flags & PACKREF_WAS_LOOSE) == 0) if ((ref->flags & PACKREF_WAS_LOOSE) == 0)
continue; continue;
...@@ -971,14 +822,13 @@ static int packed_remove_loose( ...@@ -971,14 +822,13 @@ static int packed_remove_loose(
if (git_buf_joinpath(&full_path, backend->path, ref->name) < 0) if (git_buf_joinpath(&full_path, backend->path, ref->name) < 0)
return -1; /* critical; do not try to recover on oom */ return -1; /* critical; do not try to recover on oom */
if (git_path_exists(full_path.ptr) == true && p_unlink(full_path.ptr) < 0) { if (git_path_exists(full_path.ptr) && p_unlink(full_path.ptr) < 0) {
if (failed) if (failed)
continue; continue;
giterr_set(GITERR_REFERENCE, giterr_set(GITERR_REFERENCE,
"Failed to remove loose reference '%s' after packing: %s", "Failed to remove loose reference '%s' after packing: %s",
full_path.ptr, strerror(errno)); full_path.ptr, strerror(errno));
failed = 1; failed = 1;
} }
...@@ -1002,33 +852,14 @@ static int packed_write(refdb_fs_backend *backend) ...@@ -1002,33 +852,14 @@ static int packed_write(refdb_fs_backend *backend)
git_filebuf pack_file = GIT_FILEBUF_INIT; git_filebuf pack_file = GIT_FILEBUF_INIT;
size_t i; size_t i;
git_buf pack_file_path = GIT_BUF_INIT; git_buf pack_file_path = GIT_BUF_INIT;
git_vector packing_list;
unsigned int total_refs;
assert(backend && backend->refcache.packfile);
total_refs = /* lock the cache to updates while we do this */
(unsigned int)git_strmap_num_entries(backend->refcache.packfile); if (git_sortedcache_lock(backend->refcache) < 0)
if (git_vector_init(&packing_list, total_refs, packed_sort) < 0)
return -1; return -1;
/* Load all the packfile into a vector */ /* Open the file! */
{ if (git_buf_joinpath(
struct packref *reference; &pack_file_path, backend->path, GIT_PACKEDREFS_FILE) < 0)
/* cannot fail: vector already has the right size */
git_strmap_foreach_value(backend->refcache.packfile, reference, {
git_vector_insert(&packing_list, reference);
});
}
/* sort the vector so the entries appear sorted on the packfile */
git_vector_sort(&packing_list);
/* Now we can open the file! */
if (git_buf_joinpath(&pack_file_path,
backend->path, GIT_PACKEDREFS_FILE) < 0)
goto cleanup_memory; goto cleanup_memory;
if (git_filebuf_open(&pack_file, pack_file_path.ptr, 0) < 0) if (git_filebuf_open(&pack_file, pack_file_path.ptr, 0) < 0)
...@@ -1040,8 +871,9 @@ static int packed_write(refdb_fs_backend *backend) ...@@ -1040,8 +871,9 @@ static int packed_write(refdb_fs_backend *backend)
if (git_filebuf_printf(&pack_file, "%s\n", GIT_PACKEDREFS_HEADER) < 0) if (git_filebuf_printf(&pack_file, "%s\n", GIT_PACKEDREFS_HEADER) < 0)
goto cleanup_packfile; goto cleanup_packfile;
for (i = 0; i < packing_list.length; ++i) { for (i = 0; i < git_sortedcache_entrycount(backend->refcache); ++i) {
struct packref *ref = (struct packref *)git_vector_get(&packing_list, i); struct packref *ref =
git_sortedcache_entry(backend->refcache, i);
if (packed_find_peel(backend, ref) < 0) if (packed_find_peel(backend, ref) < 0)
goto cleanup_packfile; goto cleanup_packfile;
...@@ -1057,16 +889,16 @@ static int packed_write(refdb_fs_backend *backend) ...@@ -1057,16 +889,16 @@ static int packed_write(refdb_fs_backend *backend)
/* when and only when the packfile has been properly written, /* when and only when the packfile has been properly written,
* we can go ahead and remove the loose refs */ * we can go ahead and remove the loose refs */
if (packed_remove_loose(backend, &packing_list) < 0) if (packed_remove_loose(backend) < 0)
goto cleanup_memory; goto cleanup_memory;
{ git_sortedcache_unlock(backend->refcache);
struct stat st;
if (p_stat(pack_file_path.ptr, &st) == 0) /* update filestamp to latest value */
backend->refcache.packfile_time = st.st_mtime; if (git_futils_filestamp_check(
} &backend->refcache->stamp, pack_file_path.ptr) < 0)
giterr_clear();
git_vector_free(&packing_list);
git_buf_free(&pack_file_path); git_buf_free(&pack_file_path);
/* we're good now */ /* we're good now */
...@@ -1076,7 +908,7 @@ cleanup_packfile: ...@@ -1076,7 +908,7 @@ cleanup_packfile:
git_filebuf_cleanup(&pack_file); git_filebuf_cleanup(&pack_file);
cleanup_memory: cleanup_memory:
git_vector_free(&packing_list); git_sortedcache_unlock(backend->refcache);
git_buf_free(&pack_file_path); git_buf_free(&pack_file_path);
return -1; return -1;
...@@ -1087,11 +919,10 @@ static int refdb_fs_backend__write( ...@@ -1087,11 +919,10 @@ static int refdb_fs_backend__write(
const git_reference *ref, const git_reference *ref,
int force) int force)
{ {
refdb_fs_backend *backend; refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
int error; int error;
assert(_backend); assert(backend);
backend = (refdb_fs_backend *)_backend;
error = reference_path_available(backend, ref->name, NULL, force); error = reference_path_available(backend, ref->name, NULL, force);
if (error < 0) if (error < 0)
...@@ -1104,17 +935,13 @@ static int refdb_fs_backend__delete( ...@@ -1104,17 +935,13 @@ static int refdb_fs_backend__delete(
git_refdb_backend *_backend, git_refdb_backend *_backend,
const char *ref_name) const char *ref_name)
{ {
refdb_fs_backend *backend; refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
git_buf loose_path = GIT_BUF_INIT; git_buf loose_path = GIT_BUF_INIT;
struct packref *pack_ref; size_t pack_pos;
khiter_t pack_ref_pos;
int error = 0; int error = 0;
bool loose_deleted = 0; bool loose_deleted = 0;
assert(_backend); assert(backend && ref_name);
assert(ref_name);
backend = (refdb_fs_backend *)_backend;
/* If a loose reference exists, remove it from the filesystem */ /* If a loose reference exists, remove it from the filesystem */
if (git_buf_joinpath(&loose_path, backend->path, ref_name) < 0) if (git_buf_joinpath(&loose_path, backend->path, ref_name) < 0)
...@@ -1130,19 +957,23 @@ static int refdb_fs_backend__delete( ...@@ -1130,19 +957,23 @@ static int refdb_fs_backend__delete(
if (error != 0) if (error != 0)
return error; return error;
if (packed_reload(backend) < 0)
return -1;
/* If a packed reference exists, remove it from the packfile and repack */ /* If a packed reference exists, remove it from the packfile and repack */
error = packed_map_entry(&pack_ref, &pack_ref_pos, backend, ref_name); if (git_sortedcache_lock(backend->refcache) < 0)
return -1;
if (error == GIT_ENOTFOUND) if (!(error = git_sortedcache_lookup_index(
return loose_deleted ? 0 : GIT_ENOTFOUND; &pack_pos, backend->refcache, ref_name)))
error = git_sortedcache_remove(backend->refcache, pack_pos, false);
if (error == 0) { git_sortedcache_unlock(backend->refcache);
git_strmap_delete_at(backend->refcache.packfile, pack_ref_pos);
git__free(pack_ref);
error = packed_write(backend);
}
return error; if (error == GIT_ENOTFOUND)
return loose_deleted ? 0 : ref_error_notfound(ref_name);
return packed_write(backend);
} }
static int refdb_fs_backend__rename( static int refdb_fs_backend__rename(
...@@ -1152,53 +983,44 @@ static int refdb_fs_backend__rename( ...@@ -1152,53 +983,44 @@ static int refdb_fs_backend__rename(
const char *new_name, const char *new_name,
int force) int force)
{ {
refdb_fs_backend *backend; refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
git_reference *old, *new; git_reference *old, *new;
int error; int error;
assert(_backend); assert(backend);
backend = (refdb_fs_backend *)_backend;
error = reference_path_available(backend, new_name, old_name, force);
if (error < 0)
return error;
error = refdb_fs_backend__lookup(&old, _backend, old_name); if ((error = reference_path_available(
if (error < 0) backend, new_name, old_name, force)) < 0 ||
(error = refdb_fs_backend__lookup(&old, _backend, old_name)) < 0)
return error; return error;
error = refdb_fs_backend__delete(_backend, old_name); if ((error = refdb_fs_backend__delete(_backend, old_name)) < 0) {
if (error < 0) {
git_reference_free(old); git_reference_free(old);
return error; return error;
} }
new = realloc(old, sizeof(git_reference) + strlen(new_name) + 1); new = git_reference__set_name(old, new_name);
memcpy(new->name, new_name, strlen(new_name) + 1); if (!new) {
git_reference_free(old);
return -1;
}
error = loose_write(backend, new); if ((error = loose_write(backend, new)) < 0 || out == NULL) {
if (error < 0) {
git_reference_free(new); git_reference_free(new);
return error; return error;
} }
if (out) {
*out = new; *out = new;
} else {
git_reference_free(new);
}
return 0; return 0;
} }
static int refdb_fs_backend__compress(git_refdb_backend *_backend) static int refdb_fs_backend__compress(git_refdb_backend *_backend)
{ {
refdb_fs_backend *backend; refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
assert(_backend); assert(backend);
backend = (refdb_fs_backend *)_backend;
if (packed_load(backend) < 0 || /* load the existing packfile */ if (packed_reload(backend) < 0 || /* load the existing packfile */
packed_loadloose(backend) < 0 || /* add all the loose refs */ packed_loadloose(backend) < 0 || /* add all the loose refs */
packed_write(backend) < 0) /* write back to disk */ packed_write(backend) < 0) /* write back to disk */
return -1; return -1;
...@@ -1206,29 +1028,13 @@ static int refdb_fs_backend__compress(git_refdb_backend *_backend) ...@@ -1206,29 +1028,13 @@ static int refdb_fs_backend__compress(git_refdb_backend *_backend)
return 0; return 0;
} }
static void refcache_free(git_refcache *refs)
{
assert(refs);
if (refs->packfile) {
struct packref *reference;
git_strmap_foreach_value(refs->packfile, reference, {
git__free(reference);
});
git_strmap_free(refs->packfile);
}
}
static void refdb_fs_backend__free(git_refdb_backend *_backend) static void refdb_fs_backend__free(git_refdb_backend *_backend)
{ {
refdb_fs_backend *backend; refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
assert(_backend); assert(backend);
backend = (refdb_fs_backend *)_backend;
refcache_free(&backend->refcache); git_sortedcache_free(backend->refcache);
git__free(backend->path); git__free(backend->path);
git__free(backend); git__free(backend);
} }
...@@ -1252,7 +1058,7 @@ static int setup_namespace(git_buf *path, git_repository *repo) ...@@ -1252,7 +1058,7 @@ static int setup_namespace(git_buf *path, git_repository *repo)
if (parts == NULL) if (parts == NULL)
return -1; return -1;
/** /*
* From `man gitnamespaces`: * From `man gitnamespaces`:
* namespaces which include a / will expand to a hierarchy * namespaces which include a / will expand to a hierarchy
* of namespaces; for example, GIT_NAMESPACE=foo/bar will store * of namespaces; for example, GIT_NAMESPACE=foo/bar will store
...@@ -1269,7 +1075,7 @@ static int setup_namespace(git_buf *path, git_repository *repo) ...@@ -1269,7 +1075,7 @@ static int setup_namespace(git_buf *path, git_repository *repo)
if (git_futils_mkdir_r(git_buf_cstr(path), repo->path_repository, 0777) < 0) if (git_futils_mkdir_r(git_buf_cstr(path), repo->path_repository, 0777) < 0)
return -1; return -1;
/* Return the root of the namespaced path, i.e. without the trailing '/refs' */ /* Return root of the namespaced path, i.e. without the trailing '/refs' */
git_buf_rtruncate_at_char(path, '/'); git_buf_rtruncate_at_char(path, '/');
return 0; return 0;
} }
...@@ -1286,13 +1092,19 @@ int git_refdb_backend_fs( ...@@ -1286,13 +1092,19 @@ int git_refdb_backend_fs(
backend->repo = repository; backend->repo = repository;
if (setup_namespace(&path, repository) < 0) { if (setup_namespace(&path, repository) < 0)
git__free(backend); goto fail;
return -1;
}
backend->path = git_buf_detach(&path); backend->path = git_buf_detach(&path);
if (git_buf_joinpath(&path, backend->path, GIT_PACKEDREFS_FILE) < 0 ||
git_sortedcache_new(
&backend->refcache, offsetof(struct packref, name),
NULL, NULL, packref_cmp, git_buf_cstr(&path)) < 0)
goto fail;
git_buf_free(&path);
backend->parent.exists = &refdb_fs_backend__exists; backend->parent.exists = &refdb_fs_backend__exists;
backend->parent.lookup = &refdb_fs_backend__lookup; backend->parent.lookup = &refdb_fs_backend__lookup;
backend->parent.iterator = &refdb_fs_backend__iterator; backend->parent.iterator = &refdb_fs_backend__iterator;
...@@ -1304,4 +1116,10 @@ int git_refdb_backend_fs( ...@@ -1304,4 +1116,10 @@ int git_refdb_backend_fs(
*backend_out = (git_refdb_backend *)backend; *backend_out = (git_refdb_backend *)backend;
return 0; return 0;
fail:
git_buf_free(&path);
git__free(backend->path);
git__free(backend);
return -1;
} }
#include "clar_libgit2.h"
#include "git2/refdb.h"
#include "refdb.h"
static git_repository *g_repo;
static int g_expected = 0;
void test_threads_refdb__initialize(void)
{
g_repo = NULL;
}
void test_threads_refdb__cleanup(void)
{
cl_git_sandbox_cleanup();
g_repo = NULL;
}
#define REPEAT 20
#define THREADS 20
static void *iterate_refs(void *arg)
{
git_reference_iterator *i;
git_reference *ref;
int count = 0, *id = arg;
usleep(THREADS - *id);
cl_git_pass(git_reference_iterator_new(&i, g_repo));
for (count = 0; !git_reference_next(&ref, i); ++count) {
cl_assert(ref != NULL);
git_reference_free(ref);
}
if (g_expected > 0)
cl_assert_equal_i(g_expected, count);
git_reference_iterator_free(i);
return arg;
}
void test_threads_refdb__iterator(void)
{
int r, t;
git_thread th[THREADS];
int id[THREADS];
git_oid head;
git_reference *ref;
char name[128];
git_refdb *refdb;
g_repo = cl_git_sandbox_init("testrepo2");
cl_git_pass(git_reference_name_to_id(&head, g_repo, "HEAD"));
/* make a bunch of references */
for (r = 0; r < 200; ++r) {
snprintf(name, sizeof(name), "refs/heads/direct-%03d", r);
cl_git_pass(git_reference_create(&ref, g_repo, name, &head, 0));
git_reference_free(ref);
}
cl_git_pass(git_repository_refdb(&refdb, g_repo));
cl_git_pass(git_refdb_compress(refdb));
git_refdb_free(refdb);
g_expected = 206;
for (r = 0; r < REPEAT; ++r) {
g_repo = cl_git_sandbox_reopen(); /* reopen to flush caches */
for (t = 0; t < THREADS; ++t) {
id[t] = t;
cl_git_pass(git_thread_create(&th[t], NULL, iterate_refs, &id[t]));
}
for (t = 0; t < THREADS; ++t) {
cl_git_pass(git_thread_join(th[t], NULL));
}
memset(th, 0, sizeof(th));
}
}
static void *create_refs(void *arg)
{
int *id = arg, i;
git_oid head;
char name[128];
git_reference *ref[10];
cl_git_pass(git_reference_name_to_id(&head, g_repo, "HEAD"));
for (i = 0; i < 10; ++i) {
snprintf(name, sizeof(name), "refs/heads/thread-%03d-%02d", *id, i);
cl_git_pass(git_reference_create(&ref[i], g_repo, name, &head, 0));
if (i == 5) {
git_refdb *refdb;
cl_git_pass(git_repository_refdb(&refdb, g_repo));
cl_git_pass(git_refdb_compress(refdb));
git_refdb_free(refdb);
}
}
for (i = 0; i < 10; ++i)
git_reference_free(ref[i]);
return arg;
}
static void *delete_refs(void *arg)
{
int *id = arg, i;
git_reference *ref;
char name[128];
for (i = 0; i < 10; ++i) {
snprintf(
name, sizeof(name), "refs/heads/thread-%03d-%02d", (*id) & ~0x3, i);
if (!git_reference_lookup(&ref, g_repo, name)) {
fprintf(stderr, "deleting %s\n", name);
cl_git_pass(git_reference_delete(ref));
git_reference_delete(ref);
}
if (i == 5) {
git_refdb *refdb;
cl_git_pass(git_repository_refdb(&refdb, g_repo));
cl_git_pass(git_refdb_compress(refdb));
git_refdb_free(refdb);
}
}
return arg;
}
void test_threads_refdb__edit_while_iterate(void)
{
int r, t;
git_thread th[THREADS];
int id[THREADS];
git_oid head;
git_reference *ref;
char name[128];
git_refdb *refdb;
g_repo = cl_git_sandbox_init("testrepo2");
cl_git_pass(git_reference_name_to_id(&head, g_repo, "HEAD"));
/* make a bunch of references */
for (r = 0; r < 50; ++r) {
snprintf(name, sizeof(name), "refs/heads/starter-%03d", r);
cl_git_pass(git_reference_create(&ref, g_repo, name, &head, 0));
git_reference_free(ref);
}
cl_git_pass(git_repository_refdb(&refdb, g_repo));
cl_git_pass(git_refdb_compress(refdb));
git_refdb_free(refdb);
g_expected = -1;
g_repo = cl_git_sandbox_reopen(); /* reopen to flush caches */
for (t = 0; t < THREADS; ++t) {
void *(*fn)(void *arg);
switch (t & 0x3) {
case 0: fn = create_refs; break;
case 1: fn = delete_refs; break;
default: fn = iterate_refs; break;
}
id[t] = t;
cl_git_pass(git_thread_create(&th[t], NULL, fn, &id[t]));
}
for (t = 0; t < THREADS; ++t) {
cl_git_pass(git_thread_join(th[t], NULL));
}
memset(th, 0, sizeof(th));
for (t = 0; t < THREADS; ++t) {
id[t] = t;
cl_git_pass(git_thread_create(&th[t], NULL, iterate_refs, &id[t]));
}
for (t = 0; t < THREADS; ++t) {
cl_git_pass(git_thread_join(th[t], NULL));
}
}
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