mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-08 04:27:37 +00:00
413 lines
8.8 KiB
C
413 lines
8.8 KiB
C
/*-
|
|
* See the file LICENSE for redistribution information.
|
|
*
|
|
* Copyright (c) 1998
|
|
* Sleepycat Software. All rights reserved.
|
|
*
|
|
* This code is derived from software contributed to Sleepycat Software by
|
|
* Frederick G.M. Roeber of Netscape Communications Corp.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#ifndef lint
|
|
static const char sccsid[] = "@(#)os_map.c 10.6 (Sleepycat) 4/25/98";
|
|
#endif /* not lint */
|
|
|
|
#include "db_int.h"
|
|
#include "common_ext.h"
|
|
|
|
/*
|
|
* DB version 2 uses memory-mapped files for two things: 1) faster access of
|
|
* read-only databases, and 2) shared memory for process synchronization and
|
|
* locking. The code carefully does not mix the two uses. The first-case
|
|
* uses are actually written such that memory-mapping isn't really required
|
|
* -- it's merely a convenience -- so we don't have to worry much about it.
|
|
* In the second case, it's solely used as a shared memory mechanism, so that's
|
|
* all we have to replace.
|
|
*
|
|
* The mechanism I use for shared memory on Win16 is actually fairly simple.
|
|
* All memory in Win16 is shared, and a DLL can allocate memory and keep notes.
|
|
* This implementation of Win16, at least, is entirely done as a DLL. So I
|
|
* merely have to allocate memory, remember the "filename" for that memory,
|
|
* and issue small-integer segment IDs which index the DLL's list of these
|
|
* shared-memory segments. Subsequent opens are checked against the list of
|
|
* already open segments.
|
|
*/
|
|
|
|
typedef struct {
|
|
void *segment; /* Segment address. */
|
|
u_int32_t size; /* Segment size. */
|
|
char *name; /* Segment name. */
|
|
} os_segdata_t;
|
|
|
|
static os_segdata_t *__os_segdata; /* Segment table. */
|
|
static int __os_segdata_size; /* Segment table size. */
|
|
|
|
#define OS_SEGDATA_STARTING_SIZE 16
|
|
#define OS_SEGDATA_INCREMENT 16
|
|
|
|
static int __os_map __P((char *, REGINFO *));
|
|
static int __os_segdata_allocate __P((const char *, REGINFO *));
|
|
static int __os_segdata_find_byname __P((const char *, REGINFO *));
|
|
static int __os_segdata_new __P((int *));
|
|
static int __os_segdata_release __P((int));
|
|
|
|
/*
|
|
* __db_mapanon_ok --
|
|
* Return if this OS can support anonymous memory regions.
|
|
*
|
|
* PUBLIC: int __db_mapanon_ok __P((int));
|
|
*/
|
|
int
|
|
__db_mapanon_ok(need_names)
|
|
int need_names;
|
|
{
|
|
COMPQUIET(need_names, 0);
|
|
|
|
/*
|
|
* All Win16 regions are in named anonymous shared memory,
|
|
* so the answer is always yes.
|
|
*/
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* __db_mapinit --
|
|
* Return if shared regions need to be initialized.
|
|
*
|
|
* PUBLIC: int __db_mapinit __P((void));
|
|
*/
|
|
int
|
|
__db_mapinit()
|
|
{
|
|
/* Win16 regions do not need to be fully written. */
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* __db_mapregion --
|
|
* Attach to a shared memory region.
|
|
*
|
|
* PUBLIC: int __db_mapregion __P((char *, REGINFO *));
|
|
*/
|
|
int
|
|
__db_mapregion(path, infop)
|
|
char *path;
|
|
REGINFO *infop;
|
|
{
|
|
/* If the user replaces the map call, call through their interface. */
|
|
if (__db_jump.j_map != NULL)
|
|
return (__db_jump.j_map(path, infop->fd, infop->size,
|
|
1, F_ISSET(infop, REGION_ANONYMOUS), 0, &infop->addr));
|
|
|
|
/*
|
|
* XXX
|
|
* We could actually grow regions without a lot of difficulty, but
|
|
* since the maximum region is 64K, it's unclear to me it's worth
|
|
* the effort.
|
|
*/
|
|
return (__os_map(path, infop));
|
|
}
|
|
|
|
/*
|
|
* __db_unmapregion --
|
|
* Detach from the shared memory region.
|
|
*
|
|
* PUBLIC: int __db_unmapregion __P((REGINFO *));
|
|
*/
|
|
int
|
|
__db_unmapregion(infop)
|
|
REGINFO *infop;
|
|
{
|
|
if (__db_jump.j_unmap != NULL)
|
|
return (__db_jump.j_unmap(infop->addr, infop->size));
|
|
|
|
/* There's no mapping to discard, we're done. */
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* __db_unlinkregion --
|
|
* Remove the shared memory region.
|
|
*
|
|
* PUBLIC: int __db_unlinkregion __P((char *, REGINFO *));
|
|
*/
|
|
int
|
|
__db_unlinkregion(name, infop)
|
|
char *name;
|
|
REGINFO *infop;
|
|
{
|
|
COMPQUIET(infop, NULL);
|
|
|
|
if (__db_jump.j_runlink != NULL)
|
|
return (__db_jump.j_runlink(name));
|
|
|
|
return (__os_segdata_release(infop->segid));
|
|
}
|
|
|
|
/*
|
|
* __db_mapfile --
|
|
* Map in a shared memory file.
|
|
*
|
|
* PUBLIC: int __db_mapfile __P((char *, int, size_t, int, void **));
|
|
*/
|
|
int
|
|
__db_mapfile(path, fd, len, is_rdonly, addr)
|
|
char *path;
|
|
int fd, is_rdonly;
|
|
size_t len;
|
|
void **addr;
|
|
{
|
|
if (__db_jump.j_map != NULL)
|
|
return (__db_jump.j_map(path, fd, len, 0, 0, is_rdonly, addr));
|
|
|
|
/* We cannot map in regular files in Win16. */
|
|
return (EINVAL);
|
|
}
|
|
|
|
/*
|
|
* __db_unmapfile --
|
|
* Unmap the shared memory file.
|
|
*
|
|
* PUBLIC: int __db_unmapfile __P((void *, size_t));
|
|
*/
|
|
int
|
|
__db_unmapfile(addr, len)
|
|
void *addr;
|
|
size_t len;
|
|
{
|
|
if (__db_jump.j_unmap != NULL)
|
|
return (__db_jump.j_unmap(addr, len));
|
|
|
|
/* We cannot map in regular files in Win16. */
|
|
return (EINVAL);
|
|
}
|
|
|
|
/*
|
|
* __os_map --
|
|
* Create/find a shared region.
|
|
*/
|
|
static int
|
|
__os_map(path, infop)
|
|
char *path;
|
|
REGINFO *infop;
|
|
{
|
|
int ret;
|
|
|
|
/* Try to find an already existing segment. */
|
|
if (__os_segdata_find_byname(path, infop) == 0)
|
|
return (0);
|
|
|
|
/*
|
|
* If we're trying to join the region and failing, assume
|
|
* that there was a reboot and the region no longer exists.
|
|
*/
|
|
if (!F_ISSET(infop, REGION_CREATED))
|
|
return (EAGAIN);
|
|
|
|
/* Create a new segment. */
|
|
if ((ret = __os_segdata_allocate(path, infop)) != 0)
|
|
return (ret);
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* __os_segdata_init --
|
|
* Initialises the library's table of shared memory segments. Called
|
|
* (once!) from the DLL main routine.
|
|
*
|
|
* PUBLIC: int __os_segdata_init __P((void));
|
|
*/
|
|
int
|
|
__os_segdata_init()
|
|
{
|
|
if (__os_segdata != NULL)
|
|
return (EEXIST);
|
|
|
|
__os_segdata_size = OS_SEGDATA_STARTING_SIZE;
|
|
if ((__os_segdata = (os_segdata_t *)
|
|
__db_calloc(__os_segdata_size, sizeof(os_segdata_t))) == NULL)
|
|
return (ENOMEM);
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* __os_segdata_destroy --
|
|
* Destroys the library's table of shared memory segments. It also
|
|
* frees all linked data: the segments themselves, and their names.
|
|
* Called (once!) from the DLL shutdown routine.
|
|
*
|
|
* PUBLIC: int __os_segdata_destroy __P((void));
|
|
*/
|
|
int
|
|
__os_segdata_destroy()
|
|
{
|
|
os_segdata_t *p;
|
|
int i;
|
|
|
|
if (__os_segdata == NULL)
|
|
return (0);
|
|
|
|
for (i = 0; i < __os_segdata_size; i++) {
|
|
p = &__os_segdata[i];
|
|
if (p->name != NULL) {
|
|
FREES(p->name);
|
|
p->name = NULL;
|
|
}
|
|
if (p->segment != NULL) {
|
|
FREE(p->segment, p->size);
|
|
p->segment = NULL;
|
|
}
|
|
p->size = 0;
|
|
}
|
|
|
|
FREE(__os_segdata, __os_segdata_size * sizeof(os_segdata_t));
|
|
__os_segdata = NULL;
|
|
__os_segdata_size = 0;
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* __os_segdata_allocate --
|
|
* Creates a new segment of the specified size, optionally with the
|
|
* specified name.
|
|
*/
|
|
static int
|
|
__os_segdata_allocate(name, infop)
|
|
const char *name;
|
|
REGINFO *infop;
|
|
{
|
|
os_segdata_t *p;
|
|
int segid, ret;
|
|
|
|
if ((ret = __os_segdata_new(&segid)) != 0)
|
|
return (ret);
|
|
|
|
p = &__os_segdata[segid];
|
|
if ((p->segment = (void *)__db_calloc(infop->size, 1)) == NULL)
|
|
return (ENOMEM);
|
|
if ((p->name = __db_strdup(name)) == NULL) {
|
|
FREE(p->segment, infop->size);
|
|
p->segment = NULL;
|
|
return (ENOMEM);
|
|
}
|
|
p->size = infop->size;
|
|
|
|
infop->addr = p->segment;
|
|
infop->segid = segid;
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* __os_segdata_new --
|
|
* Finds a new segdata slot. Does not initialise it, so the fd returned
|
|
* is only valid until you call this again.
|
|
*/
|
|
static int
|
|
__os_segdata_new(segidp)
|
|
int *segidp;
|
|
{
|
|
os_segdata_t *p, *newptr;
|
|
int i, newsize;
|
|
|
|
if (__os_segdata == NULL)
|
|
return (EAGAIN);
|
|
|
|
for (i = 0; i < __os_segdata_size; i++) {
|
|
p = &__os_segdata[i];
|
|
if (p->segment == NULL) {
|
|
*segidp = i;
|
|
return (0);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This test is actually pedantic, since I can't malloc more than 64K,
|
|
* and the structure is more than two bytes big. But I'm a pedant.
|
|
*/
|
|
if ((u_int)__os_segdata_size >=
|
|
(32768 / sizeof(os_segdata_t) - OS_SEGDATA_INCREMENT))
|
|
return (ENOMEM);
|
|
|
|
newsize = __os_segdata_size + OS_SEGDATA_INCREMENT;
|
|
if ((newptr = (os_segdata_t *)
|
|
__db_realloc(__os_segdata, newsize * sizeof(os_segdata_t))) == NULL)
|
|
return (ENOMEM);
|
|
memset(&newptr[__os_segdata_size],
|
|
0, OS_SEGDATA_INCREMENT * sizeof(os_segdata_t));
|
|
__os_segdata = newptr;
|
|
__os_segdata_size = newsize;
|
|
|
|
*segidp = __os_segdata_size;
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* __os_segdata_find_byname --
|
|
* Finds a segment by its name.
|
|
*
|
|
* PUBLIC: __os_segdata_find_byname __P((const char *, REGINFO *));
|
|
*/
|
|
static int
|
|
__os_segdata_find_byname(name, infop)
|
|
const char *name;
|
|
REGINFO *infop;
|
|
{
|
|
os_segdata_t *p;
|
|
int i;
|
|
|
|
if (__os_segdata == NULL)
|
|
return (EAGAIN);
|
|
|
|
if (name == NULL)
|
|
return (ENOENT);
|
|
|
|
for (i = 0; i < __os_segdata_size; i++) {
|
|
p = &__os_segdata[i];
|
|
if (p->name != NULL && strcmp(name, p->name) == 0) {
|
|
infop->addr = p->segment;
|
|
infop->segid = i;
|
|
return (0);
|
|
}
|
|
}
|
|
return (ENOENT);
|
|
}
|
|
|
|
/*
|
|
* __os_segdata_release --
|
|
* Free a segdata entry.
|
|
*/
|
|
static int
|
|
__os_segdata_release(segid)
|
|
int segid;
|
|
{
|
|
os_segdata_t *p;
|
|
|
|
if (__os_segdata == NULL)
|
|
return (EAGAIN);
|
|
|
|
if (segid < 0 || segid >= __os_segdata_size)
|
|
return (EINVAL);
|
|
|
|
p = &__os_segdata[segid];
|
|
if (p->name != NULL) {
|
|
FREES(p->name);
|
|
p->name = NULL;
|
|
}
|
|
if (p->segment != NULL) {
|
|
FREE(p->segment, p->size);
|
|
p->segment = NULL;
|
|
}
|
|
p->size = 0;
|
|
|
|
/* Any shrink-table logic could go here */
|
|
|
|
return (0);
|
|
}
|