📄 env_region.c
字号:
* and acquire, and we have to know if it fails. (It CAN fail, e.g., * SunOS, when using fcntl(2) for locking and using an in-memory * filesystem as the database home. But you knew that, I'm sure -- it * probably wasn't even worth mentioning.) */ if ((ret = __db_mutex_setup(dbenv, infop, &renv->mutex, MUTEX_NO_RECORD | MUTEX_NO_RLOCK)) != 0) { __db_err(dbenv, "%s: unable to initialize environment lock: %s", infop->name, db_strerror(ret)); goto err; } if (!F_ISSET(&renv->mutex, MUTEX_IGNORE) && (ret = __db_mutex_lock(dbenv, &renv->mutex)) != 0) { __db_err(dbenv, "%s: unable to acquire environment lock: %s", infop->name, db_strerror(ret)); goto err; } /* * Get the underlying REGION structure for this environment. Note, * we created the underlying OS region before we acquired the REGION * structure, which is backwards from the normal procedure. Update * the REGION structure. */ if ((ret = __db_des_get(dbenv, infop, infop, &rp)) != 0) {find_err: __db_err(dbenv, "%s: unable to find environment", infop->name); if (ret == 0) ret = EINVAL; goto err; } infop->rp = rp; rp->size = tregion.size; rp->segid = tregion.segid; /* * !!! * If we create an environment where regions are public and in system * memory, we have to inform processes joining the environment how to * attach to the shared memory segment. So, we write the shared memory * identifier into the file, to be read by those other processes. * * XXX * This is really OS-layer information, but I can't see any easy way * to move it down there without passing down information that it has * no right to know, e.g., that this is the one-and-only REGENV region * and not some other random region. */ if (tregion.segid != INVALID_REGION_SEGID) { ref.size = tregion.size; ref.segid = tregion.segid; if ((ret = __os_write( dbenv, dbenv->lockfhp, &ref, sizeof(ref), &nrw)) != 0) { __db_err(dbenv, "%s: unable to write out public environment ID: %s", infop->name, db_strerror(ret)); goto err; } } /* * If not doing thread locking, we need to save the file handle for * fcntl(2) locking. Otherwise, discard the handle, we no longer * need it, and the less contact between the buffer cache and the VM, * the better. */#if defined(HAVE_MUTEX_THREADS) if (dbenv->lockfhp != NULL) { (void)__os_closehandle(dbenv, dbenv->lockfhp); dbenv->lockfhp = NULL; }#endif /* Validate the file. */ renv->magic = DB_REGION_MAGIC; /* Discard our lock. */ MUTEX_UNLOCK(dbenv, &renv->mutex); /* Everything looks good, we're done. */ dbenv->reginfo = infop; return (0);err:retry: /* Close any open file handle. */ if (dbenv->lockfhp != NULL) { (void)__os_closehandle(dbenv, dbenv->lockfhp); dbenv->lockfhp = NULL; } /* * If we joined or created the region, detach from it. If we created * it, destroy it. Note, there's a path in the above code where we're * using a temporary REGION structure because we haven't yet allocated * the real one. In that case the region address (addr) will be filled * in, but the REGION pointer (rp) won't. Fix it. */ if (infop->addr != NULL) { if (infop->rp == NULL) infop->rp = &tregion; /* Reset the addr value that we "corrected" above. */ infop->addr = infop->primary; (void)__os_r_detach(dbenv, infop, F_ISSET(infop, REGION_CREATE)); } /* Free the allocated name and/or REGINFO structure. */ if (infop->name != NULL) __os_free(dbenv, infop->name); __os_free(dbenv, infop); /* If we had a temporary error, wait awhile and try again. */ if (ret == 0) { if (++retry_cnt > 3) { __db_err(dbenv, "unable to join the environment"); ret = EAGAIN; } else { __os_sleep(dbenv, retry_cnt * 3, 0); goto loop; } } return (ret);}/* * __db_e_detach -- * Detach from the environment. * * PUBLIC: int __db_e_detach __P((DB_ENV *, int)); */int__db_e_detach(dbenv, destroy) DB_ENV *dbenv; int destroy;{ REGENV *renv; REGINFO *infop; infop = dbenv->reginfo; renv = infop->primary; if (F_ISSET(dbenv, DB_ENV_PRIVATE)) destroy = 1; /* Lock the environment. */ MUTEX_LOCK(dbenv, &renv->mutex); /* Decrement the reference count. */ if (renv->refcnt == 0) { __db_err(dbenv, "region %lu (environment): reference count went negative", (u_long)infop->rp->id); } else --renv->refcnt; /* Release the lock. */ MUTEX_UNLOCK(dbenv, &renv->mutex); /* Close the locking file handle. */ if (dbenv->lockfhp != NULL) { (void)__os_closehandle(dbenv, dbenv->lockfhp); dbenv->lockfhp = NULL; } /* * If we are destroying the environment, destroy any system resources * the crypto and replication systems may have acquired and put in the * main region. */ if (destroy) {#ifdef HAVE_CRYPTO (void)__crypto_region_destroy(dbenv);#endif (void)__rep_region_destroy(dbenv); } /* * Release the region, and kill our reference. * * If we are destroying the environment, destroy any system resources * backing the mutex. */ if (destroy) { (void)__db_mutex_destroy(&renv->mutex); (void)__db_mutex_destroy(&infop->rp->mutex); /* * Only free the REGION structure itself if it was separately * allocated from the heap. */ if (F_ISSET(dbenv, DB_ENV_PRIVATE)) __db_shalloc_free(infop, infop->rp); } /* Reset the addr value that we "corrected" above. */ infop->addr = infop->primary; (void)__os_r_detach(dbenv, infop, destroy); if (infop->name != NULL) __os_free(dbenv, infop->name); /* * We set the DB_ENV->reginfo field to NULL here and discard its memory. * DB_ENV->remove calls __dbenv_remove to do the region remove, and * __dbenv_remove attached and then detaches from the region. We don't * want to return to DB_ENV->remove with a non-NULL DB_ENV->reginfo * field because it will attempt to detach again as part of its cleanup. */ __os_free(dbenv, dbenv->reginfo); dbenv->reginfo = NULL; return (0);}/* * __db_e_remove -- * Discard an environment if it's not in use. * * PUBLIC: int __db_e_remove __P((DB_ENV *, u_int32_t)); */int__db_e_remove(dbenv, flags) DB_ENV *dbenv; u_int32_t flags;{ REGENV *renv; REGINFO *infop, reginfo; REGION *rp; u_int32_t db_env_reset; int force, ret; force = LF_ISSET(DB_FORCE) ? 1 : 0; /* * This routine has to walk a nasty line between not looking into * the environment (which may be corrupted after an app or system * crash), and removing everything that needs removing. What we * do is: * 1. Connect to the environment (so it better be OK). * 2. If the environment is in use (reference count is non-zero), * return EBUSY. * 3. Overwrite the magic number so that any threads of control * attempting to connect will backoff and retry. * 4. Walk the list of regions. Connect to each region and then * disconnect with the destroy flag set. This shouldn't cause * any problems, even if the region is corrupted, because we * should never be looking inside the region. * 5. Walk the list of files in the directory, unlinking any * files that match a region name. Unlink the environment * file last. * * If the force flag is set, we do not acquire any locks during this * process. */ db_env_reset = F_ISSET(dbenv, DB_ENV_NOLOCKING | DB_ENV_NOPANIC); if (force) F_SET(dbenv, DB_ENV_NOLOCKING); F_SET(dbenv, DB_ENV_NOPANIC); /* Join the environment. */ if ((ret = __db_e_attach(dbenv, NULL)) != 0) { /* * If we can't join it, we assume that's because it doesn't * exist. It would be better to know why we failed, but it * probably isn't important. */ ret = 0; if (force) goto remfiles; goto done; } infop = dbenv->reginfo; renv = infop->primary; /* Lock the environment. */ MUTEX_LOCK(dbenv, &renv->mutex); /* * If it's in use, we're done unless we're forcing the issue or the * environment has panic'd. (Presumably, if the environment panic'd, * the thread holding the reference count may not have cleaned up.) */ if (renv->refcnt == 1 || renv->envpanic == 1 || force) { /* * Set the panic flag and overwrite the magic number. * * !!! * From this point on, there's no going back, we pretty * much ignore errors, and just whack on whatever we can. */ renv->envpanic = 1; renv->magic = 0; /* * Unlock the environment. We should no longer need the lock * because we've poisoned the pool, but we can't continue to * hold it either, because other routines may want it. */ MUTEX_UNLOCK(dbenv, &renv->mutex); /* * Attach to each sub-region and destroy it. * * !!! * The REGION_CREATE_OK flag is set for Windows/95 -- regions * are zero'd out when the last reference to the region goes * away, in which case the underlying OS region code requires * callers be prepared to create the region in order to join it. */ memset(®info, 0, sizeof(reginfo)); for (rp = SH_LIST_FIRST(&renv->regionq, __db_region); rp != NULL; rp = SH_LIST_NEXT(rp, q, __db_region)) { if (rp->type == REGION_TYPE_ENV) continue; /* * If we get here and can't attach and/or detach to the * region, it's a mess. Ignore errors, there's nothing * we can do about them. */ reginfo.id = rp->id; reginfo.flags = REGION_CREATE_OK; if (__db_r_attach(dbenv, ®info, 0) == 0) { R_UNLOCK(dbenv, ®info); (void)__db_r_detach(dbenv, ®info, 1); } } /* Destroy the environment's region. */ (void)__db_e_detach(dbenv, 1); /* Discard any remaining physical files. */remfiles: (void)__db_e_remfile(dbenv); } else { /* Unlock the environment. */ MUTEX_UNLOCK(dbenv, &renv->mutex); /* Discard the environment. */ (void)__db_e_detach(dbenv, 0); ret = EBUSY; }done: F_CLR(dbenv, DB_ENV_NOLOCKING | DB_ENV_NOPANIC); F_SET(dbenv, db_env_reset); return (ret);}/* * __db_e_remfile -- * Discard any region files in the filesystem. */static int__db_e_remfile(dbenv) DB_ENV *dbenv;{ int cnt, fcnt, lastrm, ret; const char *dir; char saved_char, *p, **names, *path, buf[sizeof(DB_REGION_FMT) + 20]; /* Get the full path of a file in the environment. */ (void)snprintf(buf, sizeof(buf), "%s", DB_REGION_ENV); if ((ret = __db_appname(dbenv, DB_APP_NONE, buf, 0, NULL, &path)) != 0) return (ret); /* Get the parent directory for the environment. */ if ((p = __db_rpath(path)) == NULL) { p = path; saved_char = *p; dir = PATH_DOT; } else { saved_char = *p; *p = '\0'; dir = path; } /* Get the list of file names. */ if ((ret = __os_dirlist(dbenv, dir, &names, &fcnt)) != 0) __db_err(dbenv, "%s: %s", dir, db_strerror(ret)); /* Restore the path, and free it. */ *p = saved_char; __os_free(dbenv, path); if (ret != 0) return (ret); /* * Remove files from the region directory. */ for (lastrm = -1, cnt = fcnt; --cnt >= 0;) { /* Skip anything outside our name space. */ if (strncmp(names[cnt], DB_REGION_PREFIX, sizeof(DB_REGION_PREFIX) - 1)) continue; /* Skip queue extent files. */ if (strncmp(names[cnt], "__dbq.", 6) == 0) continue; /* Skip replication files. */ if (strncmp(names[cnt], "__db.rep.", 9) == 0) continue; /* * Remove the primary environment region last, because it's * the key to this whole mess. */ if (strcmp(names[cnt], DB_REGION_ENV) == 0) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -