Skip to content

Commit

Permalink
ConfigFile: Read async on changes
Browse files Browse the repository at this point in the history
Except on initial creation, do reading the config file on a thread job
if the thread pool is available to avoid stalling the main thread on I/O
issues.
  • Loading branch information
Zirias committed Oct 8, 2024
1 parent 8ba637c commit b93f12e
Showing 1 changed file with 93 additions and 38 deletions.
131 changes: 93 additions & 38 deletions src/bin/xmoji/configfile.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,23 @@

#define TMPSUFX ".tmp"

static char buf[8192];
typedef struct ReadContext
{
ConfigFile *configFile;
PSC_ThreadJob *job;
int rc;
int checkchanges;
char buf[8192];
char *vals[];
} ReadContext;

struct ConfigFile
{
const char *path;
FileWatcher *watcher;
PSC_Event *changed;
const char **keys;
ReadContext *readContext;
size_t nkeys;
int dirty;
char *vals[];
Expand All @@ -36,7 +45,7 @@ static size_t keyidx(const ConfigFile *self, const char *key)
return i;
}

static int parseline(const char **key, const char **val)
static int parseline(char *buf, const char **key, const char **val)
{
char *p = buf;
while (*p == ' ' || *p == '\t') ++p;
Expand All @@ -55,62 +64,107 @@ static int parseline(const char **key, const char **val)
return 1;
}

static int doread(ConfigFile *self, int checkchanges)
static void readjob(void *arg)
{
int rc = -1;
FILE *f = fopen(self->path, "r");
if (!f) return rc;
char **newvals = PSC_malloc(self->nkeys * sizeof *newvals);
memset(newvals, 0, self->nkeys * sizeof *newvals);
while (fgets(buf, sizeof buf, f))
ReadContext *ctx = arg;
FILE *f = fopen(ctx->configFile->path, "r");
if (!f || (ctx->job && PSC_ThreadJob_canceled())) goto done;
while (fgets(ctx->buf, sizeof ctx->buf, f))
{
if (ctx->job && PSC_ThreadJob_canceled()) goto done;
const char *key;
const char *val;
if (parseline(&key, &val))
if (parseline(ctx->buf, &key, &val))
{
size_t i = keyidx(self, key);
if (i < self->nkeys)
size_t i = keyidx(ctx->configFile, key);
if (i < ctx->configFile->nkeys)
{
free(newvals[i]);
newvals[i] = PSC_copystr(val);
free(ctx->vals[i]);
ctx->vals[i] = PSC_copystr(val);
}
}
}
if (ctx->job && PSC_ThreadJob_canceled()) goto done;
if (ferror(f)) goto done;
rc = 0;
for (size_t i = 0; i < self->nkeys; ++i)
ctx->rc = 0;
done:
fclose(f);
}

static void readjobfinished(void *receiver, void *sender, void *args)
{
ConfigFile *self = receiver;
PSC_ThreadJob *job = sender;
ReadContext *ctx = args;

if ((!job || PSC_ThreadJob_hasCompleted(job)) && ctx->rc == 0)
{
int changed = 0;
if (newvals[i])
for (size_t i = 0; i < self->nkeys; ++i)
{
if (!self->vals[i])
int changed = 0;
if (ctx->vals[i])
{
self->vals[i] = newvals[i];
changed = 1;
if (!self->vals[i])
{
self->vals[i] = ctx->vals[i];
ctx->vals[i] = 0;
changed = 1;
}
else if (strcmp(ctx->vals[i], self->vals[i]))
{
free(self->vals[i]);
self->vals[i] = ctx->vals[i];
ctx->vals[i] = 0;
changed = 1;
}
}
else if (strcmp(newvals[i], self->vals[i]))
else if (self->vals[i])
{
free(self->vals[i]);
self->vals[i] = newvals[i];
self->vals[i] = 0;
changed = 1;
}
else free(newvals[i]);
}
else if (self->vals[i])
{
free(self->vals[i]);
self->vals[i] = 0;
changed = 1;
}
if (checkchanges && changed)
{
ConfigFileChangedEventArgs args = { self->keys[i] };
PSC_Event_raise(self->changed, 0, &args);
if (ctx->checkchanges && changed)
{
ConfigFileChangedEventArgs ea = { self->keys[i] };
PSC_Event_raise(self->changed, 0, &ea);
}
}
}
done:
free(newvals);
fclose(f);

if (self->readContext == ctx) self->readContext = 0;
for (size_t i = 0; i < self->nkeys; ++i) free(ctx->vals[i]);
free(ctx);
}

static int doread(ConfigFile *self, int checkchanges)
{
if (self->readContext)
{
PSC_ThreadPool_cancel(self->readContext->job);
}

self->readContext = PSC_malloc(sizeof *self->readContext
+ self->nkeys * sizeof *self->readContext->vals);
memset(self->readContext, 0, sizeof *self->readContext
+ self->nkeys * sizeof *self->readContext->vals);
self->readContext->configFile = self;
self->readContext->rc = -1;
self->readContext->checkchanges = checkchanges;

if (checkchanges && PSC_ThreadPool_active())
{
self->readContext->job = PSC_ThreadJob_create(readjob,
self->readContext, 0);
PSC_Event_register(PSC_ThreadJob_finished(self->readContext->job),
self, readjobfinished, 0);
PSC_ThreadPool_enqueue(self->readContext->job);
return 0;
}

readjob(self->readContext);
int rc = self->readContext->rc;
readjobfinished(self, 0, self->readContext);
return rc;
}

Expand Down Expand Up @@ -161,6 +215,7 @@ ConfigFile *ConfigFile_create(const char *path, size_t nkeys,
self->watcher = FileWatcher_create(path);
self->changed = PSC_Event_create(self);
self->keys = keys;
self->readContext = 0;
self->nkeys = nkeys;
memset(self->vals, 0, nkeys * sizeof *self->vals);
PSC_Event_register(FileWatcher_changed(self->watcher), self,
Expand Down

0 comments on commit b93f12e

Please sign in to comment.