Add locking to prevent race condition from destroying data when multiple processes access the same structure, fix a memory leak

master
teknomunk 11 months ago
parent 819e69f095
commit b3b893b2b0

@ -3,6 +3,8 @@
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/file.h>
int fs_list_get( const char* filename )
{
@ -20,12 +22,42 @@ int fs_list_get( const char* filename )
}
void fs_list_set( const char* path, int value )
{
char buffer[512];
snprintf( buffer,sizeof(buffer), "%s.lock", path );
int fd = open( buffer, O_CREAT );
flock( fd, LOCK_EX );
char tmp_filename[512];
snprintf( tmp_filename, 512, "%s.tmp", path );
FILE* f = fopen(tmp_filename,"w");
fprintf( f, "%d", value );
fclose(f);
rename( tmp_filename, path );
flock( fd, LOCK_UN );
close(fd);
}
int fs_list_inc( const char* path )
{
char buffer[512];
snprintf( buffer,sizeof(buffer), "%s.lock", path );
int fd = open( buffer, O_CREAT );
flock( fd, LOCK_EX );
int value = fs_list_get( path ) + 1;
char tmp_filename[512];
snprintf( tmp_filename, 512, "%s.tmp", path );
FILE* f = fopen(tmp_filename,"w");
fprintf( f, "%d", value );
fclose(f);
rename( tmp_filename, path );
flock( fd, LOCK_UN );
close(fd);
return value;
}

@ -1,5 +1,6 @@
#pragma once
int fs_list_get( const char* path );
int fs_list_inc( const char* path );
void fs_list_set( const char* path, int value );

@ -7,7 +7,9 @@
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/file.h>
//#define DEBUG
@ -235,12 +237,19 @@ enum {
trie_entry_set_result_deleted_existing = 3,
trie_entry_set_result_no_change = 4,
};
static int trie_entry_set( struct trie_entry* e, const char* key, const char* value )
static int trie_entry_set( struct trie_entry* e, const char* key, const char* value, int parent_count )
{
int prefix_len = 0;
struct edge* ed;
int key_length = strlen(key);
int this_count = 0;
for( int i = 0; i < e->edges.count; ++i ) {
ed = &e->edges.items[i];
this_count += ed->count;
}
DEBUG_printf( "root this_count = %d, parent_count = %d\n", this_count, parent_count );
DEBUG_printf( "key: %s\n", key );
for( int i = 0; i < e->edges.count; ++i ) {
ed = &e->edges.items[i];
@ -264,10 +273,11 @@ static int trie_entry_set( struct trie_entry* e, const char* key, const char* va
goto add_new_edge;
add_new_edge:
DEBUG_printf( "add_new_edge %s\n", key );
if( !value ) {
DEBUG_printf( "value is null, not adding\n" );
return trie_entry_set_result_no_change;
}
DEBUG_printf( "add_new_edge %s\n", key );
{
e->edges.count += 1;
e->edges.items = realloc( e->edges.items, sizeof(struct edge) * e->edges.count );
@ -288,6 +298,9 @@ update_existing:
}
ed->value = strdup(value);
trie_entry_save_to_file( e->filename, e );
if( this_count > parent_count ) {
DEBUG_printf( "size mismatch detected. TODO: correct\n" );
}
return trie_entry_set_result_updated_existing;
delete_existing:
@ -316,12 +329,12 @@ traverse_existing_edge:
char filename[512];
snprintf( filename, sizeof(filename), "%s%s", e->filename, ed->label );
DEBUG_printf( "Traversing down %s (filename=%s) key=%s, remaining=%s\n", ed->label, filename, key, &key[prefix_len] );
DEBUG_printf( "Traversing down %s (filename=%s) key=%s, remaining=%s, count=%d\n", ed->label, filename, key, &key[prefix_len], ed->count );
struct trie_entry branch;
memset(&branch,0,sizeof(branch));
trie_entry_load_from_file( filename, &branch );
int result = trie_entry_set( &branch, &key[prefix_len], value );
int result = trie_entry_set( &branch, &key[prefix_len], value, this_count );
DEBUG_printf( "result=%d\n", result );
// update count
@ -347,6 +360,15 @@ traverse_existing_edge:
}
trie_entry_free_composite( &branch );
DEBUG_printf( "Traverse result = %d\n", result );
/*
if( parent_count > this_count ) {
if( result == trie_entry_set_result_updated_existing ) {
DEBUG_printf( "Fixing up parent count parent_count = %d, this_count = %d\n", parent_count, this_count );
return trie_entry_set_result_deleted_existing;
}
}
*/
return result;
split_existing_edge:
@ -408,14 +430,24 @@ static struct trie_entry* load_root_node( const char* filename )
bool ffdb_trie_set( const char* filename, const char* key, const char* value )
{
DEBUG_printf( "ffdb_trie_set( filename = '%s', key = '%s', value = '%s' )\n", filename, key, value );
struct trie_entry* root = NULL;
char* key_escaped = escape(key);
bool result = false;
mkdir( filename, 0755 );
char buffer[512];
snprintf( buffer,sizeof(buffer), "%s/.lock", filename );
int fd = open( buffer, O_CREAT );
flock( fd, LOCK_EX );
root = load_root_node(filename);
int res = trie_entry_set( root, key_escaped, value );
int this_count = 0;
for( int i = 0; i < root->edges.count; ++i ) {
this_count += root->edges.items[i].count;
}
int res = trie_entry_set( root, key_escaped, value, this_count );
if( !res ) {
printf( "Failed to set %s to %s\n", key, value );
goto failed;
@ -428,6 +460,8 @@ bool ffdb_trie_set( const char* filename, const char* key, const char* value )
cleanup:
free(key_escaped);
trie_entry_free(root);
flock( fd, LOCK_UN );
close(fd);
return result;
failed:
result = false;
@ -551,14 +585,18 @@ struct string_array {
static void load_items( struct trie_entry* e, int offset, int limit, struct string_array* keys, struct string_array* values )
{
DEBUG_printf( "Looking at edges in '%s'\n", e->prefix );
for( int i = 0; i < e->edges.count; ++i ) {
struct edge* ed = &e->edges.items[i];
DEBUG_printf( "ed = { .label = '%s', .value = '%s', .count = %d }\n", ed->label, ed->value, ed->count );
if( ed->count <= offset ) {
printf( "Skipping all for '%s' offset=%d\n", e->prefix, offset );
// branch - skip all
offset -= ed->count;
} else if( ed->count > 1 ) {
// branch - skip part, include part
int branch_count = ed->count - offset;
DEBUG_printf( "Skipping part of '%s', offset=%d, branch_count=%d, limit=%d\n", e->prefix, offset, branch_count, limit );
if( branch_count > limit ) {
branch_count = limit;
}
@ -577,6 +615,7 @@ static void load_items( struct trie_entry* e, int offset, int limit, struct stri
limit -= branch_count;
} else if( limit > 0 ) {
// leaf - include
DEBUG_printf( "Loading from '%s', offset=%d\n", e->prefix, offset );
if( keys ) {
char* str = aformat( "%s%s", e->prefix, ed->label );
array_append( keys, sizeof(str), &str );
@ -588,6 +627,7 @@ static void load_items( struct trie_entry* e, int offset, int limit, struct stri
limit -= 1;
}
}
DEBUG_printf( "Out of edges in '%s'\n", e->prefix );
}
void ffdb_trie_list( const char* filename, int offset, int limit, void* keys_ptr, void* values_ptr )
@ -599,28 +639,34 @@ void ffdb_trie_list( const char* filename, int offset, int limit, void* keys_pt
}
bool ffdb_trie_get_index( const char* filename, int offset, char** key, char** value )
{
bool result = false;
/// Convenience function to get single item at offset
struct string_array keys;
struct string_array values;
memset(&keys,0,sizeof(keys));
memset(&values,0,sizeof(values));
ffdb_trie_list( filename, offset, 1, key, value ? &values : NULL );
ffdb_trie_list( filename, offset, 1, &keys, value ? &values : NULL );
if( keys.count == 0 ) {
return false;
}
if( key ) {
*key = keys.items[0];
result = false;
} else {
free( keys.items[0] );
}
if( value ) {
*value = values.items[0];
result = true;
if( key ) {
*key = keys.items[0];
} else {
free( keys.items[0] );
}
if( value ) {
*value = values.items[0];
} else {
free( values.items[0] );
}
}
free(keys.items);
free(values.items);
return true;
return result;
}

Loading…
Cancel
Save