Generalize ef_extend() to non-file-backed tables

ef_extend() extended the file bypassing the cache, which screws up the
cache if it's EFF_MEM.  It fixed that by closing and reopening the
table.  Cheesy, and worked only for file-backed tables.

Rewrite ef_extend() to remap the cache properly for EFF_MEM.  While
there, simplify the !EFF_MEM case: steal a cache slot instead of
allocating a buffer.

Factor cache mapping out of ef_open() and ef_extend() into new
ef_realloc_cache().
This commit is contained in:
Markus Armbruster 2008-02-21 21:12:58 +01:00
parent f8dcb7b07b
commit 2eb8672b5e

View file

@ -46,8 +46,10 @@
#include "nsc.h" #include "nsc.h"
#include "prototypes.h" #include "prototypes.h"
static int ef_realloc_cache(struct empfile *, int);
static int fillcache(struct empfile *, int); static int fillcache(struct empfile *, int);
static int do_write(struct empfile *, void *, int, int); static int do_write(struct empfile *, void *, int, int);
static void do_blank(struct empfile *, void *, int, int);
/* /*
* Open the file-backed table TYPE (EF_SECTOR, ...). * Open the file-backed table TYPE (EF_SECTOR, ...).
@ -61,7 +63,7 @@ ef_open(int type, int how)
{ {
struct empfile *ep; struct empfile *ep;
struct flock lock; struct flock lock;
int oflags, fd, fsiz, size; int oflags, fd, fsiz, nslots;
if (ef_check(type) < 0) if (ef_check(type) < 0)
return 0; return 0;
@ -116,19 +118,14 @@ ef_open(int type, int how)
} }
} }
} else { } else {
if (how & EFF_MEM)
ep->csize = ep->fids;
else
ep->csize = blksize(fd) / ep->size;
/* 0 could lead to null cache, which confuses assertions */
if (!ep->csize)
ep->csize++;
size = ep->csize * ep->size;
if (CANT_HAPPEN(ep->cache)) if (CANT_HAPPEN(ep->cache))
free(ep->cache); free(ep->cache);
ep->cache = malloc(size); if (how & EFF_MEM)
if (ep->cache == NULL) { nslots = ep->fids;
logerror("Can't open %s: out of memory", ep->file); else
nslots = blksize(fd) / ep->size;
if (!ef_realloc_cache(ep, nslots)) {
logerror("Can't map %s (%s)", ep->file, strerror(errno));
close(fd); close(fd);
return 0; return 0;
} }
@ -151,6 +148,40 @@ ef_open(int type, int how)
return 1; return 1;
} }
/*
* Reallocate cache for table EP to hold COUNT slots.
* The table must not be allocated statically.
* The cache may still be unmapped.
* If reallocation succeeds, any pointers obtained from ef_ptr()
* become invalid.
* If it fails, the cache is unchanged, and errno is set.
* Return non-zero on success, zero on failure.
*/
static int
ef_realloc_cache(struct empfile *ep, int count)
{
void *cache;
if (CANT_HAPPEN(ep->flags & EFF_STATIC))
return 0;
if (CANT_HAPPEN(count < 0))
count = 0;
/*
* Avoid zero slots, because that can lead to null cache, which
* would be interpreted as unmapped cache.
*/
if (count == 0)
count++;
cache = realloc(ep->cache, count * ep->size);
if (!cache)
return 0;
ep->cache = cache;
ep->csize = count;
return 1;
}
/* /*
* Close the file-backed table TYPE (EF_SECTOR, ...). * Close the file-backed table TYPE (EF_SECTOR, ...).
* Return non-zero on success, zero on failure. * Return non-zero on success, zero on failure.
@ -384,47 +415,72 @@ ef_write(int type, int id, void *from)
} }
/* /*
* Extend the file-backed table TYPE by COUNT elements. * Extend table TYPE by COUNT elements.
* Can't extend privately mapped tables. * Any pointers obtained from ef_ptr() become invalid.
* Return non-zero on success, zero on failure. * Return non-zero on success, zero on failure.
*/ */
int int
ef_extend(int type, int count) ef_extend(int type, int count)
{ {
struct empfile *ep; struct empfile *ep;
void *tmpobj; char *p;
int id, i, how; int i, id;
if (ef_check(type) < 0) if (ef_check(type) < 0)
return 0; return 0;
ep = &empfile[type]; ep = &empfile[type];
if (CANT_HAPPEN(ep->fd < 0 || count < 0)) if (CANT_HAPPEN(count < 0))
return 0; return 0;
if (CANT_HAPPEN(ep->flags & EFF_PRIVATE))
return 0; /* not implemented */
tmpobj = calloc(1, ep->size);
id = ep->fids; id = ep->fids;
for (i = 0; i < count; i++) {
if (ep->init)
ep->init(id + i, tmpobj);
if (do_write(ep, tmpobj, id + i, 1) < 0)
break;
}
free(tmpobj);
if (ep->flags & EFF_MEM) { if (ep->flags & EFF_MEM) {
/* FIXME lazy bastards... do this right */ if (id + count > ep->csize) {
/* XXX this will cause problems if there are ef_ptrs (to the if (ep->flags & EFF_STATIC) {
* old allocated structure) active when we do the re-open */ logerror("Can't extend %s beyond %d elements",
how = ep->flags & ~EFF_IMMUTABLE; ep->file, ep->csize);
ef_close(type); return 0;
ef_open(type, how);
} else {
ep->fids += i;
} }
if (!ef_realloc_cache(ep, id + count)) {
logerror("Can't extend %s to %d elements (%s)",
ep->file, id + count, strerror(errno));
return 0;
}
}
p = ep->cache + id * ep->size;
do_blank(ep, p, id, count);
if (ep->fd >= 0 && !(ep->flags & EFF_PRIVATE)) {
if (do_write(ep, p, id, count) < 0)
return 0;
}
ep->cids += count;
} else {
/* need a buffer, steal last cache slot */
if (ep->cids == ep->csize)
ep->cids--;
p = ep->cache + ep->cids * ep->size;
for (i = 0; i < count; i++) {
do_blank(ep, p, id + i, 1);
if (do_write(ep, p, id + i, 1) < 0)
return 0;
}
}
ep->fids += count;
return 1;
}
return i == count; /*
* Initialize COUNT elements of EP in BUF, starting with element ID.
*/
static void
do_blank(struct empfile *ep, void *buf, int id, int count)
{
int i;
memset(buf, 0, count * ep->size);
if (ep->init) {
for (i = 0; i < count; i++)
ep->init(id + i, (char *)buf + i * ep->size);
}
} }
struct castr * struct castr *