Commit 5340d63d by Carlos Martín Nieto

config: perform unlocking via git_transaction

This makes the API for commiting or discarding changes the same as for
references.
parent 36f784b5
...@@ -9,11 +9,10 @@ v0.23 + 1 ...@@ -9,11 +9,10 @@ v0.23 + 1
### API additions ### API additions
* `git_config_lock()` and `git_config_unlock()` have been added, which * `git_config_lock()` has been added, which allow for
allow for transactional/atomic complex updates to the configuration, transactional/atomic complex updates to the configuration, removing
removing the opportunity for concurrent operations and not the opportunity for concurrent operations and not committing any
committing any changes until the unlock. changes until the unlock.
### API removals ### API removals
......
...@@ -696,25 +696,16 @@ GIT_EXTERN(int) git_config_backend_foreach_match( ...@@ -696,25 +696,16 @@ GIT_EXTERN(int) git_config_backend_foreach_match(
* updates made after locking will not be visible to a reader until * updates made after locking will not be visible to a reader until
* the file is unlocked. * the file is unlocked.
* *
* @param cfg the configuration in which to lock * You can apply the changes by calling `git_transaction_commit()`
* @return 0 or an error code * before freeing the transaction. Either of these actions will unlock
*/ * the config.
GIT_EXTERN(int) git_config_lock(git_config *cfg);
/**
* Unlock the backend with the highest priority
* *
* Unlocking will allow other writers to updat the configuration * @param tx the resulting transaction, use this to commit or undo the
* file. Optionally, any changes performed since the lock will be * changes
* applied to the configuration. * @param cfg the configuration in which to lock
*
* @param cfg the configuration
* @param commit boolean which indicates whether to commit any changes
* done since locking
* @return 0 or an error code * @return 0 or an error code
*/ */
GIT_EXTERN(int) git_config_unlock(git_config *cfg, int commit); GIT_EXTERN(int) git_config_lock(git_transaction **tx, git_config *cfg);
/** @} */ /** @} */
GIT_END_DECL GIT_END_DECL
......
...@@ -1144,8 +1144,9 @@ int git_config_open_default(git_config **out) ...@@ -1144,8 +1144,9 @@ int git_config_open_default(git_config **out)
return error; return error;
} }
int git_config_lock(git_config *cfg) int git_config_lock(git_transaction **out, git_config *cfg)
{ {
int error;
git_config_backend *file; git_config_backend *file;
file_internal *internal; file_internal *internal;
...@@ -1156,7 +1157,10 @@ int git_config_lock(git_config *cfg) ...@@ -1156,7 +1157,10 @@ int git_config_lock(git_config *cfg)
} }
file = internal->file; file = internal->file;
return file->lock(file); if ((error = file->lock(file)) < 0)
return error;
return git_transaction_config_new(out, cfg);
} }
int git_config_unlock(git_config *cfg, int commit) int git_config_unlock(git_config *cfg, int commit)
......
...@@ -88,4 +88,19 @@ extern int git_config__cvar( ...@@ -88,4 +88,19 @@ extern int git_config__cvar(
*/ */
int git_config_lookup_map_enum(git_cvar_t *type_out, const char **str_out, int git_config_lookup_map_enum(git_cvar_t *type_out, const char **str_out,
const git_cvar_map *maps, size_t map_n, int enum_val); const git_cvar_map *maps, size_t map_n, int enum_val);
/**
* Unlock the backend with the highest priority
*
* Unlocking will allow other writers to updat the configuration
* file. Optionally, any changes performed since the lock will be
* applied to the configuration.
*
* @param cfg the configuration
* @param commit boolean which indicates whether to commit any changes
* done since locking
* @return 0 or an error code
*/
GIT_EXTERN(int) git_config_unlock(git_config *cfg, int commit);
#endif #endif
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "pool.h" #include "pool.h"
#include "reflog.h" #include "reflog.h"
#include "signature.h" #include "signature.h"
#include "config.h"
#include "git2/transaction.h" #include "git2/transaction.h"
#include "git2/signature.h" #include "git2/signature.h"
...@@ -20,6 +21,12 @@ ...@@ -20,6 +21,12 @@
GIT__USE_STRMAP GIT__USE_STRMAP
typedef enum {
TRANSACTION_NONE,
TRANSACTION_REFS,
TRANSACTION_CONFIG,
} transaction_t;
typedef struct { typedef struct {
const char *name; const char *name;
void *payload; void *payload;
...@@ -39,13 +46,29 @@ typedef struct { ...@@ -39,13 +46,29 @@ typedef struct {
} transaction_node; } transaction_node;
struct git_transaction { struct git_transaction {
transaction_t type;
git_repository *repo; git_repository *repo;
git_refdb *db; git_refdb *db;
git_config *cfg;
git_strmap *locks; git_strmap *locks;
git_pool pool; git_pool pool;
}; };
int git_transaction_config_new(git_transaction **out, git_config *cfg)
{
git_transaction *tx;
assert(out && cfg);
tx = git__calloc(1, sizeof(git_transaction));
GITERR_CHECK_ALLOC(tx);
tx->type = TRANSACTION_CONFIG;
tx->cfg = cfg;
*out = tx;
return 0;
}
int git_transaction_new(git_transaction **out, git_repository *repo) int git_transaction_new(git_transaction **out, git_repository *repo)
{ {
int error; int error;
...@@ -71,6 +94,7 @@ int git_transaction_new(git_transaction **out, git_repository *repo) ...@@ -71,6 +94,7 @@ int git_transaction_new(git_transaction **out, git_repository *repo)
if ((error = git_repository_refdb(&tx->db, repo)) < 0) if ((error = git_repository_refdb(&tx->db, repo)) < 0)
goto on_error; goto on_error;
tx->type = TRANSACTION_REFS;
memcpy(&tx->pool, &pool, sizeof(git_pool)); memcpy(&tx->pool, &pool, sizeof(git_pool));
tx->repo = repo; tx->repo = repo;
*out = tx; *out = tx;
...@@ -305,6 +329,14 @@ int git_transaction_commit(git_transaction *tx) ...@@ -305,6 +329,14 @@ int git_transaction_commit(git_transaction *tx)
assert(tx); assert(tx);
if (tx->type == TRANSACTION_CONFIG) {
error = git_config_unlock(tx->cfg, true);
git_config_free(tx->cfg);
tx->cfg = NULL;
return error;
}
for (pos = kh_begin(tx->locks); pos < kh_end(tx->locks); pos++) { for (pos = kh_begin(tx->locks); pos < kh_end(tx->locks); pos++) {
if (!git_strmap_has_data(tx->locks, pos)) if (!git_strmap_has_data(tx->locks, pos))
continue; continue;
...@@ -332,6 +364,16 @@ void git_transaction_free(git_transaction *tx) ...@@ -332,6 +364,16 @@ void git_transaction_free(git_transaction *tx)
assert(tx); assert(tx);
if (tx->type == TRANSACTION_CONFIG) {
if (tx->cfg) {
git_config_unlock(tx->cfg, false);
git_config_free(tx->cfg);
}
git__free(tx);
return;
}
/* start by unlocking the ones we've left hanging, if any */ /* start by unlocking the ones we've left hanging, if any */
for (pos = kh_begin(tx->locks); pos < kh_end(tx->locks); pos++) { for (pos = kh_begin(tx->locks); pos < kh_end(tx->locks); pos++) {
if (!git_strmap_has_data(tx->locks, pos)) if (!git_strmap_has_data(tx->locks, pos))
......
/*
* Copyright (C) the libgit2 contributors. All rights reserved.
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#ifndef INCLUDE_transaction_h__
#define INCLUDE_transaction_h__
#include "common.h"
int git_transaction_config_new(git_transaction **out, git_config *cfg);
#endif
...@@ -637,6 +637,7 @@ void test_config_write__locking(void) ...@@ -637,6 +637,7 @@ void test_config_write__locking(void)
{ {
git_config *cfg, *cfg2; git_config *cfg, *cfg2;
git_config_entry *entry; git_config_entry *entry;
git_transaction *tx;
const char *filename = "locked-file"; const char *filename = "locked-file";
/* Open the config and lock it */ /* Open the config and lock it */
...@@ -645,7 +646,7 @@ void test_config_write__locking(void) ...@@ -645,7 +646,7 @@ void test_config_write__locking(void)
cl_git_pass(git_config_get_entry(&entry, cfg, "section.name")); cl_git_pass(git_config_get_entry(&entry, cfg, "section.name"));
cl_assert_equal_s("value", entry->value); cl_assert_equal_s("value", entry->value);
git_config_entry_free(entry); git_config_entry_free(entry);
cl_git_pass(git_config_lock(cfg)); cl_git_pass(git_config_lock(&tx, cfg));
/* Change entries in the locked backend */ /* Change entries in the locked backend */
cl_git_pass(git_config_set_string(cfg, "section.name", "other value")); cl_git_pass(git_config_set_string(cfg, "section.name", "other value"));
...@@ -665,8 +666,8 @@ void test_config_write__locking(void) ...@@ -665,8 +666,8 @@ void test_config_write__locking(void)
git_config_entry_free(entry); git_config_entry_free(entry);
cl_git_fail_with(GIT_ENOTFOUND, git_config_get_entry(&entry, cfg, "section2.name3")); cl_git_fail_with(GIT_ENOTFOUND, git_config_get_entry(&entry, cfg, "section2.name3"));
git_config_unlock(cfg, true); cl_git_pass(git_transaction_commit(tx));
git_config_free(cfg); git_transaction_free(tx);
/* Now that we've unlocked it, we should see both updates */ /* Now that we've unlocked it, we should see both updates */
cl_git_pass(git_config_open_ondisk(&cfg, filename)); cl_git_pass(git_config_open_ondisk(&cfg, filename));
......
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