| --- a/src/dbinc_auto/int_def.in |
| +++ b/src/dbinc_auto/int_def.in |
| @@ -1373,6 +1373,7 @@ |
| #define __memp_pgread __memp_pgread@DB_VERSION_UNIQUE_NAME@ |
| #define __memp_pg __memp_pg@DB_VERSION_UNIQUE_NAME@ |
| #define __memp_bhfree __memp_bhfree@DB_VERSION_UNIQUE_NAME@ |
| +#define __memp_bh_clear_dirty __memp_bh_clear_dirty@DB_VERSION_UNIQUE_NAME@ |
| #define __memp_fget_pp __memp_fget_pp@DB_VERSION_UNIQUE_NAME@ |
| #define __memp_fget __memp_fget@DB_VERSION_UNIQUE_NAME@ |
| #define __memp_fcreate_pp __memp_fcreate_pp@DB_VERSION_UNIQUE_NAME@ |
| @@ -1397,6 +1398,7 @@ |
| #define __memp_fclose __memp_fclose@DB_VERSION_UNIQUE_NAME@ |
| #define __memp_mf_discard __memp_mf_discard@DB_VERSION_UNIQUE_NAME@ |
| #define __memp_inmemlist __memp_inmemlist@DB_VERSION_UNIQUE_NAME@ |
| +#define __memp_mf_mark_dead __memp_mf_mark_dead@DB_VERSION_UNIQUE_NAME@ |
| #define __memp_fput_pp __memp_fput_pp@DB_VERSION_UNIQUE_NAME@ |
| #define __memp_fput __memp_fput@DB_VERSION_UNIQUE_NAME@ |
| #define __memp_unpin_buffers __memp_unpin_buffers@DB_VERSION_UNIQUE_NAME@ |
| @@ -1455,6 +1457,7 @@ |
| #define __mp_xxx_fh __mp_xxx_fh@DB_VERSION_UNIQUE_NAME@ |
| #define __memp_sync_int __memp_sync_int@DB_VERSION_UNIQUE_NAME@ |
| #define __memp_mf_sync __memp_mf_sync@DB_VERSION_UNIQUE_NAME@ |
| +#define __memp_purge_dead_files __memp_purge_dead_files@DB_VERSION_UNIQUE_NAME@ |
| #define __memp_trickle_pp __memp_trickle_pp@DB_VERSION_UNIQUE_NAME@ |
| #define __mutex_alloc __mutex_alloc@DB_VERSION_UNIQUE_NAME@ |
| #define __mutex_alloc_int __mutex_alloc_int@DB_VERSION_UNIQUE_NAME@ |
| --- a/src/dbinc_auto/mp_ext.h |
| +++ b/src/dbinc_auto/mp_ext.h |
| @@ -16,6 +16,7 @@ int __memp_bhwrite __P((DB_MPOOL *, DB_M |
| int __memp_pgread __P((DB_MPOOLFILE *, BH *, int)); |
| int __memp_pg __P((DB_MPOOLFILE *, db_pgno_t, void *, int)); |
| int __memp_bhfree __P((DB_MPOOL *, REGINFO *, MPOOLFILE *, DB_MPOOL_HASH *, BH *, u_int32_t)); |
| +void __memp_bh_clear_dirty __P((ENV*, DB_MPOOL_HASH *, BH *)); |
| int __memp_fget_pp __P((DB_MPOOLFILE *, db_pgno_t *, DB_TXN *, u_int32_t, void *)); |
| int __memp_fget __P((DB_MPOOLFILE *, db_pgno_t *, DB_THREAD_INFO *, DB_TXN *, u_int32_t, void *)); |
| int __memp_fcreate_pp __P((DB_ENV *, DB_MPOOLFILE **, u_int32_t)); |
| @@ -40,6 +41,7 @@ int __memp_fclose_pp __P((DB_MPOOLFILE * |
| int __memp_fclose __P((DB_MPOOLFILE *, u_int32_t)); |
| int __memp_mf_discard __P((DB_MPOOL *, MPOOLFILE *, int)); |
| int __memp_inmemlist __P((ENV *, char ***, int *)); |
| +void __memp_mf_mark_dead __P((DB_MPOOL *, MPOOLFILE *, int*)); |
| int __memp_fput_pp __P((DB_MPOOLFILE *, void *, DB_CACHE_PRIORITY, u_int32_t)); |
| int __memp_fput __P((DB_MPOOLFILE *, DB_THREAD_INFO *, void *, DB_CACHE_PRIORITY)); |
| int __memp_unpin_buffers __P((ENV *, DB_THREAD_INFO *)); |
| @@ -98,6 +100,7 @@ int __memp_fsync __P((DB_MPOOLFILE *)); |
| int __mp_xxx_fh __P((DB_MPOOLFILE *, DB_FH **)); |
| int __memp_sync_int __P((ENV *, DB_MPOOLFILE *, u_int32_t, u_int32_t, u_int32_t *, int *)); |
| int __memp_mf_sync __P((DB_MPOOL *, MPOOLFILE *, int)); |
| +int __memp_purge_dead_files __P((ENV *)); |
| int __memp_trickle_pp __P((DB_ENV *, int, int *)); |
| |
| #if defined(__cplusplus) |
| --- a/src/mp/mp_bh.c |
| +++ b/src/mp/mp_bh.c |
| @@ -474,11 +474,8 @@ file_dead: |
| if (F_ISSET(bhp, BH_DIRTY | BH_TRASH)) { |
| MUTEX_LOCK(env, hp->mtx_hash); |
| DB_ASSERT(env, !SH_CHAIN_HASNEXT(bhp, vc)); |
| - if (ret == 0 && F_ISSET(bhp, BH_DIRTY)) { |
| - F_CLR(bhp, BH_DIRTY | BH_DIRTY_CREATE); |
| - DB_ASSERT(env, atomic_read(&hp->hash_page_dirty) > 0); |
| - atomic_dec(env, &hp->hash_page_dirty); |
| - } |
| + if (ret == 0) |
| + __memp_bh_clear_dirty(env, hp, bhp); |
| |
| /* put the page back if necessary. */ |
| if ((ret != 0 || BH_REFCOUNT(bhp) > 1) && |
| @@ -688,3 +685,29 @@ no_hp: if (mfp != NULL) |
| |
| return (ret); |
| } |
| + |
| +/* |
| + * __memp_bh_clear_dirty -- |
| + * Clear the dirty flag of of a buffer. Calls on the same buffer must be |
| + * serialized to get the accounting correct. This can be achieved by |
| + * acquiring an exclusive lock on the buffer, a shared lock on the |
| + * buffer plus an exclusive lock on the hash bucket, or some other |
| + * mechanism that guarantees single-thread access to the entire region |
| + * (e.g. during __memp_region_bhfree()). |
| + * |
| + * PUBLIC: void __memp_bh_clear_dirty __P((ENV*, DB_MPOOL_HASH *, BH *)); |
| + */ |
| +void |
| +__memp_bh_clear_dirty(env, hp, bhp) |
| + ENV *env; |
| + DB_MPOOL_HASH *hp; |
| + BH *bhp; |
| +{ |
| + COMPQUIET(env, env); |
| + if (F_ISSET(bhp, BH_DIRTY)) { |
| + F_CLR(bhp, BH_DIRTY | BH_DIRTY_CREATE); |
| + DB_ASSERT(env, atomic_read(&hp->hash_page_dirty) > 0); |
| + (void)atomic_dec(env, &hp->hash_page_dirty); |
| + } |
| +} |
| + |
| --- a/src/mp/mp_fget.c |
| +++ b/src/mp/mp_fget.c |
| @@ -439,12 +439,7 @@ thawed: need_free = (atomic_dec(env, & |
| if (flags == DB_MPOOL_FREE) { |
| freebuf: MUTEX_LOCK(env, hp->mtx_hash); |
| h_locked = 1; |
| - if (F_ISSET(bhp, BH_DIRTY)) { |
| - F_CLR(bhp, BH_DIRTY | BH_DIRTY_CREATE); |
| - DB_ASSERT(env, |
| - atomic_read(&hp->hash_page_dirty) > 0); |
| - atomic_dec(env, &hp->hash_page_dirty); |
| - } |
| + __memp_bh_clear_dirty(env, hp, bhp); |
| |
| /* |
| * If the buffer we found is already freed, we're done. |
| --- a/src/mp/mp_fopen.c |
| +++ b/src/mp/mp_fopen.c |
| @@ -14,6 +14,7 @@ |
| #include "dbinc/db_page.h" |
| #include "dbinc/hash.h" |
| |
| +static int __memp_count_dead_mutex __P((DB_MPOOL *, u_int32_t *)); |
| static int __memp_mpf_alloc __P((DB_MPOOL *, |
| DB_MPOOLFILE *, const char *, u_int32_t, u_int32_t, MPOOLFILE **)); |
| static int __memp_mpf_find __P((ENV *, |
| @@ -711,7 +712,11 @@ __memp_mpf_find(env, dbmfp, hp, path, fl |
| */ |
| if (LF_ISSET(DB_TRUNCATE)) { |
| MUTEX_LOCK(env, mfp->mutex); |
| - mfp->deadfile = 1; |
| + /* |
| + * We cannot purge dead files here, because the caller |
| + * is holding the mutex of the hash bucket of mfp. |
| + */ |
| + __memp_mf_mark_dead(dbmp, mfp, NULL); |
| MUTEX_UNLOCK(env, mfp->mutex); |
| continue; |
| } |
| @@ -909,10 +914,11 @@ __memp_fclose(dbmfp, flags) |
| MPOOLFILE *mfp; |
| char *rpath; |
| u_int32_t ref; |
| - int deleted, ret, t_ret; |
| + int deleted, purge_dead, ret, t_ret; |
| |
| env = dbmfp->env; |
| dbmp = env->mp_handle; |
| + purge_dead = 0; |
| ret = 0; |
| |
| /* |
| @@ -1006,7 +1012,7 @@ __memp_fclose(dbmfp, flags) |
| if (--mfp->mpf_cnt == 0 || LF_ISSET(DB_MPOOL_DISCARD)) { |
| if (LF_ISSET(DB_MPOOL_DISCARD) || |
| F_ISSET(mfp, MP_TEMP) || mfp->unlink_on_close) { |
| - mfp->deadfile = 1; |
| + __memp_mf_mark_dead(dbmp, mfp, &purge_dead); |
| } |
| if (mfp->unlink_on_close) { |
| if ((t_ret = __db_appname(dbmp->env, DB_APP_DATA, |
| @@ -1039,6 +1045,8 @@ __memp_fclose(dbmfp, flags) |
| } |
| if (!deleted && !LF_ISSET(DB_MPOOL_NOLOCK)) |
| MUTEX_UNLOCK(env, mfp->mutex); |
| + if (purge_dead) |
| + (void)__memp_purge_dead_files(env); |
| |
| done: /* Discard the DB_MPOOLFILE structure. */ |
| if (dbmfp->pgcookie != NULL) { |
| @@ -1093,7 +1101,7 @@ __memp_mf_discard(dbmp, mfp, hp_locked) |
| * mutex so we don't deadlock. Make sure nobody ever looks at this |
| * structure again. |
| */ |
| - mfp->deadfile = 1; |
| + __memp_mf_mark_dead(dbmp, mfp, NULL); |
| |
| /* Discard the mutex we're holding and return it too the pool. */ |
| MUTEX_UNLOCK(env, mfp->mutex); |
| @@ -1218,3 +1226,104 @@ nomem: MUTEX_UNLOCK(env, hp->mtx_hash); |
| *namesp = NULL; |
| return (ret); |
| } |
| + |
| +/* |
| + * __memp_mf_mark_dead -- |
| + * Mark an MPOOLFILE as dead because its contents are no longer necessary. |
| + * This happens when removing, truncation, or closing an unnamed in-memory |
| + * database. Return, in the purgep parameter, whether the caller should |
| + * call __memp_purge_dead_files() after the lock on mfp is released. The |
| + * caller must hold an exclusive lock on the mfp handle. |
| + * |
| + * PUBLIC: void __memp_mf_mark_dead __P((DB_MPOOL *, MPOOLFILE *, int*)); |
| + */ |
| +void |
| +__memp_mf_mark_dead(dbmp, mfp, purgep) |
| + DB_MPOOL *dbmp; |
| + MPOOLFILE *mfp; |
| + int *purgep; |
| +{ |
| + ENV *env; |
| +#ifdef HAVE_MUTEX_SUPPORT |
| + REGINFO *infop; |
| + DB_MUTEXREGION *mtxregion; |
| + u_int32_t mutex_max, mutex_inuse, dead_mutex; |
| +#endif |
| + |
| + if (purgep != NULL) |
| + *purgep = 0; |
| + |
| + env = dbmp->env; |
| + |
| +#ifdef HAVE_MUTEX_SUPPORT |
| + MUTEX_REQUIRED(env, mfp->mutex); |
| + |
| + if (MUTEX_ON(env) && mfp->deadfile == 0) { |
| + infop = &env->mutex_handle->reginfo; |
| + mtxregion = infop->primary; |
| + |
| + mutex_inuse = mtxregion->stat.st_mutex_inuse; |
| + if ((mutex_max = env->dbenv->mutex_max) == 0) |
| + mutex_max = infop->rp->max / mtxregion->mutex_size; |
| + |
| + /* |
| + * Purging dead pages requires a full scan of the entire cache |
| + * buffer, so it is a slow operation. We only want to do it |
| + * when it is necessary and provides enough benefits. Below is |
| + * a simple heuristic that determines when to purge all dead |
| + * pages. |
| + */ |
| + if (purgep != NULL && mutex_inuse > mutex_max - 200) { |
| + /* |
| + * If the mutex region is almost full and there are |
| + * many mutexes held by dead files, purge dead files. |
| + */ |
| + (void)__memp_count_dead_mutex(dbmp, &dead_mutex); |
| + dead_mutex += mfp->block_cnt + 1; |
| + |
| + if (dead_mutex > mutex_inuse / 20) |
| + *purgep = 1; |
| + } |
| + } |
| +#endif |
| + |
| + mfp->deadfile = 1; |
| +} |
| + |
| +/* |
| + * __memp_count_dead_mutex -- |
| + * Estimate the number of mutexes held by dead files. |
| + */ |
| +static int |
| +__memp_count_dead_mutex(dbmp, dead_mutex) |
| + DB_MPOOL *dbmp; |
| + u_int32_t *dead_mutex; |
| +{ |
| + ENV *env; |
| + DB_MPOOL_HASH *hp; |
| + MPOOL *mp; |
| + MPOOLFILE *mfp; |
| + u_int32_t mutex_per_file; |
| + int busy, i; |
| + |
| + env = dbmp->env; |
| + *dead_mutex = 0; |
| + mutex_per_file = 1; |
| +#ifndef HAVE_ATOMICFILEREAD |
| + mutex_per_file = 2; |
| +#endif |
| + mp = dbmp->reginfo[0].primary; |
| + hp = R_ADDR(dbmp->reginfo, mp->ftab); |
| + for (i = 0; i < MPOOL_FILE_BUCKETS; i++, hp++) { |
| + busy = MUTEX_TRYLOCK(env, hp->mtx_hash); |
| + if (busy) |
| + continue; |
| + SH_TAILQ_FOREACH(mfp, &hp->hash_bucket, q, __mpoolfile) { |
| + if (mfp->deadfile) |
| + *dead_mutex += mfp->block_cnt + mutex_per_file; |
| + } |
| + MUTEX_UNLOCK(env, hp->mtx_hash); |
| + } |
| + |
| + return (0); |
| +} |
| --- a/src/mp/mp_method.c |
| +++ b/src/mp/mp_method.c |
| @@ -640,7 +640,7 @@ __memp_nameop(env, fileid, newname, full |
| MPOOLFILE *mfp; |
| roff_t newname_off; |
| u_int32_t bucket; |
| - int locked, ret; |
| + int locked, purge_dead, ret; |
| size_t nlen; |
| void *p; |
| |
| @@ -657,6 +657,7 @@ __memp_nameop(env, fileid, newname, full |
| nhp = NULL; |
| p = NULL; |
| locked = ret = 0; |
| + purge_dead = 0; |
| |
| if (!MPOOL_ON(env)) |
| goto fsop; |
| @@ -749,7 +750,7 @@ __memp_nameop(env, fileid, newname, full |
| */ |
| if (mfp->no_backing_file) |
| mfp->mpf_cnt--; |
| - mfp->deadfile = 1; |
| + __memp_mf_mark_dead(dbmp, mfp, &purge_dead); |
| MUTEX_UNLOCK(env, mfp->mutex); |
| } else { |
| /* |
| @@ -808,6 +809,12 @@ err: if (p != NULL) { |
| if (nhp != NULL && nhp != hp) |
| MUTEX_UNLOCK(env, nhp->mtx_hash); |
| } |
| + /* |
| + * __memp_purge_dead_files() must be called when the hash bucket is |
| + * unlocked. |
| + */ |
| + if (purge_dead) |
| + (void)__memp_purge_dead_files(env); |
| return (ret); |
| } |
| |
| --- a/src/mp/mp_sync.c |
| +++ b/src/mp/mp_sync.c |
| @@ -26,6 +26,7 @@ static int __memp_close_flush_files __P( |
| static int __memp_sync_files __P((ENV *)); |
| static int __memp_sync_file __P((ENV *, |
| MPOOLFILE *, void *, u_int32_t *, u_int32_t)); |
| +static inline void __update_err_ret(int, int*); |
| |
| /* |
| * __memp_walk_files -- |
| @@ -965,3 +966,123 @@ __bhcmp(p1, p2) |
| return (1); |
| return (0); |
| } |
| + |
| +/* |
| + * __memp_purge_dead_files -- |
| + * Remove all dead files and their buffers from the mpool. The caller |
| + * cannot hold any lock on the dead MPOOLFILE handles, their buffers |
| + * or their hash buckets. |
| + * |
| + * PUBLIC: int __memp_purge_dead_files __P((ENV *)); |
| + */ |
| +int |
| +__memp_purge_dead_files(env) |
| + ENV *env; |
| +{ |
| + BH *bhp; |
| + DB_MPOOL *dbmp; |
| + DB_MPOOL_HASH *hp, *hp_end; |
| + REGINFO *infop; |
| + MPOOL *c_mp, *mp; |
| + MPOOLFILE *mfp; |
| + u_int32_t i_cache; |
| + int ret, t_ret, h_lock; |
| + |
| + if (!MPOOL_ON(env)) |
| + return (0); |
| + |
| + dbmp = env->mp_handle; |
| + mp = dbmp->reginfo[0].primary; |
| + ret = t_ret = h_lock = 0; |
| + |
| + /* |
| + * Walk each cache's list of buffers and free all buffers whose |
| + * MPOOLFILE is marked as dead. |
| + */ |
| + for (i_cache = 0; i_cache < mp->nreg; i_cache++) { |
| + infop = &dbmp->reginfo[i_cache]; |
| + c_mp = infop->primary; |
| + |
| + hp = R_ADDR(infop, c_mp->htab); |
| + hp_end = &hp[c_mp->htab_buckets]; |
| + for (; hp < hp_end; hp++) { |
| + /* Skip empty buckets. */ |
| + if (SH_TAILQ_FIRST(&hp->hash_bucket, __bh) == NULL) |
| + continue; |
| + |
| + /* |
| + * Search for a dead buffer. Other places that call |
| + * __memp_bhfree() acquire the buffer lock before the |
| + * hash bucket lock. Even though we acquire the two |
| + * locks in reverse order, we cannot deadlock here |
| + * because we don't block waiting for the locks. |
| + */ |
| + t_ret = MUTEX_TRYLOCK(env, hp->mtx_hash); |
| + if (t_ret != 0) { |
| + __update_err_ret(t_ret, &ret); |
| + continue; |
| + } |
| + h_lock = 1; |
| + SH_TAILQ_FOREACH(bhp, &hp->hash_bucket, hq, __bh) { |
| + /* Skip buffers that are being used. */ |
| + if (BH_REFCOUNT(bhp) > 0) |
| + continue; |
| + |
| + mfp = R_ADDR(dbmp->reginfo, bhp->mf_offset); |
| + if (!mfp->deadfile) |
| + continue; |
| + |
| + /* Found a dead buffer. Prepare to free it. */ |
| + t_ret = MUTEX_TRYLOCK(env, bhp->mtx_buf); |
| + if (t_ret != 0) { |
| + __update_err_ret(t_ret, &ret); |
| + continue; |
| + } |
| + |
| + DB_ASSERT(env, (!F_ISSET(bhp, BH_EXCLUSIVE) && |
| + BH_REFCOUNT(bhp) == 0)); |
| + F_SET(bhp, BH_EXCLUSIVE); |
| + (void)atomic_inc(env, &bhp->ref); |
| + |
| + __memp_bh_clear_dirty(env, hp, bhp); |
| + |
| + /* |
| + * Free the buffer. The buffer and hash bucket |
| + * are unlocked by __memp_bhfree. |
| + */ |
| + if ((t_ret = __memp_bhfree(dbmp, infop, mfp, |
| + hp, bhp, BH_FREE_FREEMEM)) == 0) |
| + /* |
| + * Decrement hp, so the next turn will |
| + * search the same bucket again. |
| + */ |
| + hp--; |
| + else |
| + __update_err_ret(t_ret, &ret); |
| + |
| + /* |
| + * The hash bucket is unlocked, we need to |
| + * start over again. |
| + */ |
| + h_lock = 0; |
| + break; |
| + } |
| + |
| + if (h_lock) { |
| + MUTEX_UNLOCK(env, hp->mtx_hash); |
| + h_lock = 0; |
| + } |
| + } |
| + } |
| + |
| + return (ret); |
| +} |
| + |
| +static inline void |
| +__update_err_ret(t_ret, retp) |
| + int t_ret; |
| + int *retp; |
| +{ |
| + if (t_ret != 0 && t_ret != DB_LOCK_NOTGRANTED && *retp == 0) |
| + *retp = t_ret; |
| +} |
| --- a/src/mp/mp_trickle.c |
| +++ b/src/mp/mp_trickle.c |
| @@ -67,6 +67,10 @@ __memp_trickle(env, pct, nwrotep) |
| return (EINVAL); |
| } |
| |
| + /* First we purge all dead files and their buffers. */ |
| + if ((ret = __memp_purge_dead_files(env)) != 0) |
| + return (ret); |
| + |
| /* |
| * Loop through the caches counting total/dirty buffers. |
| * |
| --- a/src/mutex/mut_region.c |
| +++ b/src/mutex/mut_region.c |
| @@ -17,7 +17,7 @@ |
| static db_size_t __mutex_align_size __P((ENV *)); |
| static int __mutex_region_init __P((ENV *, DB_MUTEXMGR *)); |
| static size_t __mutex_region_size __P((ENV *)); |
| -static size_t __mutex_region_max __P((ENV *)); |
| +static size_t __mutex_region_max __P((ENV *, u_int32_t)); |
| |
| /* |
| * __mutex_open -- |
| @@ -34,7 +34,7 @@ __mutex_open(env, create_ok) |
| DB_MUTEXMGR *mtxmgr; |
| DB_MUTEXREGION *mtxregion; |
| size_t size; |
| - u_int32_t cpu_count; |
| + u_int32_t cpu_count, mutex_needed; |
| int ret; |
| #ifndef HAVE_ATOMIC_SUPPORT |
| u_int i; |
| @@ -61,19 +61,20 @@ __mutex_open(env, create_ok) |
| } |
| |
| /* |
| - * If the user didn't set an absolute value on the number of mutexes |
| - * we'll need, figure it out. We're conservative in our allocation, |
| - * we need mutexes for DB handles, group-commit queues and other things |
| - * applications allocate at run-time. The application may have kicked |
| - * up our count to allocate its own mutexes, add that in. |
| + * Figure out the number of mutexes we'll need. We're conservative in |
| + * our allocation, we need mutexes for DB handles, group-commit queues |
| + * and other things applications allocate at run-time. The application |
| + * may have kicked up our count to allocate its own mutexes, add that |
| + * in. |
| */ |
| + mutex_needed = |
| + __lock_region_mutex_count(env) + |
| + __log_region_mutex_count(env) + |
| + __memp_region_mutex_count(env) + |
| + __txn_region_mutex_count(env); |
| if (dbenv->mutex_cnt == 0 && |
| F_ISSET(env, ENV_PRIVATE | ENV_THREAD) != ENV_PRIVATE) |
| - dbenv->mutex_cnt = |
| - __lock_region_mutex_count(env) + |
| - __log_region_mutex_count(env) + |
| - __memp_region_mutex_count(env) + |
| - __txn_region_mutex_count(env); |
| + dbenv->mutex_cnt = mutex_needed; |
| |
| if (dbenv->mutex_max != 0 && dbenv->mutex_cnt > dbenv->mutex_max) |
| dbenv->mutex_cnt = dbenv->mutex_max; |
| @@ -90,8 +91,8 @@ __mutex_open(env, create_ok) |
| size = __mutex_region_size(env); |
| if (create_ok) |
| F_SET(&mtxmgr->reginfo, REGION_CREATE_OK); |
| - if ((ret = __env_region_attach(env, |
| - &mtxmgr->reginfo, size, size + __mutex_region_max(env))) != 0) |
| + if ((ret = __env_region_attach(env, &mtxmgr->reginfo, |
| + size, size + __mutex_region_max(env, mutex_needed))) != 0) |
| goto err; |
| |
| /* If we created the region, initialize it. */ |
| @@ -352,9 +353,13 @@ __mutex_region_size(env) |
| |
| s = sizeof(DB_MUTEXMGR) + 1024; |
| |
| - /* We discard one mutex for the OOB slot. */ |
| + /* |
| + * We discard one mutex for the OOB slot. Make sure mutex_cnt doesn't |
| + * overflow. |
| + */ |
| s += __env_alloc_size( |
| - (dbenv->mutex_cnt + 1) *__mutex_align_size(env)); |
| + (dbenv->mutex_cnt + (dbenv->mutex_cnt == UINT32_MAX ? 0 : 1)) * |
| + __mutex_align_size(env)); |
| |
| return (s); |
| } |
| @@ -364,28 +369,42 @@ __mutex_region_size(env) |
| * Return the amount of space needed to reach the maximum size. |
| */ |
| static size_t |
| -__mutex_region_max(env) |
| +__mutex_region_max(env, mutex_needed) |
| ENV *env; |
| + u_int32_t mutex_needed; |
| { |
| DB_ENV *dbenv; |
| - u_int32_t max; |
| + u_int32_t max, mutex_cnt; |
| |
| dbenv = env->dbenv; |
| + mutex_cnt = dbenv->mutex_cnt; |
| |
| - if ((max = dbenv->mutex_max) == 0) { |
| + /* |
| + * We want to limit the region size to accommodate at most UINT32_MAX |
| + * mutexes. If mutex_cnt is UINT32_MAX, no more space is allowed. |
| + */ |
| + if ((max = dbenv->mutex_max) == 0 && mutex_cnt != UINT32_MAX) |
| if (F_ISSET(env, ENV_PRIVATE | ENV_THREAD) == ENV_PRIVATE) |
| - max = dbenv->mutex_inc + 1; |
| - else |
| + if (dbenv->mutex_inc + 1 < UINT32_MAX - mutex_cnt) |
| + max = dbenv->mutex_inc + 1 + mutex_cnt; |
| + else |
| + max = UINT32_MAX; |
| + else { |
| max = __lock_region_mutex_max(env) + |
| __txn_region_mutex_max(env) + |
| __log_region_mutex_max(env) + |
| dbenv->mutex_inc + 100; |
| - } else if (max <= dbenv->mutex_cnt) |
| + if (max < UINT32_MAX - mutex_needed) |
| + max += mutex_needed; |
| + else |
| + max = UINT32_MAX; |
| + } |
| + |
| + if (max <= mutex_cnt) |
| return (0); |
| else |
| - max -= dbenv->mutex_cnt; |
| - |
| - return ( __env_alloc_size(max * __mutex_align_size(env))); |
| + return (__env_alloc_size( |
| + (max - mutex_cnt) * __mutex_align_size(env))); |
| } |
| |
| #ifdef HAVE_MUTEX_SYSTEM_RESOURCES |