📄 mp_fopen.c
字号:
ret = EINVAL; goto err; } } /* * If the user specifies DB_MPOOL_LAST or DB_MPOOL_NEW on a * page get, we have to increment the last page in the file. * Figure it out and save it away. * * Note correction: page numbers are zero-based, not 1-based. */ last_pgno = (db_pgno_t)(mbytes * (MEGABYTE / pagesize)); last_pgno += (db_pgno_t)(bytes / pagesize); if (last_pgno != 0) --last_pgno; mfp->orig_last_pgno = mfp->last_pgno = last_pgno; /* Copy the file path into shared memory. */ if ((ret = __memp_alloc(dbmp, dbmp->reginfo, NULL, strlen(path) + 1, &mfp->path_off, &p)) != 0) goto err; memcpy(p, path, strlen(path) + 1); /* Copy the file identification string into shared memory. */ if ((ret = __memp_alloc(dbmp, dbmp->reginfo, NULL, DB_FILE_ID_LEN, &mfp->fileid_off, &p)) != 0) goto err; memcpy(p, dbmfp->fileid, DB_FILE_ID_LEN); } /* Copy the page cookie into shared memory. */ if (dbmfp->pgcookie == NULL || dbmfp->pgcookie->size == 0) { mfp->pgcookie_len = 0; mfp->pgcookie_off = 0; } else { if ((ret = __memp_alloc(dbmp, dbmp->reginfo, NULL, dbmfp->pgcookie->size, &mfp->pgcookie_off, &p)) != 0) goto err; memcpy(p, dbmfp->pgcookie->data, dbmfp->pgcookie->size); mfp->pgcookie_len = dbmfp->pgcookie->size; } /* * Prepend the MPOOLFILE to the list of MPOOLFILE's. */ R_LOCK(dbenv, dbmp->reginfo); ret = __db_mutex_setup(dbenv, dbmp->reginfo, &mfp->mutex, MUTEX_NO_RLOCK); if (ret == 0) SH_TAILQ_INSERT_HEAD(&mp->mpfq, mfp, q, __mpoolfile); R_UNLOCK(dbenv, dbmp->reginfo); if (ret != 0) goto err;check_map: /* * If a file: * + isn't temporary * + is read-only * + doesn't require any pgin/pgout support * + the DB_NOMMAP flag wasn't set (in either the file open or * the environment in which it was opened) * + and is less than mp_mmapsize bytes in size * * we can mmap it instead of reading/writing buffers. Don't do error * 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. * * 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. */#define DB_MAXMMAPSIZE (10 * 1024 * 1024) /* 10 MB. */ if (F_ISSET(mfp, MP_CAN_MMAP)) { if (path == NULL) F_CLR(mfp, MP_CAN_MMAP); if (!F_ISSET(dbmfp, MP_READONLY)) F_CLR(mfp, MP_CAN_MMAP); if (dbmfp->ftype != 0) F_CLR(mfp, MP_CAN_MMAP); if (LF_ISSET(DB_NOMMAP) || F_ISSET(dbenv, DB_ENV_NOMMAP)) F_CLR(mfp, MP_CAN_MMAP); maxmap = dbenv->mp_mmapsize == 0 ? DB_MAXMMAPSIZE : dbenv->mp_mmapsize; if (mbytes > maxmap / MEGABYTE || (mbytes == maxmap / MEGABYTE && bytes >= maxmap % MEGABYTE)) F_CLR(mfp, MP_CAN_MMAP); dbmfp->addr = NULL; if (F_ISSET(mfp, MP_CAN_MMAP)) { dbmfp->len = (size_t)mbytes * MEGABYTE + bytes; if (__os_mapfile(dbenv, rpath, dbmfp->fhp, dbmfp->len, 1, &dbmfp->addr) != 0) { dbmfp->addr = NULL; F_CLR(mfp, MP_CAN_MMAP); } } } dbmfp->mfp = mfp; F_SET(dbmfp, MP_OPEN_CALLED); /* Add the file to the process' list of DB_MPOOLFILEs. */ MUTEX_THREAD_LOCK(dbenv, dbmp->mutexp); TAILQ_INSERT_TAIL(&dbmp->dbmfq, dbmfp, q); MUTEX_THREAD_UNLOCK(dbenv, dbmp->mutexp); if (0) {err: if (F_ISSET(dbmfp->fhp, DB_FH_VALID)) (void)__os_closehandle(dbenv, dbmfp->fhp); if (mfp_alloc) { R_LOCK(dbenv, dbmp->reginfo); if (mfp->path_off != 0) __db_shalloc_free(dbmp->reginfo[0].addr, R_ADDR(dbmp->reginfo, mfp->path_off)); if (mfp->fileid_off != 0) __db_shalloc_free(dbmp->reginfo[0].addr, R_ADDR(dbmp->reginfo, mfp->fileid_off)); __db_shalloc_free(dbmp->reginfo[0].addr, mfp); R_UNLOCK(dbenv, dbmp->reginfo); } } if (rpath != NULL) __os_free(dbenv, rpath); return (ret);}/* * __memp_get_fileid -- * Return the file ID. * * XXX * Undocumented interface: DB private. */static void__memp_get_fileid(dbmfp, fidp) DB_MPOOLFILE *dbmfp; u_int8_t *fidp;{ /* * No lock needed -- we're using the handle, it had better not * be going away. * * !!! * Get the fileID out of the region, not out of the DB_MPOOLFILE * structure because the DB_MPOOLFILE reference is possibly short * lived, and isn't to be trusted. */ memcpy(fidp, R_ADDR( dbmfp->dbmp->reginfo, dbmfp->mfp->fileid_off), DB_FILE_ID_LEN);}/* * __memp_last_pgno -- * Return the page number of the last page in the file. * * XXX * Undocumented interface: DB private. */static void__memp_last_pgno(dbmfp, pgnoaddr) DB_MPOOLFILE *dbmfp; db_pgno_t *pgnoaddr;{ DB_ENV *dbenv; DB_MPOOL *dbmp; dbmp = dbmfp->dbmp; dbenv = dbmp->dbenv; R_LOCK(dbenv, dbmp->reginfo); *pgnoaddr = dbmfp->mfp->last_pgno; R_UNLOCK(dbenv, dbmp->reginfo);}/* * __memp_refcnt -- * Return the current reference count. * * XXX * Undocumented interface: DB private. */static void__memp_refcnt(dbmfp, cntp) DB_MPOOLFILE *dbmfp; db_pgno_t *cntp;{ DB_ENV *dbenv; dbenv = dbmfp->dbmp->dbenv; MUTEX_LOCK(dbenv, &dbmfp->mfp->mutex); *cntp = dbmfp->mfp->mpf_cnt; MUTEX_UNLOCK(dbenv, &dbmfp->mfp->mutex);}/* * __memp_set_unlink -- * Set unlink on last close flag. * * XXX * Undocumented interface: DB private. */static void__memp_set_unlink(dbmpf, set) DB_MPOOLFILE *dbmpf; int set;{ DB_ENV *dbenv; dbenv = dbmpf->dbmp->dbenv; MUTEX_LOCK(dbenv, &dbmpf->mfp->mutex); if (set) F_SET(dbmpf->mfp, MP_UNLINK); else F_CLR(dbmpf->mfp, MP_UNLINK); MUTEX_UNLOCK(dbenv, &dbmpf->mfp->mutex);}/* * memp_fclose -- * Close a backing file for the memory pool. */static int__memp_fclose(dbmfp, flags) DB_MPOOLFILE *dbmfp; u_int32_t flags;{ DB_ENV *dbenv; int ret, t_ret; dbenv = dbmfp->dbmp->dbenv; PANIC_CHECK(dbenv); /* * XXX * DB_MPOOL_DISCARD: Undocumented flag: DB private. */ ret = __db_fchk(dbenv, "DB_MPOOLFILE->close", flags, DB_MPOOL_DISCARD); if ((t_ret = __memp_fclose_int(dbmfp, flags)) != 0 && ret == 0) ret = t_ret; return (ret);}/* * __memp_fclose_int -- * Internal version of __memp_fclose. * * PUBLIC: int __memp_fclose_int __P((DB_MPOOLFILE *, u_int32_t)); */int__memp_fclose_int(dbmfp, flags) DB_MPOOLFILE *dbmfp; u_int32_t flags;{ DB_ENV *dbenv; DB_MPOOL *dbmp; MPOOLFILE *mfp; char *rpath; int deleted, ret, t_ret; dbmp = dbmfp->dbmp; dbenv = dbmp->dbenv; ret = 0; /* * We have to reference count DB_MPOOLFILE structures as other threads * in the process may be using them. Here's the problem: * * 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 any threads, and closing * the databases after all 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 * find it. We do this, rather than have the last reference holder * (whoever that might be) discard the DB_MPOOLFILE structure, because * we'd rather write error messages to the application in the close * routine, not in the checkpoint/sync routine. * * !!! * It's possible the DB_MPOOLFILE was never added to the DB_MPOOLFILE * file list, check the DB_OPEN_CALLED flag to be sure. */ for (deleted = 0;;) { MUTEX_THREAD_LOCK(dbenv, dbmp->mutexp); if (dbmfp->ref == 1) { if (F_ISSET(dbmfp, MP_OPEN_CALLED)) TAILQ_REMOVE(&dbmp->dbmfq, dbmfp, q); deleted = 1; } MUTEX_THREAD_UNLOCK(dbenv, dbmp->mutexp); if (deleted) break; __os_sleep(dbenv, 1, 0); } /* Complain if pinned blocks never returned. */ if (dbmfp->pinref != 0) { __db_err(dbenv, "%s: close: %lu blocks left pinned", __memp_fn(dbmfp), (u_long)dbmfp->pinref); ret = __db_panic(dbenv, DB_RUNRECOVERY); } /* Discard any mmap information. */ if (dbmfp->addr != NULL && (ret = __os_unmapfile(dbenv, dbmfp->addr, dbmfp->len)) != 0) __db_err(dbenv, "%s: %s", __memp_fn(dbmfp), db_strerror(ret)); /* Close the file; temporary files may not yet have been created. */ if (F_ISSET(dbmfp->fhp, DB_FH_VALID) && (t_ret = __os_closehandle(dbenv, dbmfp->fhp)) != 0) { __db_err(dbenv, "%s: %s", __memp_fn(dbmfp), db_strerror(t_ret)); if (ret == 0) ret = t_ret; } /* Discard the thread mutex. */ if (dbmfp->mutexp != NULL) __db_mutex_free(dbenv, dbmp->reginfo, dbmfp->mutexp); /* * Discard our reference on the the underlying MPOOLFILE, and close * it if it's no longer useful to anyone. It possible the open of * the file never happened or wasn't successful, in which case, mpf * will be NULL; */ if ((mfp = dbmfp->mfp) == NULL) goto done; /* * If it's a temp file, all outstanding references belong to unflushed * buffers. (A temp file can only be referenced by one DB_MPOOLFILE). * We don't care about preserving any of those buffers, so mark the * MPOOLFILE as dead so that even the dirty ones just get discarded * when we try to flush them. */ deleted = 0; MUTEX_LOCK(dbenv, &mfp->mutex); if (--mfp->mpf_cnt == 0 || LF_ISSET(DB_MPOOL_DISCARD)) { if (LF_ISSET(DB_MPOOL_DISCARD) || F_ISSET(mfp, MP_TEMP | MP_UNLINK)) MPOOLFILE_IGNORE(mfp); if (F_ISSET(mfp, MP_UNLINK)) { if ((t_ret = __db_appname(dbmp->dbenv, DB_APP_DATA, R_ADDR(dbmp->reginfo, mfp->path_off), 0, NULL, &rpath)) != 0 && ret == 0) ret = t_ret; if (t_ret == 0) { if ((t_ret = __os_unlink( dbmp->dbenv, rpath) != 0) && ret == 0) ret = t_ret; __os_free(dbenv, rpath); } } if (mfp->block_cnt == 0) { if ((t_ret = __memp_mf_discard(dbmp, mfp)) != 0 && ret == 0) ret = t_ret; deleted = 1; } } if (deleted == 0) MUTEX_UNLOCK(dbenv, &mfp->mutex); /* Discard the DB_MPOOLFILE structure. */done: __os_free(dbenv, dbmfp->fhp); __os_free(dbenv, dbmfp); return (ret);}/* * __memp_mf_discard -- * Discard an MPOOLFILE. * * PUBLIC: int __memp_mf_discard __P((DB_MPOOL *, MPOOLFILE *)); */int__memp_mf_discard(dbmp, mfp) DB_MPOOL *dbmp; MPOOLFILE *mfp;{ DB_ENV *dbenv; DB_FH fh; DB_MPOOL_STAT *sp; MPOOL *mp; char *rpath; int ret; dbenv = dbmp->dbenv; mp = dbmp->reginfo[0].primary; ret = 0; /* * Expects caller to be holding the MPOOLFILE mutex. * * When discarding a file, we have to flush writes from it to disk. * The scenario is that dirty buffers from this file need to be * flushed to satisfy a future checkpoint, but when the checkpoint * calls mpool sync, the sync code won't know anything about them. */ if (!F_ISSET(mfp, MP_DEADFILE) && (ret = __db_appname(dbenv, DB_APP_DATA, R_ADDR(dbmp->reginfo, mfp->path_off), 0, NULL, &rpath)) == 0) { if ((ret = __os_open(dbenv, rpath, 0, 0, &fh)) == 0) { ret = __os_fsync(dbenv, &fh); (void)__os_closehandle(dbenv, &fh); } __os_free(dbenv, rpath); } /* * We have to release the MPOOLFILE lock before acquiring the region * lock so that we don't deadlock. Make sure nobody ever looks at * this structure again. */ MPOOLFILE_IGNORE(mfp); /* Discard the mutex we're holding. */ MUTEX_UNLOCK(dbenv, &mfp->mutex); /* Delete from the list of MPOOLFILEs. */ R_LOCK(dbenv, dbmp->reginfo); SH_TAILQ_REMOVE(&mp->mpfq, mfp, q, __mpoolfile); /* Copy the statistics into the region. */ sp = &mp->stat; sp->st_cache_hit += mfp->stat.st_cache_hit; sp->st_cache_miss += mfp->stat.st_cache_miss; sp->st_map += mfp->stat.st_map; sp->st_page_create += mfp->stat.st_page_create; sp->st_page_in += mfp->stat.st_page_in; sp->st_page_out += mfp->stat.st_page_out; /* Clear the mutex this MPOOLFILE recorded. */ __db_shlocks_clear(&mfp->mutex, dbmp->reginfo, (REGMAINT *)R_ADDR(dbmp->reginfo, mp->maint_off)); /* Free the space. */ if (mfp->path_off != 0) __db_shalloc_free(dbmp->reginfo[0].addr, R_ADDR(dbmp->reginfo, mfp->path_off)); if (mfp->fileid_off != 0) __db_shalloc_free(dbmp->reginfo[0].addr, R_ADDR(dbmp->reginfo, mfp->fileid_off)); if (mfp->pgcookie_off != 0) __db_shalloc_free(dbmp->reginfo[0].addr, R_ADDR(dbmp->reginfo, mfp->pgcookie_off)); __db_shalloc_free(dbmp->reginfo[0].addr, mfp); R_UNLOCK(dbenv, dbmp->reginfo); return (ret);}/* * __memp_fn -- * On errors we print whatever is available as the file name. * * PUBLIC: char * __memp_fn __P((DB_MPOOLFILE *)); */char *__memp_fn(dbmfp) DB_MPOOLFILE *dbmfp;{ return (__memp_fns(dbmfp->dbmp, dbmfp->mfp));}/* * __memp_fns -- * On errors we print whatever is available as the file name. * * PUBLIC: char * __memp_fns __P((DB_MPOOL *, MPOOLFILE *)); * */char *__memp_fns(dbmp, mfp) DB_MPOOL *dbmp; MPOOLFILE *mfp;{ if (mfp->path_off == 0) return ((char *)"temporary"); return ((char *)R_ADDR(dbmp->reginfo, mfp->path_off));}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -