00001 /*- 00002 * See the file LICENSE for redistribution information. 00003 * 00004 * Copyright (c) 1996, 1997, 1998, 1999, 2000 00005 * Sleepycat Software. All rights reserved. 00006 */ 00007 00008 #include "config.h" 00009 00010 #ifndef lint 00011 static const char revid[] = "$Id: txn__region_8c-source.html,v 1.1 2008年06月08日 10:24:57 sebdiaz Exp $"; 00012 #endif /* not lint */ 00013 00014 #ifndef NO_SYSTEM_INCLUDES 00015 #include <sys/types.h> 00016 00017 #if TIME_WITH_SYS_TIME 00018 #include <sys/time.h> 00019 #include <time.h> 00020 #else 00021 #if HAVE_SYS_TIME_H 00022 #include <sys/time.h> 00023 #else 00024 #include <time.h> 00025 #endif 00026 #endif 00027 00028 #include <string.h> 00029 #endif 00030 00031 #ifdef HAVE_RPC 00032 #include "db_server.h" 00033 #endif 00034 00035 #include "db_int.h" 00036 #include "db_page.h" 00037 #include "txn.h" 00038 #include "db_am.h" 00039 00040 #ifdef HAVE_RPC 00041 #include "gen_client_ext.h" 00042 #include "rpc_client_ext.h" 00043 #endif 00044 00045 static int __txn_init __P((DB_ENV *, DB_TXNMGR *)); 00046 static int __txn_set_tx_max __P((DB_ENV *, u_int32_t)); 00047 static int __txn_set_tx_recover __P((DB_ENV *, 00048 int (*)(DB_ENV *, DBT *, DB_LSN *, db_recops, void *))); 00049 static int __txn_set_tx_timestamp __P((DB_ENV *, time_t *)); 00050 00051 /* 00052 * CDB___txn_dbenv_create -- 00053 * Transaction specific initialization of the DB_ENV structure. 00054 * 00055 * PUBLIC: void CDB___txn_dbenv_create __P((DB_ENV *)); 00056 */ 00057 void 00058 CDB___txn_dbenv_create(dbenv) 00059 DB_ENV *dbenv; 00060 { 00061 dbenv->tx_max = DEF_MAX_TXNS; 00062 00063 dbenv->set_tx_max = __txn_set_tx_max; 00064 dbenv->set_tx_recover = __txn_set_tx_recover; 00065 dbenv->set_tx_timestamp = __txn_set_tx_timestamp; 00066 00067 #ifdef HAVE_RPC 00068 /* 00069 * If we have a client, overwrite what we just setup to point to 00070 * client functions. 00071 */ 00072 if (F_ISSET(dbenv, DB_ENV_RPCCLIENT)) { 00073 dbenv->set_tx_max = __dbcl_set_tx_max; 00074 dbenv->set_tx_recover = __dbcl_set_tx_recover; 00075 dbenv->set_tx_timestamp = __dbcl_set_tx_timestamp; 00076 } 00077 #endif 00078 } 00079 00080 /* 00081 * __txn_set_tx_max -- 00082 * Set the size of the transaction table. 00083 */ 00084 static int 00085 __txn_set_tx_max(dbenv, tx_max) 00086 DB_ENV *dbenv; 00087 u_int32_t tx_max; 00088 { 00089 ENV_ILLEGAL_AFTER_OPEN(dbenv, "set_tx_max"); 00090 00091 dbenv->tx_max = tx_max; 00092 return (0); 00093 } 00094 00095 /* 00096 * __txn_set_tx_recover -- 00097 * Set the transaction abort recover function. 00098 */ 00099 static int 00100 __txn_set_tx_recover(dbenv, tx_recover) 00101 DB_ENV *dbenv; 00102 int (*tx_recover) __P((DB_ENV *, DBT *, DB_LSN *, db_recops, void *)); 00103 { 00104 dbenv->tx_recover = tx_recover; 00105 return (0); 00106 } 00107 00108 /* 00109 * __txn_set_tx_timestamp -- 00110 * Set the transaction recovery timestamp. 00111 */ 00112 static int 00113 __txn_set_tx_timestamp(dbenv, timestamp) 00114 DB_ENV *dbenv; 00115 time_t *timestamp; 00116 { 00117 ENV_ILLEGAL_AFTER_OPEN(dbenv, "set_tx_timestamp"); 00118 00119 dbenv->tx_timestamp = *timestamp; 00120 return (0); 00121 } 00122 00123 /* 00124 * CDB___txn_open -- 00125 * Open a transaction region. 00126 * 00127 * PUBLIC: int CDB___txn_open __P((DB_ENV *)); 00128 */ 00129 int 00130 CDB___txn_open(dbenv) 00131 DB_ENV *dbenv; 00132 { 00133 DB_TXNMGR *tmgrp; 00134 int ret; 00135 00136 /* Create/initialize the transaction manager structure. */ 00137 if ((ret = CDB___os_calloc(dbenv, 1, sizeof(DB_TXNMGR), &tmgrp)) != 0) 00138 return (ret); 00139 TAILQ_INIT(&tmgrp->txn_chain); 00140 tmgrp->dbenv = dbenv; 00141 tmgrp->recover = 00142 dbenv->tx_recover == NULL ? CDB___db_dispatch : dbenv->tx_recover; 00143 00144 /* Join/create the txn region. */ 00145 tmgrp->reginfo.id = REG_ID_TXN; 00146 tmgrp->reginfo.mode = dbenv->db_mode; 00147 if (F_ISSET(dbenv, DB_ENV_CREATE)) 00148 F_SET(&tmgrp->reginfo, REGION_CREATE_OK); 00149 if ((ret = CDB___db_r_attach(dbenv, 00150 &tmgrp->reginfo, TXN_REGION_SIZE(dbenv->tx_max))) != 0) 00151 goto err; 00152 00153 /* If we created the region, initialize it. */ 00154 if (F_ISSET(&tmgrp->reginfo, REGION_CREATE)) 00155 if ((ret = __txn_init(dbenv, tmgrp)) != 0) 00156 goto err; 00157 00158 /* Set the local addresses. */ 00159 tmgrp->reginfo.primary = 00160 R_ADDR(&tmgrp->reginfo, tmgrp->reginfo.rp->primary); 00161 00162 R_UNLOCK(dbenv, &tmgrp->reginfo); 00163 00164 /* Acquire a mutex to protect the active TXN list. */ 00165 if (F_ISSET(dbenv, DB_ENV_THREAD)) { 00166 if ((ret = CDB___db_mutex_alloc( 00167 dbenv, &tmgrp->reginfo, &tmgrp->mutexp)) != 0) 00168 goto detach; 00169 if ((ret = __db_mutex_init( 00170 dbenv, tmgrp->mutexp, 0, MUTEX_THREAD)) != 0) 00171 goto detach; 00172 } 00173 00174 dbenv->tx_handle = tmgrp; 00175 return (0); 00176 00177 err: if (tmgrp->reginfo.addr != NULL) { 00178 if (F_ISSET(&tmgrp->reginfo, REGION_CREATE)) 00179 F_SET(tmgrp->reginfo.rp, REG_DEAD); 00180 R_UNLOCK(dbenv, &tmgrp->reginfo); 00181 00182 detach: (void)CDB___db_r_detach(dbenv, &tmgrp->reginfo, 0); 00183 } 00184 CDB___os_free(tmgrp, sizeof(*tmgrp)); 00185 return (ret); 00186 } 00187 00188 /* 00189 * __txn_init -- 00190 * Initialize a transaction region in shared memory. 00191 */ 00192 static int 00193 __txn_init(dbenv, tmgrp) 00194 DB_ENV *dbenv; 00195 DB_TXNMGR *tmgrp; 00196 { 00197 DB_TXNREGION *region; 00198 int ret; 00199 00200 if ((ret = CDB___db_shalloc(tmgrp->reginfo.addr, 00201 sizeof(DB_TXNREGION), 0, &tmgrp->reginfo.primary)) != 0) { 00202 CDB___db_err(dbenv, 00203 "Unable to allocate memory for the transaction region"); 00204 return (ret); 00205 } 00206 tmgrp->reginfo.rp->primary = 00207 R_OFFSET(&tmgrp->reginfo, tmgrp->reginfo.primary); 00208 region = tmgrp->reginfo.primary; 00209 memset(region, 0, sizeof(*region)); 00210 00211 region->maxtxns = dbenv->tx_max; 00212 region->last_txnid = TXN_MINIMUM; 00213 ZERO_LSN(region->pending_ckp); 00214 ZERO_LSN(region->last_ckp); 00215 region->time_ckp = time(NULL); 00216 00217 /* 00218 * XXX 00219 * If we ever do more types of locking and logging, this changes. 00220 */ 00221 region->logtype = 0; 00222 region->locktype = 0; 00223 region->naborts = 0; 00224 region->ncommits = 0; 00225 region->nbegins = 0; 00226 region->nactive = 0; 00227 region->maxnactive = 0; 00228 00229 SH_TAILQ_INIT(®ion->active_txn); 00230 00231 return (0); 00232 } 00233 00234 /* 00235 * CDB___txn_close -- 00236 * Close a transaction region. 00237 * 00238 * PUBLIC: int CDB___txn_close __P((DB_ENV *)); 00239 */ 00240 int 00241 CDB___txn_close(dbenv) 00242 DB_ENV *dbenv; 00243 { 00244 DB_TXN *txnp; 00245 DB_TXNMGR *tmgrp; 00246 int ret, t_ret; 00247 00248 ret = 0; 00249 tmgrp = dbenv->tx_handle; 00250 00251 /* 00252 * This function can only be called once per process (i.e., not 00253 * once per thread), so no synchronization is required. 00254 * 00255 * We would like to abort any running transactions, but the caller 00256 * is doing something wrong by calling close with active 00257 * transactions. It's quite likely that this will fail because 00258 * recovery won't find open files. If this happens, the right 00259 * solution is DB_RUNRECOVERY. So, convert any failure messages 00260 * to that. 00261 */ 00262 while ((txnp = 00263 TAILQ_FIRST(&tmgrp->txn_chain)) != TAILQ_END(&tmgrp->txn_chain)) 00264 if ((t_ret = CDB_txn_abort(txnp)) != 0) { 00265 CDB___db_err(dbenv, 00266 "Unable to abort transaction 0x%x: %s\n", 00267 txnp->txnid, CDB_db_strerror(t_ret)); 00268 CDB___txn_end(txnp, 0); 00269 if (ret == 0) 00270 ret = t_ret == 0 ? 0 : DB_RUNRECOVERY; 00271 } 00272 00273 /* Flush the log. */ 00274 if (LOGGING_ON(dbenv) && 00275 (t_ret = CDB_log_flush(dbenv, NULL)) != 0 && ret == 0) 00276 ret = t_ret; 00277 00278 /* Discard the per-thread lock. */ 00279 if (tmgrp->mutexp != NULL) 00280 CDB___db_mutex_free(dbenv, &tmgrp->reginfo, tmgrp->mutexp); 00281 00282 /* Detach from the region. */ 00283 if ((t_ret = CDB___db_r_detach(dbenv, &tmgrp->reginfo, 0)) != 0 && ret == 0) 00284 ret = t_ret; 00285 00286 CDB___os_free(tmgrp, sizeof(*tmgrp)); 00287 00288 dbenv->tx_handle = NULL; 00289 return (ret); 00290 } 00291 00292 int 00293 CDB_txn_stat(dbenv, statp, db_malloc) 00294 DB_ENV *dbenv; 00295 DB_TXN_STAT **statp; 00296 void *(*db_malloc) __P((size_t)); 00297 { 00298 DB_TXNMGR *mgr; 00299 DB_TXNREGION *region; 00300 DB_TXN_STAT *stats; 00301 TXN_DETAIL *txnp; 00302 size_t nbytes; 00303 u_int32_t nactive, ndx; 00304 int ret, slop; 00305 00306 #ifdef HAVE_RPC 00307 if (F_ISSET(dbenv, DB_ENV_RPCCLIENT)) 00308 return (__dbcl_txn_stat(dbenv, statp, db_malloc)); 00309 #endif 00310 00311 PANIC_CHECK(dbenv); 00312 ENV_REQUIRES_CONFIG(dbenv, dbenv->tx_handle, DB_INIT_TXN); 00313 00314 *statp = NULL; 00315 00316 slop = 200; 00317 mgr = dbenv->tx_handle; 00318 region = mgr->reginfo.primary; 00319 00320 retry: R_LOCK(dbenv, &mgr->reginfo); 00321 nactive = region->nactive; 00322 R_UNLOCK(dbenv, &mgr->reginfo); 00323 00324 /* 00325 * Allocate extra active structures to handle any transactions that 00326 * are created while we have the region unlocked. 00327 */ 00328 nbytes = sizeof(DB_TXN_STAT) + sizeof(DB_TXN_ACTIVE) * (nactive + slop); 00329 if ((ret = CDB___os_malloc(dbenv, nbytes, db_malloc, &stats)) != 0) 00330 return (ret); 00331 00332 R_LOCK(dbenv, &mgr->reginfo); 00333 stats->st_last_txnid = region->last_txnid; 00334 stats->st_last_ckp = region->last_ckp; 00335 stats->st_maxtxns = region->maxtxns; 00336 stats->st_naborts = region->naborts; 00337 stats->st_nbegins = region->nbegins; 00338 stats->st_ncommits = region->ncommits; 00339 stats->st_pending_ckp = region->pending_ckp; 00340 stats->st_time_ckp = region->time_ckp; 00341 stats->st_nactive = region->nactive; 00342 if (stats->st_nactive > nactive + 200) { 00343 R_UNLOCK(dbenv, &mgr->reginfo); 00344 slop *= 2; 00345 goto retry; 00346 } 00347 stats->st_maxnactive = region->maxnactive; 00348 stats->st_txnarray = (DB_TXN_ACTIVE *)&stats[1]; 00349 00350 ndx = 0; 00351 for (txnp = SH_TAILQ_FIRST(®ion->active_txn, __txn_detail); 00352 txnp != NULL; 00353 txnp = SH_TAILQ_NEXT(txnp, links, __txn_detail)) { 00354 stats->st_txnarray[ndx].txnid = txnp->txnid; 00355 if (txnp->parent == INVALID_ROFF) 00356 stats->st_txnarray[ndx].parentid = TXN_INVALID_ID; 00357 else 00358 stats->st_txnarray[ndx].parentid = 00359 ((TXN_DETAIL *)R_ADDR(&mgr->reginfo, 00360 txnp->parent))->txnid; 00361 stats->st_txnarray[ndx].lsn = txnp->begin_lsn; 00362 ndx++; 00363 00364 if (ndx >= stats->st_nactive) 00365 break; 00366 } 00367 00368 stats->st_region_wait = mgr->reginfo.rp->mutex.mutex_set_wait; 00369 stats->st_region_nowait = mgr->reginfo.rp->mutex.mutex_set_nowait; 00370 stats->st_regsize = mgr->reginfo.rp->size; 00371 00372 R_UNLOCK(dbenv, &mgr->reginfo); 00373 00374 *statp = stats; 00375 return (0); 00376 }