Skip to content

Commit 988e7f5

Browse files
pks-tgitster
authored andcommitted
reftable/system: provide thin wrapper for lockfile subsystem
We use the lockfile subsystem to write lockfiles for "tables.list". As with the tempfile subsystem, the lockfile subsystem also hooks into our infrastructure to prune stale locks via atexit(3p) or signal handlers. Furthermore, the lockfile subsystem also handles locking timeouts, which do add quite a bit of logic. Having to reimplement that in the context of Git wouldn't make a whole lot of sense, and it is quite likely that downstream users of the reftable library may have a better idea for how exactly to implement timeouts. So again, provide a thin wrapper for the lockfile subsystem instead such that the compatibility shim is fully self-contained. Signed-off-by: Patrick Steinhardt <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 6361226 commit 988e7f5

File tree

8 files changed

+154
-37
lines changed

8 files changed

+154
-37
lines changed

reftable/stack.c

Lines changed: 27 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -657,7 +657,7 @@ static int format_name(struct reftable_buf *dest, uint64_t min, uint64_t max)
657657
}
658658

659659
struct reftable_addition {
660-
struct lock_file tables_list_lock;
660+
struct reftable_flock tables_list_lock;
661661
struct reftable_stack *stack;
662662

663663
char **new_tables;
@@ -676,10 +676,8 @@ static int reftable_stack_init_addition(struct reftable_addition *add,
676676

677677
add->stack = st;
678678

679-
err = hold_lock_file_for_update_timeout(&add->tables_list_lock,
680-
st->list_file,
681-
LOCK_NO_DEREF,
682-
st->opts.lock_timeout_ms);
679+
err = flock_acquire(&add->tables_list_lock, st->list_file,
680+
st->opts.lock_timeout_ms);
683681
if (err < 0) {
684682
if (errno == EEXIST) {
685683
err = REFTABLE_LOCK_ERROR;
@@ -689,7 +687,7 @@ static int reftable_stack_init_addition(struct reftable_addition *add,
689687
goto done;
690688
}
691689
if (st->opts.default_permissions) {
692-
if (chmod(get_lock_file_path(&add->tables_list_lock),
690+
if (chmod(add->tables_list_lock.path,
693691
st->opts.default_permissions) < 0) {
694692
err = REFTABLE_IO_ERROR;
695693
goto done;
@@ -733,7 +731,7 @@ static void reftable_addition_close(struct reftable_addition *add)
733731
add->new_tables_len = 0;
734732
add->new_tables_cap = 0;
735733

736-
rollback_lock_file(&add->tables_list_lock);
734+
flock_release(&add->tables_list_lock);
737735
reftable_buf_release(&nm);
738736
}
739737

@@ -749,7 +747,6 @@ void reftable_addition_destroy(struct reftable_addition *add)
749747
int reftable_addition_commit(struct reftable_addition *add)
750748
{
751749
struct reftable_buf table_list = REFTABLE_BUF_INIT;
752-
int lock_file_fd = get_lock_file_fd(&add->tables_list_lock);
753750
int err = 0;
754751
size_t i;
755752

@@ -767,20 +764,20 @@ int reftable_addition_commit(struct reftable_addition *add)
767764
goto done;
768765
}
769766

770-
err = write_in_full(lock_file_fd, table_list.buf, table_list.len);
767+
err = write_in_full(add->tables_list_lock.fd, table_list.buf, table_list.len);
771768
reftable_buf_release(&table_list);
772769
if (err < 0) {
773770
err = REFTABLE_IO_ERROR;
774771
goto done;
775772
}
776773

777-
err = stack_fsync(&add->stack->opts, lock_file_fd);
774+
err = stack_fsync(&add->stack->opts, add->tables_list_lock.fd);
778775
if (err < 0) {
779776
err = REFTABLE_IO_ERROR;
780777
goto done;
781778
}
782779

783-
err = commit_lock_file(&add->tables_list_lock);
780+
err = flock_commit(&add->tables_list_lock);
784781
if (err < 0) {
785782
err = REFTABLE_IO_ERROR;
786783
goto done;
@@ -1160,8 +1157,8 @@ static int stack_compact_range(struct reftable_stack *st,
11601157
struct reftable_buf new_table_name = REFTABLE_BUF_INIT;
11611158
struct reftable_buf new_table_path = REFTABLE_BUF_INIT;
11621159
struct reftable_buf table_name = REFTABLE_BUF_INIT;
1163-
struct lock_file tables_list_lock = LOCK_INIT;
1164-
struct lock_file *table_locks = NULL;
1160+
struct reftable_flock tables_list_lock = REFTABLE_FLOCK_INIT;
1161+
struct reftable_flock *table_locks = NULL;
11651162
struct reftable_tmpfile new_table = REFTABLE_TMPFILE_INIT;
11661163
int is_empty_table = 0, err = 0;
11671164
size_t first_to_replace, last_to_replace;
@@ -1179,10 +1176,7 @@ static int stack_compact_range(struct reftable_stack *st,
11791176
* Hold the lock so that we can read "tables.list" and lock all tables
11801177
* which are part of the user-specified range.
11811178
*/
1182-
err = hold_lock_file_for_update_timeout(&tables_list_lock,
1183-
st->list_file,
1184-
LOCK_NO_DEREF,
1185-
st->opts.lock_timeout_ms);
1179+
err = flock_acquire(&tables_list_lock, st->list_file, st->opts.lock_timeout_ms);
11861180
if (err < 0) {
11871181
if (errno == EEXIST)
11881182
err = REFTABLE_LOCK_ERROR;
@@ -1205,19 +1199,20 @@ static int stack_compact_range(struct reftable_stack *st,
12051199
* older process is still busy compacting tables which are preexisting
12061200
* from the point of view of the newer process.
12071201
*/
1208-
REFTABLE_CALLOC_ARRAY(table_locks, last - first + 1);
1202+
REFTABLE_ALLOC_ARRAY(table_locks, last - first + 1);
12091203
if (!table_locks) {
12101204
err = REFTABLE_OUT_OF_MEMORY_ERROR;
12111205
goto done;
12121206
}
1207+
for (i = 0; i < last - first + 1; i++)
1208+
table_locks[i] = REFTABLE_FLOCK_INIT;
12131209

12141210
for (i = last + 1; i > first; i--) {
12151211
err = stack_filename(&table_name, st, reader_name(st->readers[i - 1]));
12161212
if (err < 0)
12171213
goto done;
12181214

1219-
err = hold_lock_file_for_update(&table_locks[nlocks],
1220-
table_name.buf, LOCK_NO_DEREF);
1215+
err = flock_acquire(&table_locks[nlocks], table_name.buf, 0);
12211216
if (err < 0) {
12221217
/*
12231218
* When the table is locked already we may do a
@@ -1253,7 +1248,7 @@ static int stack_compact_range(struct reftable_stack *st,
12531248
* run into file descriptor exhaustion when we compress a lot
12541249
* of tables.
12551250
*/
1256-
err = close_lock_file_gently(&table_locks[nlocks++]);
1251+
err = flock_close(&table_locks[nlocks++]);
12571252
if (err < 0) {
12581253
err = REFTABLE_IO_ERROR;
12591254
goto done;
@@ -1265,7 +1260,7 @@ static int stack_compact_range(struct reftable_stack *st,
12651260
* "tables.list" lock while compacting the locked tables. This allows
12661261
* concurrent updates to the stack to proceed.
12671262
*/
1268-
err = rollback_lock_file(&tables_list_lock);
1263+
err = flock_release(&tables_list_lock);
12691264
if (err < 0) {
12701265
err = REFTABLE_IO_ERROR;
12711266
goto done;
@@ -1288,10 +1283,7 @@ static int stack_compact_range(struct reftable_stack *st,
12881283
* "tables.list". We'll then replace the compacted range of tables with
12891284
* the new table.
12901285
*/
1291-
err = hold_lock_file_for_update_timeout(&tables_list_lock,
1292-
st->list_file,
1293-
LOCK_NO_DEREF,
1294-
st->opts.lock_timeout_ms);
1286+
err = flock_acquire(&tables_list_lock, st->list_file, st->opts.lock_timeout_ms);
12951287
if (err < 0) {
12961288
if (errno == EEXIST)
12971289
err = REFTABLE_LOCK_ERROR;
@@ -1301,7 +1293,7 @@ static int stack_compact_range(struct reftable_stack *st,
13011293
}
13021294

13031295
if (st->opts.default_permissions) {
1304-
if (chmod(get_lock_file_path(&tables_list_lock),
1296+
if (chmod(tables_list_lock.path,
13051297
st->opts.default_permissions) < 0) {
13061298
err = REFTABLE_IO_ERROR;
13071299
goto done;
@@ -1456,22 +1448,22 @@ static int stack_compact_range(struct reftable_stack *st,
14561448
goto done;
14571449
}
14581450

1459-
err = write_in_full(get_lock_file_fd(&tables_list_lock),
1451+
err = write_in_full(tables_list_lock.fd,
14601452
tables_list_buf.buf, tables_list_buf.len);
14611453
if (err < 0) {
14621454
err = REFTABLE_IO_ERROR;
14631455
unlink(new_table_path.buf);
14641456
goto done;
14651457
}
14661458

1467-
err = stack_fsync(&st->opts, get_lock_file_fd(&tables_list_lock));
1459+
err = stack_fsync(&st->opts, tables_list_lock.fd);
14681460
if (err < 0) {
14691461
err = REFTABLE_IO_ERROR;
14701462
unlink(new_table_path.buf);
14711463
goto done;
14721464
}
14731465

1474-
err = commit_lock_file(&tables_list_lock);
1466+
err = flock_commit(&tables_list_lock);
14751467
if (err < 0) {
14761468
err = REFTABLE_IO_ERROR;
14771469
unlink(new_table_path.buf);
@@ -1492,22 +1484,21 @@ static int stack_compact_range(struct reftable_stack *st,
14921484
* readers, so it is expected that unlinking tables may fail.
14931485
*/
14941486
for (i = 0; i < nlocks; i++) {
1495-
struct lock_file *table_lock = &table_locks[i];
1496-
const char *lock_path = get_lock_file_path(table_lock);
1487+
struct reftable_flock *table_lock = &table_locks[i];
14971488

14981489
reftable_buf_reset(&table_name);
1499-
err = reftable_buf_add(&table_name, lock_path,
1500-
strlen(lock_path) - strlen(".lock"));
1490+
err = reftable_buf_add(&table_name, table_lock->path,
1491+
strlen(table_lock->path) - strlen(".lock"));
15011492
if (err)
15021493
continue;
15031494

15041495
unlink(table_name.buf);
15051496
}
15061497

15071498
done:
1508-
rollback_lock_file(&tables_list_lock);
1499+
flock_release(&tables_list_lock);
15091500
for (i = 0; table_locks && i < nlocks; i++)
1510-
rollback_lock_file(&table_locks[i]);
1501+
flock_release(&table_locks[i]);
15111502
reftable_free(table_locks);
15121503

15131504
tmpfile_delete(&new_table);

reftable/system.c

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "system.h"
22
#include "basics.h"
33
#include "reftable-error.h"
4+
#include "../lockfile.h"
45
#include "../tempfile.h"
56

67
int tmpfile_from_pattern(struct reftable_tmpfile *out, const char *pattern)
@@ -47,3 +48,79 @@ int tmpfile_rename(struct reftable_tmpfile *t, const char *path)
4748
return REFTABLE_IO_ERROR;
4849
return 0;
4950
}
51+
52+
int flock_acquire(struct reftable_flock *l, const char *target_path,
53+
long timeout_ms)
54+
{
55+
struct lock_file *lockfile;
56+
int err;
57+
58+
lockfile = reftable_malloc(sizeof(*lockfile));
59+
if (!lockfile)
60+
return REFTABLE_OUT_OF_MEMORY_ERROR;
61+
62+
err = hold_lock_file_for_update_timeout(lockfile, target_path, LOCK_NO_DEREF,
63+
timeout_ms);
64+
if (err < 0) {
65+
reftable_free(lockfile);
66+
if (errno == EEXIST)
67+
return REFTABLE_LOCK_ERROR;
68+
return -1;
69+
}
70+
71+
l->fd = get_lock_file_fd(lockfile);
72+
l->path = get_lock_file_path(lockfile);
73+
l->priv = lockfile;
74+
75+
return 0;
76+
}
77+
78+
int flock_close(struct reftable_flock *l)
79+
{
80+
struct lock_file *lockfile = l->priv;
81+
int ret;
82+
83+
if (!lockfile)
84+
return REFTABLE_API_ERROR;
85+
86+
ret = close_lock_file_gently(lockfile);
87+
l->fd = -1;
88+
if (ret < 0)
89+
return REFTABLE_IO_ERROR;
90+
91+
return 0;
92+
}
93+
94+
int flock_release(struct reftable_flock *l)
95+
{
96+
struct lock_file *lockfile = l->priv;
97+
int ret;
98+
99+
if (!lockfile)
100+
return 0;
101+
102+
ret = rollback_lock_file(lockfile);
103+
reftable_free(lockfile);
104+
*l = REFTABLE_FLOCK_INIT;
105+
if (ret < 0)
106+
return REFTABLE_IO_ERROR;
107+
108+
return 0;
109+
}
110+
111+
int flock_commit(struct reftable_flock *l)
112+
{
113+
struct lock_file *lockfile = l->priv;
114+
int ret;
115+
116+
if (!lockfile)
117+
return REFTABLE_API_ERROR;
118+
119+
ret = commit_lock_file(lockfile);
120+
reftable_free(lockfile);
121+
*l = REFTABLE_FLOCK_INIT;
122+
if (ret < 0)
123+
return REFTABLE_IO_ERROR;
124+
125+
return 0;
126+
}

reftable/system.h

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ license that can be found in the LICENSE file or at
1212
/* This header glues the reftable library to the rest of Git */
1313

1414
#include "git-compat-util.h"
15-
#include "lockfile.h"
1615

1716
/*
1817
* An implementation-specific temporary file. By making this specific to the
@@ -55,4 +54,48 @@ int tmpfile_delete(struct reftable_tmpfile *t);
5554
*/
5655
int tmpfile_rename(struct reftable_tmpfile *t, const char *path);
5756

57+
/*
58+
* An implementation-specific file lock. Same as with `reftable_tmpfile`,
59+
* making this specific to the implementation makes it possible to tie this
60+
* into signal or atexit handlers such that we know to clean up stale locks on
61+
* abnormal exits.
62+
*/
63+
struct reftable_flock {
64+
const char *path;
65+
int fd;
66+
void *priv;
67+
};
68+
#define REFTABLE_FLOCK_INIT ((struct reftable_flock){ .fd = -1, })
69+
70+
/*
71+
* Acquire the lock for the given target path by exclusively creating a file
72+
* with ".lock" appended to it. If that lock exists, we wait up to `timeout_ms`
73+
* to acquire the lock. If `timeout_ms` is 0 we don't wait, if it is negative
74+
* we block indefinitely.
75+
*
76+
* Retrun 0 on success, a reftable error code on error.
77+
*/
78+
int flock_acquire(struct reftable_flock *l, const char *target_path,
79+
long timeout_ms);
80+
81+
/*
82+
* Close the lockfile's file descriptor without removing the lock itself. This
83+
* is a no-op in case the lockfile has already been closed beforehand. Returns
84+
* 0 on success, a reftable error code on error.
85+
*/
86+
int flock_close(struct reftable_flock *l);
87+
88+
/*
89+
* Release the lock by unlinking the lockfile. This is a no-op in case the
90+
* lockfile has already been released or committed beforehand. Returns 0 on
91+
* success, a reftable error code on error.
92+
*/
93+
int flock_release(struct reftable_flock *l);
94+
95+
/*
96+
* Commit the lock by renaming the lockfile into place. Returns 0 on success, a
97+
* reftable error code on error.
98+
*/
99+
int flock_commit(struct reftable_flock *l);
100+
58101
#endif

t/unit-tests/lib-reftable.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include "test-lib.h"
33
#include "reftable/constants.h"
44
#include "reftable/writer.h"
5+
#include "strbuf.h"
56

67
void t_reftable_set_hash(uint8_t *p, int i, enum reftable_hash id)
78
{

t/unit-tests/t-reftable-block.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ license that can be found in the LICENSE file or at
1111
#include "reftable/blocksource.h"
1212
#include "reftable/constants.h"
1313
#include "reftable/reftable-error.h"
14+
#include "strbuf.h"
1415

1516
static void t_ref_block_read_write(void)
1617
{

t/unit-tests/t-reftable-pq.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ license that can be found in the LICENSE file or at
99
#include "test-lib.h"
1010
#include "reftable/constants.h"
1111
#include "reftable/pq.h"
12+
#include "strbuf.h"
1213

1314
static void merged_iter_pqueue_check(const struct merged_iter_pqueue *pq)
1415
{

t/unit-tests/t-reftable-readwrite.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ license that can be found in the LICENSE file or at
1313
#include "reftable/reader.h"
1414
#include "reftable/reftable-error.h"
1515
#include "reftable/reftable-writer.h"
16+
#include "strbuf.h"
1617

1718
static const int update_index = 5;
1819

t/unit-tests/t-reftable-stack.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ license that can be found in the LICENSE file or at
1313
#include "reftable/reader.h"
1414
#include "reftable/reftable-error.h"
1515
#include "reftable/stack.h"
16+
#include "strbuf.h"
17+
#include "tempfile.h"
1618
#include <dirent.h>
1719

1820
static void clear_dir(const char *dirname)

0 commit comments

Comments
 (0)