mp_fopen.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 1996, 1997, 1998
* Sleepycat Software. All rights reserved.
*/
#include "config.h"
#ifndef lint
static const char sccsid[] = "@(#)mp_fopen.c 10.60 (Sleepycat) 1/1/99";
#endif /* not lint */
#ifndef NO_SYSTEM_INCLUDES
#include <errno.h>
#include <string.h>
#endif
#include "db_int.h"
#include "shqueue.h"
#include "db_shash.h"
#include "mp.h"
#include "common_ext.h"
/*
* memp_fopen --
* Open a backing file for the memory pool.
*/
int
const char *path;
int mode;
DB_MPOOLFILE **retp;
{
int ret;
/* Validate arguments. */
return (ret);
/* Require a non-zero pagesize. */
if (pagesize == 0) {
return (EINVAL);
}
return (EINVAL);
return (__memp_fopen(dbmp,
}
/*
* __memp_fopen --
* Open a backing file for the memory pool; internal version.
*
* PUBLIC: int __memp_fopen __P((DB_MPOOL *, MPOOLFILE *, const char *,
* PUBLIC: u_int32_t, int, size_t, int, DB_MPOOL_FINFO *, DB_MPOOLFILE **));
*/
int
const char *path;
DB_MPOOLFILE **retp;
{
int ret;
char *rpath;
ret = 0;
/*
* If mfp is provided, we take the DB_MPOOL_FINFO information from
* the mfp. We don't bother initializing everything, because some
* of them are expensive to acquire. If no mfp is provided and the
* finfop argument is NULL, we default the values.
*/
} else {
}
}
/* Allocate and initialize the per-process structure. */
return (ret);
"memp_fopen: temporary files can't be readonly");
goto err;
}
last_pgno = 0;
} else {
/* Get the real name for this file and open it. */
goto err;
goto err;
}
/*
* Don't permit files that aren't a multiple of the pagesize,
* and find the number of the last page in the file, all the
* time being careful not to overflow 32 bits.
*
* !!!
* We can't use off_t's here, or in any code in the mainline
* library for that matter. (We have to use them in the os
* stubs, of course, as there are system calls that take them
* as arguments.) The reason is that some customers build in
* environments where an off_t is 32-bits, but still run where
* offsets are 64-bits, and they pay us a lot of money.
*/
goto err;
}
/* Page sizes have to be a power-of-two, ignore mbytes. */
"%s: file size not a multiple of the pagesize",
rpath);
goto err;
}
/* Correction: page numbers are zero-based, not 1-based. */
if (last_pgno != 0)
--last_pgno;
/*
* Get the file id if we weren't given one. Generated file id's
* don't use timestamps, otherwise there'd be no chance of any
* other process joining the party.
*/
goto err;
}
}
/*
* If we weren't provided an underlying shared object to join with,
* for the per-process thread lock.
*/
if (needlock)
else {
ret = 0;
}
if (ret == 0 &&
if (needlock)
if (ret != 0)
goto err;
/*
* If a file:
* + is read-only
* + isn't temporary
* + the DB_NOMMAP flag wasn't set
* + and is less than mp_mmapsize bytes in size
*
* checking based on the mmap call failure. We want to do normal I/O
* on the file if the reason we failed was because the file was on an
* NFS mounted partition, and we can fail in buffer I/O just as easily
* as here.
*
* XXX
* We'd like to test to see if the file is too big to mmap. Since we
* don't know what size or type off_t's or size_t's are, or the largest
* unsigned integral type is, or what random insanity the local C
* compiler will perpetrate, doing the comparison in a portable way is
* flatly impossible. Hope that mmap fails if the file is too large.
*/
}
if (__db_mapfile(rpath,
}
}
return (0);
err: /*
* Note that we do not have to free the thread mutex, because we
* never get to here after we have successfully allocated it.
*/
return (ret);
}
/*
* __memp_mf_open --
* Open an MPOOLFILE.
*/
static int
const char *path;
{
int ret;
void *p;
/*
* Walk the list of MPOOLFILE's, looking for a matching file.
* Temporary files can't match previous files.
*/
if (!ISTEMPORARY)
continue;
"%s: ftype, clear length or pagesize changed",
path);
return (EINVAL);
}
/* Found it: increment the reference count. */
return (0);
}
}
/* Allocate a new MPOOLFILE. */
return (ret);
/* Initialize the structure. */
/*
* If the user specifies DB_MPOOL_LAST or DB_MPOOL_NEW on a memp_fget,
* we have to know the last page in the file. Figure it out and save
* it away.
*/
if (ISTEMPORARY)
else {
/* Copy the file path into shared memory. */
goto err;
/* Copy the file identification string into shared memory. */
goto err;
}
/* Copy the page cookie into shared memory. */
mfp->pgcookie_len = 0;
mfp->pgcookie_off = 0;
} else {
goto err;
}
/* Prepend the MPOOLFILE to the list of MPOOLFILE's. */
if (0) {
if (mfp->fileid_off != 0)
}
return (0);
}
/*
* memp_fclose --
* Close a backing file for the memory pool.
*/
int
{
ret = 0;
for (;;) {
/*
* We have to reference count DB_MPOOLFILE structures as other
* threads may be using them. The problem only happens if the
* application makes a bad design choice. Here's the path:
*
* Thread A opens a database.
* Thread B uses thread A's DB_MPOOLFILE to write a buffer
* in order to free up memory in the mpool cache.
* Thread A closes the database while thread B is using the
* DB_MPOOLFILE structure.
*
* By opening all databases before creating the threads, and
* closing them after the threads have exited, applications
* get better performance and avoid the problem path entirely.
*
* Regardless, holding the DB_MPOOLFILE to flush a dirty buffer
* is a short-term lock, even in worst case, since we better be
* the only thread of control using the DB_MPOOLFILE structure
* to read pages *into* the cache. Wait until we're the only
* reference holder and remove the DB_MPOOLFILE structure from
* the list, so nobody else can even find it.
*/
break;
}
(void)__os_sleep(1, 0);
}
/* Complain if pinned blocks never returned. */
/* Close the underlying MPOOLFILE. */
/* Discard any mmap information. */
/* Close the file; temporary files may not yet have been created. */
if (ret != 0)
}
/* Free memory. */
}
/* Discard the DB_MPOOLFILE structure. */
return (ret);
}
/*
* __memp_mf_close --
* Close down an MPOOLFILE.
*/
static int
{
/* If more than a single reference, simply decrement. */
goto ret1;
}
/*
* Move any BH's held by the file to the free list. We don't free the
* memory itself because we may be discarding the memory pool, and it's
* fairly expensive to reintegrate the buffers back into the region for
* no purpose.
*/
#ifdef DEBUG_NO_DIRTY
/* Complain if we find any blocks that were left dirty. */
"%s: close: pgno %lu left dirty; ref %lu",
#endif
}
}
}
/* Delete from the list of MPOOLFILEs. */
/* Free the space. */
if (mfp->fileid_off != 0)
if (mfp->pgcookie_off != 0)
return (0);
}