Main Page | Class Hierarchy | Data Structures | Directories | File List | Data Fields | Related Pages

env_failchk.c

00001 /*-
00002  * See the file LICENSE for redistribution information.
00003  *
00004  * Copyright (c) 2005
00005  *      Sleepycat Software.  All rights reserved.
00006  *
00007  * $Id: env_failchk.c,v 12.17 2005/11/07 14:51:52 bostic Exp $
00008  */
00009 
00010 #include "db_config.h"
00011 
00012 #ifndef NO_SYSTEM_INCLUDES
00013 #include <sys/types.h>
00014 #include <string.h>
00015 #endif
00016 
00017 #include "db_int.h"
00018 #include "dbinc/db_shash.h"
00019 #include "dbinc/mutex_int.h"
00020 #include "dbinc/db_page.h"
00021 #include "dbinc/db_am.h"
00022 #include "dbinc/hash.h"                 /* Needed for call to __ham_func5. */
00023 #include "dbinc/lock.h"
00024 #include "dbinc/txn.h"
00025 
00026 static int __env_in_api __P((DB_ENV *));
00027 
00028 /*
00029  * __env_failchk_pp --
00030  *      DB_ENV->failchk pre/post processing.
00031  *
00032  * PUBLIC: int __env_failchk_pp __P((DB_ENV *, u_int32_t));
00033  */
00034 int
00035 __env_failchk_pp(dbenv, flags)
00036         DB_ENV *dbenv;
00037         u_int32_t flags;
00038 {
00039         DB_THREAD_INFO *ip;
00040         int ret;
00041 
00042         PANIC_CHECK(dbenv);
00043         ENV_ILLEGAL_BEFORE_OPEN(dbenv, "DB_ENV->failchk");
00044 
00045         /*
00046          * DB_ENV->failchk requires self and is-alive functions.  We
00047          * have a default self function, but no is-alive function.
00048          */
00049         if (!ALIVE_ON(dbenv)) {
00050                 __db_err(dbenv,
00051         "DB_ENV->failchk requires DB_ENV->is_alive be configured");
00052                 return (EINVAL);
00053         }
00054 
00055         if (flags != 0)
00056                 return (__db_ferr(dbenv, "DB_ENV->failchk", 0));
00057 
00058         ENV_ENTER(dbenv, ip);
00059 
00060         /*
00061          * We check for dead threads in the API first as this would be likely
00062          * to hang other things we try later, like locks and transactions.
00063          */
00064         if ((ret = __env_in_api(dbenv)) != 0)
00065                 goto err;
00066 
00067         if (LOCKING_ON(dbenv) && (ret = __lock_failchk(dbenv)) != 0)
00068                 goto err;
00069 
00070         if (TXN_ON(dbenv) && (ret = __txn_failchk(dbenv)) != 0)
00071                 goto err;
00072 
00073 err:    ENV_LEAVE(dbenv, ip);
00074         return (ret);
00075 }
00076 
00077 /*
00078  * __env_thread_init --
00079  *      Initialize the thread control block table.
00080  *
00081  * PUBLIC: int __env_thread_init __P((DB_ENV *, int));
00082  */
00083 int
00084 __env_thread_init(dbenv, created)
00085         DB_ENV *dbenv;
00086         int created;
00087 {
00088         DB_HASHTAB *htab;
00089         DB_MUTEXMGR *mtxmgr;
00090         DB_MUTEXREGION *mtxregion;
00091         REGINFO *infop;
00092         THREAD_INFO *thread;
00093         int ret;
00094 
00095         mtxmgr = dbenv->mutex_handle;
00096         mtxregion = mtxmgr->reginfo.primary;
00097         infop = &mtxmgr->reginfo;
00098 
00099         if (mtxregion->thread_off == INVALID_ROFF) {
00100                 if (dbenv->thr_nbucket == 0) {
00101                         dbenv->thr_hashtab = NULL;
00102                         if (ALIVE_ON(dbenv)) {
00103                                 __db_err(dbenv,
00104                 "is_alive method specified but no thread region allocated");
00105                                 return (EINVAL);
00106                         }
00107                         return (0);
00108                 }
00109                 
00110                 if (!created) {
00111                         __db_err(dbenv,
00112                 "thread table must be allocated at environment create time");
00113                         return (EINVAL);
00114                 }
00115 
00116                 if ((ret = __db_shalloc(infop,
00117                      sizeof(THREAD_INFO), 0, &thread)) != 0) {
00118                         __db_err(dbenv,
00119                              "cannot allocate a thread status block");
00120                         return (ret);
00121                 }
00122                 memset(thread, 0, sizeof(*thread));
00123                 mtxregion->thread_off = R_OFFSET(infop, thread);
00124                 thread->thr_nbucket = __db_tablesize(dbenv->thr_nbucket);
00125                 if ((ret = __db_shalloc(infop,
00126                      thread->thr_nbucket * sizeof(DB_HASHTAB), 0, &htab)) != 0)
00127                         return (ret);
00128                 thread->thr_hashoff = R_OFFSET(infop, htab);
00129                 __db_hashinit(htab, thread->thr_nbucket);
00130                 thread->thr_max = dbenv->thr_max;
00131         } else {
00132                 thread = R_ADDR(infop, mtxregion->thread_off);
00133                 htab = R_ADDR(infop, thread->thr_hashoff);
00134         }
00135 
00136         dbenv->thr_hashtab = htab;
00137         dbenv->thr_nbucket = thread->thr_nbucket;
00138         dbenv->thr_max = thread->thr_max;
00139         return (0);
00140 }
00141 
00142 /*
00143  * __env_in_api --
00144  *      Look for threads which died in the api and complain.
00145  */
00146 static int
00147 __env_in_api(dbenv)
00148         DB_ENV *dbenv;
00149 {
00150         DB_HASHTAB *htab;
00151         DB_MUTEXMGR *mtxmgr;
00152         DB_MUTEXREGION *mtxregion;
00153         DB_THREAD_INFO *ip;
00154         REGINFO *infop;
00155         THREAD_INFO *thread;
00156         u_int32_t i;
00157 
00158         if ((htab = dbenv->thr_hashtab) == NULL)
00159                 return (EINVAL);
00160 
00161         mtxmgr = dbenv->mutex_handle;
00162         mtxregion = mtxmgr->reginfo.primary;
00163         infop = &mtxmgr->reginfo;
00164         thread = R_ADDR(infop, mtxregion->thread_off);
00165 
00166         for (i = 0; i < dbenv->thr_nbucket; i++)
00167                 SH_TAILQ_FOREACH(ip, &htab[i], dbth_links, __db_thread_info) {
00168                         if (ip->dbth_state == THREAD_SLOT_NOT_IN_USE ||
00169                             (ip->dbth_state == THREAD_OUT &&
00170                             thread->thr_count <  thread->thr_max))
00171                                 continue;
00172                         if (dbenv->is_alive(dbenv, ip->dbth_pid, ip->dbth_tid))
00173                                 continue;
00174                         if (ip->dbth_state == THREAD_OUT) {
00175                                 ip->dbth_state = THREAD_SLOT_NOT_IN_USE;
00176                                 continue;
00177                         }
00178                         return (__db_failed(dbenv,
00179                              "Thread died in Berkeley DB library",
00180                              ip->dbth_pid, ip->dbth_tid));
00181                 }
00182 
00183         return (0);
00184 }
00185 
00186 struct __db_threadid {
00187         pid_t pid;
00188         db_threadid_t tid;
00189 };
00190 
00191 static int __thread_id_cmp __P((struct __db_threadid *, DB_THREAD_INFO *));
00192 static int __thread_state_cmp __P((DB_THREAD_STATE, DB_THREAD_INFO *));
00193 
00194 static
00195 int __thread_id_cmp(id, ip)
00196         struct __db_threadid *id;
00197         DB_THREAD_INFO *ip;
00198 {
00199 #ifdef HAVE_INTEGRAL_THREAD_TYPE
00200         return (id->pid == ip->dbth_pid && id->tid == ip->dbth_tid);
00201 #else
00202         if (memcmp(&id->pid, &ip->dbth_pid, sizeof(id->pid)) != 0)
00203                 return (0);
00204         if (memcmp(&id->tid, &ip->dbth_tid, sizeof(id->tid)) != 0)
00205                 return (0);
00206         return (1);
00207 #endif
00208 }
00209 
00210 static
00211 int __thread_state_cmp(state, ip)
00212         DB_THREAD_STATE state;
00213         DB_THREAD_INFO *ip;
00214 {
00215         return (ip->dbth_state == state);
00216 }
00217 
00218 /*
00219  * PUBLIC: int __env_set_state __P((DB_ENV *,
00220  * PUBLIC:      DB_THREAD_INFO **, DB_THREAD_STATE));
00221  */
00222 int
00223 __env_set_state(dbenv, ipp, state)
00224         DB_ENV *dbenv;
00225         DB_THREAD_INFO **ipp;
00226         DB_THREAD_STATE state;
00227 {
00228         DB_HASHTAB *htab;
00229         DB_MUTEXMGR *mtxmgr;
00230         DB_MUTEXREGION *mtxregion;
00231         DB_THREAD_INFO *ip;
00232         struct __db_threadid id;
00233         REGINFO *infop;
00234         THREAD_INFO *thread;
00235         int ret;
00236         u_int32_t indx;
00237 
00238         htab = (DB_HASHTAB *)dbenv->thr_hashtab;
00239 
00240         dbenv->thread_id(dbenv, &id.pid, &id.tid);
00241 
00242         /*
00243          * Hashing of thread ids.  This is simple but could be replaced with
00244          * something more expensive if needed.
00245          */
00246 #ifdef HAVE_INTEGRAL_THREAD_TYPE
00247         /*
00248          * A thread ID may be a pointer, so explicitly cast to a pointer of
00249          * the appropriate size before doing the bitwise XOR.
00250          */
00251         indx = (u_int32_t)((uintptr_t)id.pid ^ (uintptr_t)id.tid);
00252 #else
00253         indx = __ham_func5(NULL, &id.tid, sizeof(id.tid));
00254 #endif
00255         indx %= dbenv->thr_nbucket;
00256         HASHLOOKUP(htab, indx,
00257              __db_thread_info, dbth_links, &id, ip, __thread_id_cmp);
00258 #ifdef DIAGNOSTIC
00259         if (state == THREAD_DIAGNOSTIC) {
00260                 *ipp = ip;
00261                 return (0);
00262         }
00263 #endif
00264 
00265         ret = 0;
00266         if (ip == NULL) {
00267                 mtxmgr = dbenv->mutex_handle;
00268                 mtxregion = mtxmgr->reginfo.primary;
00269                 infop = &mtxmgr->reginfo;
00270                 thread = R_ADDR(infop, mtxregion->thread_off);
00271                 MUTEX_SYSTEM_LOCK(dbenv);
00272 
00273                 /*
00274                  * If we are passed the specified max, try to reclaim one from
00275                  * our queue.  If failcheck has marked the slot not in use, we
00276                  * can take it, otherwise we must call is_alive before freeing
00277                  * it.
00278                  */
00279                 if (thread->thr_count >= thread->thr_max) {
00280                         HASHLOOKUP(htab, indx, __db_thread_info,
00281                             dbth_links, THREAD_OUT, ip, __thread_state_cmp);
00282                         while (ip != NULL &&
00283                             ip->dbth_state != THREAD_SLOT_NOT_IN_USE &&
00284                             (ip->dbth_state != THREAD_OUT || !ALIVE_ON(dbenv) ||
00285                             dbenv->is_alive(dbenv,
00286                             ip->dbth_pid, ip->dbth_tid))) {
00287                                 ip = SH_TAILQ_NEXT(ip,
00288                                     dbth_links, __db_thread_info);
00289                         }
00290                         if (ip != NULL)
00291                                 goto init;
00292                 }
00293 
00294                 thread->thr_count++;
00295                 if ((ret = __db_shalloc(infop,
00296                      sizeof(DB_THREAD_INFO), 0, &ip)) == 0) {
00297                         memset(ip, 0, sizeof(*ip));
00298                         /*
00299                          * This assumes we can link atomically since we do
00300                          * no locking here.  We never use the backpointer
00301                          * so we only need to be able to write an offset
00302                          * atomically.
00303                          */
00304                         HASHINSERT(htab,
00305                             indx, __db_thread_info, dbth_links, ip);
00306 init:                   ip->dbth_pid = id.pid;
00307                         ip->dbth_tid = id.tid;
00308                         ip->dbth_state = state;
00309                 }
00310                 MUTEX_SYSTEM_UNLOCK(dbenv);
00311         } else
00312                 ip->dbth_state = state;
00313         *ipp = ip;
00314 
00315         return (ret);
00316 }
00317 
00318 /*
00319  * __env_thread_id_string --
00320  *      Convert a thread id to a string.
00321  *
00322  * PUBLIC: char *__env_thread_id_string
00323  * PUBLIC:     __P((DB_ENV *, pid_t, db_threadid_t, char *));
00324  */
00325 char *
00326 __env_thread_id_string(dbenv, pid, tid, buf)
00327         DB_ENV *dbenv;
00328         pid_t pid;
00329         db_threadid_t tid;
00330         char *buf;
00331 {
00332 #ifdef HAVE_INTEGRAL_THREAD_TYPE
00333 #ifdef UINT64_FMT
00334         char fmt[10];
00335 
00336         snprintf(fmt, sizeof(fmt), "%s/%s", UINT64_FMT, UINT64_FMT);
00337         snprintf(buf,
00338             DB_THREADID_STRLEN, fmt, (u_int64_t)pid, (u_int64_t)(uintptr_t)tid);
00339 #else
00340         snprintf(buf, DB_THREADID_STRLEN, "%lu/%lu", (u_long)pid, (u_long)tid);
00341 #endif
00342 #else
00343 #ifdef UINT64_FMT
00344         char fmt[10];
00345 
00346         snprintf(fmt, sizeof(fmt), "%s/TID", UINT64_FMT);
00347         snprintf(buf, DB_THREADID_STRLEN, fmt, (u_int64_t)pid);
00348 #else
00349         snprintf(buf, DB_THREADID_STRLEN, "%lu/TID", (u_long)pid);
00350 #endif
00351 #endif
00352         COMPQUIET(dbenv, NULL);
00353         COMPQUIET(*(u_int8_t *)&tid, 0);
00354 
00355         return (buf);
00356 }

Generated on Sun Dec 25 12:14:24 2005 for Berkeley DB 4.4.16 by  doxygen 1.4.2