txn.c
上传用户:romrleung
上传日期:2022-05-23
资源大小:18897k
文件大小:37k
源码类别:

MySQL数据库

开发平台:

Visual C++

  1. /*-
  2.  * See the file LICENSE for redistribution information.
  3.  *
  4.  * Copyright (c) 1996-2002
  5.  * Sleepycat Software.  All rights reserved.
  6.  */
  7. /*
  8.  * Copyright (c) 1995, 1996
  9.  * The President and Fellows of Harvard University.  All rights reserved.
  10.  *
  11.  * This code is derived from software contributed to Berkeley by
  12.  * Margo Seltzer.
  13.  *
  14.  * Redistribution and use in source and binary forms, with or without
  15.  * modification, are permitted provided that the following conditions
  16.  * are met:
  17.  * 1. Redistributions of source code must retain the above copyright
  18.  *    notice, this list of conditions and the following disclaimer.
  19.  * 2. Redistributions in binary form must reproduce the above copyright
  20.  *    notice, this list of conditions and the following disclaimer in the
  21.  *    documentation and/or other materials provided with the distribution.
  22.  * 3. Neither the name of the University nor the names of its contributors
  23.  *    may be used to endorse or promote products derived from this software
  24.  *    without specific prior written permission.
  25.  *
  26.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  27.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  28.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  29.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  30.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  31.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  32.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  33.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  34.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  35.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  36.  * SUCH DAMAGE.
  37.  */
  38. #include "db_config.h"
  39. #ifndef lint
  40. static const char revid[] = "$Id: txn.c,v 11.179 2002/08/29 17:41:17 margo Exp $";
  41. #endif /* not lint */
  42. #ifndef NO_SYSTEM_INCLUDES
  43. #include <sys/types.h>
  44. #include <stdlib.h>
  45. #if TIME_WITH_SYS_TIME
  46. #include <sys/time.h>
  47. #include <time.h>
  48. #else
  49. #if HAVE_SYS_TIME_H
  50. #include <sys/time.h>
  51. #else
  52. #include <time.h>
  53. #endif
  54. #endif
  55. #include <string.h>
  56. #endif
  57. #include "db_int.h"
  58. #include "dbinc/crypto.h"
  59. #include "dbinc/hmac.h"
  60. #include "dbinc/db_page.h"
  61. #include "dbinc/db_shash.h"
  62. #include "dbinc/hash.h"
  63. #include "dbinc/lock.h"
  64. #include "dbinc/log.h"
  65. #include "dbinc/txn.h"
  66. #define SET_LOG_FLAGS(dbenv, txnp, lflags)
  67. do {
  68. lflags = DB_COMMIT | DB_PERMANENT;
  69. if (F_ISSET(txnp, TXN_SYNC))
  70. lflags |= DB_FLUSH;
  71. else if (!F_ISSET(txnp, TXN_NOSYNC) &&
  72.     !F_ISSET(dbenv, DB_ENV_TXN_NOSYNC)) {
  73. if (F_ISSET(dbenv, DB_ENV_TXN_WRITE_NOSYNC))
  74. lflags |= DB_WRNOSYNC;
  75. else
  76. lflags |= DB_FLUSH;
  77. }
  78. } while (0)
  79. /*
  80.  * __txn_isvalid enumerated types.  We cannot simply use the transaction
  81.  * statuses, because different statuses need to be handled differently
  82.  * depending on the caller.
  83.  */
  84. typedef enum {
  85. TXN_OP_ABORT,
  86. TXN_OP_COMMIT,
  87. TXN_OP_DISCARD,
  88. TXN_OP_PREPARE
  89. } txnop_t;
  90. static int  __txn_begin_int __P((DB_TXN *, int));
  91. static int  __txn_end __P((DB_TXN *, int));
  92. static int  __txn_isvalid __P((const DB_TXN *, TXN_DETAIL **, txnop_t));
  93. static int  __txn_set_timeout __P(( DB_TXN *, db_timeout_t, u_int32_t));
  94. static int  __txn_undo __P((DB_TXN *));
  95. #ifndef db_create
  96. /*
  97.  * txn_abort --
  98.  * txn_begin --
  99.  * txn_commit --
  100.  *
  101.  * When we switched to methods in 4.0, we guessed txn_{abort,begin,commit}
  102.  * were the interfaces applications would likely use and not be willing to
  103.  * change, due to the sheer volume of the calls.  Provide wrappers -- we
  104.  * could do txn_abort and txn_commit using macros, but not txn_begin, as
  105.  * the name of the field is txn_begin, we didn't want to modify it.
  106.  *
  107.  * The issue with txn_begin hits us in another way.  If configured with the
  108.  * --with-uniquename option, we use #defines to re-define DB's interfaces
  109.  * to unique names.  We can't do that for these functions because txn_begin
  110.  * is also a field name in the DB_ENV structure, and the #defines we use go
  111.  * at the end of the db.h file -- we get control too late to #define a field
  112.  * name.  So, modify the script that generates the unique names #defines to
  113.  * not generate them for these three functions, and don't include the three
  114.  * functions in libraries built with that configuration option.
  115.  *
  116.  * EXTERN: int txn_abort __P((DB_TXN *));
  117.  * EXTERN: int txn_begin __P((DB_ENV *, DB_TXN *, DB_TXN **, u_int32_t));
  118.  * EXTERN: int txn_commit __P((DB_TXN *, u_int32_t));
  119.  */
  120. int
  121. txn_abort(txnp)
  122. DB_TXN *txnp;
  123. {
  124. return (txnp->abort(txnp));
  125. }
  126. int
  127. txn_begin(dbenv, parent, txnpp, flags)
  128. DB_ENV *dbenv;
  129. DB_TXN *parent, **txnpp;
  130. u_int32_t flags;
  131. {
  132. return (dbenv->txn_begin(dbenv, parent, txnpp, flags));
  133. }
  134. int
  135. txn_commit(txnp, flags)
  136. DB_TXN *txnp;
  137. u_int32_t flags;
  138. {
  139. return (txnp->commit(txnp, flags));
  140. }
  141. #endif /* !db_create */
  142. /*
  143.  * __txn_begin --
  144.  * This is a wrapper to the actual begin process.  Normal transaction
  145.  * begin allocates a DB_TXN structure for the caller, while XA transaction
  146.  * begin does not.  Other than that, both call into common __txn_begin_int
  147.  * code.
  148.  *
  149.  * Internally, we use TXN_DETAIL structures, but the DB_TXN structure
  150.  * provides access to the transaction ID and the offset in the transaction
  151.  * region of the TXN_DETAIL structure.
  152.  *
  153.  * PUBLIC: int __txn_begin __P((DB_ENV *, DB_TXN *, DB_TXN **, u_int32_t));
  154.  */
  155. int
  156. __txn_begin(dbenv, parent, txnpp, flags)
  157. DB_ENV *dbenv;
  158. DB_TXN *parent, **txnpp;
  159. u_int32_t flags;
  160. {
  161. DB_LOCKREGION *region;
  162. DB_TXN *txn;
  163. int ret;
  164. *txnpp = NULL;
  165. PANIC_CHECK(dbenv);
  166. ENV_REQUIRES_CONFIG(dbenv, dbenv->tx_handle, "txn_begin", DB_INIT_TXN);
  167. if ((ret = __db_fchk(dbenv,
  168.     "txn_begin", flags,
  169.     DB_DIRTY_READ | DB_TXN_NOWAIT |
  170.     DB_TXN_NOSYNC | DB_TXN_SYNC)) != 0)
  171. return (ret);
  172. if ((ret = __db_fcchk(dbenv,
  173.     "txn_begin", flags, DB_TXN_NOSYNC, DB_TXN_SYNC)) != 0)
  174. return (ret);
  175. if ((ret = __os_calloc(dbenv, 1, sizeof(DB_TXN), &txn)) != 0)
  176. return (ret);
  177. txn->mgrp = dbenv->tx_handle;
  178. txn->parent = parent;
  179. TAILQ_INIT(&txn->kids);
  180. TAILQ_INIT(&txn->events);
  181. txn->flags = TXN_MALLOC;
  182. if (LF_ISSET(DB_DIRTY_READ))
  183. F_SET(txn, TXN_DIRTY_READ);
  184. if (LF_ISSET(DB_TXN_NOSYNC))
  185. F_SET(txn, TXN_NOSYNC);
  186. if (LF_ISSET(DB_TXN_SYNC))
  187. F_SET(txn, TXN_SYNC);
  188. if (LF_ISSET(DB_TXN_NOWAIT))
  189. F_SET(txn, TXN_NOWAIT);
  190. if ((ret = __txn_begin_int(txn, 0)) != 0)
  191. goto err;
  192. if (parent != NULL)
  193. TAILQ_INSERT_HEAD(&parent->kids, txn, klinks);
  194. if (LOCKING_ON(dbenv)) {
  195. region = ((DB_LOCKTAB *)dbenv->lk_handle)->reginfo.primary;
  196. if (parent != NULL) {
  197. ret = __lock_inherit_timeout(dbenv,
  198.     parent->txnid, txn->txnid);
  199. /* No parent locker set yet. */
  200. if (ret == EINVAL) {
  201. parent = NULL;
  202. ret = 0;
  203. }
  204. if (ret != 0)
  205. goto err;
  206. }
  207. /*
  208.  * Parent is NULL if we have no parent
  209.  * or it has no timeouts set.
  210.  */
  211. if (parent == NULL && region->tx_timeout != 0)
  212. if ((ret = __lock_set_timeout(dbenv, txn->txnid,
  213.     region->tx_timeout, DB_SET_TXN_TIMEOUT)) != 0)
  214. goto err;
  215. }
  216. *txnpp = txn;
  217. return (0);
  218. err:
  219. __os_free(dbenv, txn);
  220. return (ret);
  221. }
  222. /*
  223.  * __txn_xa_begin --
  224.  * XA version of txn_begin.
  225.  *
  226.  * PUBLIC: int __txn_xa_begin __P((DB_ENV *, DB_TXN *));
  227.  */
  228. int
  229. __txn_xa_begin(dbenv, txn)
  230. DB_ENV *dbenv;
  231. DB_TXN *txn;
  232. {
  233. PANIC_CHECK(dbenv);
  234. memset(txn, 0, sizeof(DB_TXN));
  235. txn->mgrp = dbenv->tx_handle;
  236. TAILQ_INIT(&txn->kids);
  237. TAILQ_INIT(&txn->events);
  238. return (__txn_begin_int(txn, 0));
  239. }
  240. /*
  241.  * __txn_compensate_begin
  242.  * Begin an compensation transaction.  This is a special interface
  243.  * that is used only for transactions that must be started to compensate
  244.  * for actions during an abort.  Currently only used for allocations.
  245.  *
  246.  * PUBLIC: int __txn_compensate_begin __P((DB_ENV *, DB_TXN **txnp));
  247.  */
  248. int
  249. __txn_compensate_begin(dbenv, txnpp)
  250. DB_ENV *dbenv;
  251. DB_TXN **txnpp;
  252. {
  253. DB_TXN *txn;
  254. int ret;
  255. PANIC_CHECK(dbenv);
  256. if ((ret = __os_calloc(dbenv, 1, sizeof(DB_TXN), &txn)) != 0)
  257. return (ret);
  258. txn->mgrp = dbenv->tx_handle;
  259. TAILQ_INIT(&txn->kids);
  260. TAILQ_INIT(&txn->events);
  261. txn->flags = TXN_MALLOC;
  262. F_SET(txn, TXN_COMPENSATE);
  263. *txnpp = txn;
  264. return (__txn_begin_int(txn, 1));
  265. }
  266. /*
  267.  * __txn_begin_int --
  268.  * Normal DB version of txn_begin.
  269.  */
  270. static int
  271. __txn_begin_int(txn, internal)
  272. DB_TXN *txn;
  273. int internal;
  274. {
  275. DB_ENV *dbenv;
  276. DB_LSN begin_lsn, null_lsn;
  277. DB_TXNMGR *mgr;
  278. DB_TXNREGION *region;
  279. TXN_DETAIL *td;
  280. size_t off;
  281. u_int32_t id, *ids;
  282. int nids, ret;
  283. mgr = txn->mgrp;
  284. dbenv = mgr->dbenv;
  285. region = mgr->reginfo.primary;
  286. /*
  287.  * We do not have to write begin records (and if we do not, then we
  288.  * need never write records for read-only transactions).  However,
  289.  * we do need to find the current LSN so that we can store it in the
  290.  * transaction structure, so we can know where to take checkpoints.
  291.  *
  292.  * XXX
  293.  * We should set this value when we write the first log record, not
  294.  * here.
  295.  */
  296. if (DBENV_LOGGING(dbenv))
  297. __log_txn_lsn(dbenv, &begin_lsn, NULL, NULL);
  298. R_LOCK(dbenv, &mgr->reginfo);
  299. if (!F_ISSET(txn, TXN_COMPENSATE) && F_ISSET(region, TXN_IN_RECOVERY)) {
  300. __db_err(dbenv, "operation not permitted during recovery");
  301. ret = EINVAL;
  302. goto err;
  303. }
  304. /* Make sure that we aren't still recovering prepared transactions. */
  305. if (!internal && region->stat.st_nrestores != 0) {
  306. __db_err(dbenv,
  307.     "recovery of prepared but not yet committed transactions is incomplete");
  308. ret = EINVAL;
  309. goto err;
  310. }
  311. /*
  312.  * Allocate a new transaction id. Our current valid range can span
  313.  * the maximum valid value, so check for it and wrap manually.
  314.  */
  315. if (region->last_txnid == TXN_MAXIMUM &&
  316.     region->cur_maxid != TXN_MAXIMUM)
  317. region->last_txnid = TXN_MINIMUM - 1;
  318. if (region->last_txnid == region->cur_maxid) {
  319. if ((ret = __os_malloc(dbenv,
  320.     sizeof(u_int32_t) * region->maxtxns, &ids)) != 0)
  321. goto err;
  322. nids = 0;
  323. for (td = SH_TAILQ_FIRST(&region->active_txn, __txn_detail);
  324.     td != NULL;
  325.     td = SH_TAILQ_NEXT(td, links, __txn_detail))
  326. ids[nids++] = td->txnid;
  327. region->last_txnid = TXN_MINIMUM - 1;
  328. region->cur_maxid = TXN_MAXIMUM;
  329. if (nids != 0)
  330. __db_idspace(ids, nids,
  331.     &region->last_txnid, &region->cur_maxid);
  332. __os_free(dbenv, ids);
  333. if (DBENV_LOGGING(dbenv) &&
  334.     (ret = __txn_recycle_log(dbenv, NULL,
  335.     &null_lsn, 0, region->last_txnid, region->cur_maxid)) != 0)
  336. goto err;
  337. }
  338. /* Allocate a new transaction detail structure. */
  339. if ((ret =
  340.     __db_shalloc(mgr->reginfo.addr, sizeof(TXN_DETAIL), 0, &td)) != 0) {
  341. __db_err(dbenv,
  342.     "Unable to allocate memory for transaction detail");
  343. goto err;
  344. }
  345. /* Place transaction on active transaction list. */
  346. SH_TAILQ_INSERT_HEAD(&region->active_txn, td, links, __txn_detail);
  347. id = ++region->last_txnid;
  348. ++region->stat.st_nbegins;
  349. if (++region->stat.st_nactive > region->stat.st_maxnactive)
  350. region->stat.st_maxnactive = region->stat.st_nactive;
  351. td->txnid = id;
  352. td->begin_lsn = begin_lsn;
  353. ZERO_LSN(td->last_lsn);
  354. td->status = TXN_RUNNING;
  355. if (txn->parent != NULL)
  356. td->parent = txn->parent->off;
  357. else
  358. td->parent = INVALID_ROFF;
  359. td->flags = 0;
  360. off = R_OFFSET(&mgr->reginfo, td);
  361. R_UNLOCK(dbenv, &mgr->reginfo);
  362. ZERO_LSN(txn->last_lsn);
  363. txn->txnid = id;
  364. txn->off = (u_int32_t)off;
  365. txn->abort = __txn_abort;
  366. txn->commit = __txn_commit;
  367. txn->discard = __txn_discard;
  368. txn->id = __txn_id;
  369. txn->prepare = __txn_prepare;
  370. txn->set_timeout = __txn_set_timeout;
  371. /*
  372.  * If this is a transaction family, we must link the child to the
  373.  * maximal grandparent in the lock table for deadlock detection.
  374.  */
  375. if (txn->parent != NULL && LOCKING_ON(dbenv))
  376. if ((ret = __lock_addfamilylocker(dbenv,
  377.     txn->parent->txnid, txn->txnid)) != 0)
  378. return (ret);
  379. if (F_ISSET(txn, TXN_MALLOC)) {
  380. MUTEX_THREAD_LOCK(dbenv, mgr->mutexp);
  381. TAILQ_INSERT_TAIL(&mgr->txn_chain, txn, links);
  382. MUTEX_THREAD_UNLOCK(dbenv, mgr->mutexp);
  383. }
  384. return (0);
  385. err: R_UNLOCK(dbenv, &mgr->reginfo);
  386. return (ret);
  387. }
  388. /*
  389.  * __txn_commit --
  390.  * Commit a transaction.
  391.  *
  392.  * PUBLIC: int __txn_commit __P((DB_TXN *, u_int32_t));
  393.  */
  394. int
  395. __txn_commit(txnp, flags)
  396. DB_TXN *txnp;
  397. u_int32_t flags;
  398. {
  399. DB_ENV *dbenv;
  400. DB_LOCKREQ request;
  401. DB_TXN *kid;
  402. TXN_DETAIL *td;
  403. u_int32_t lflags;
  404. int ret, t_ret;
  405. dbenv = txnp->mgrp->dbenv;
  406. PANIC_CHECK(dbenv);
  407. if ((ret = __txn_isvalid(txnp, &td, TXN_OP_COMMIT)) != 0)
  408. return (ret);
  409. /*
  410.  * We clear flags that are incorrect, ignoring any flag errors, and
  411.  * default to synchronous operations.  By definition, transaction
  412.  * handles are dead when we return, and this error should never
  413.  * happen, but we don't want to fail in the field 'cause the app is
  414.  * specifying the wrong flag for some reason.
  415.  */
  416. if (__db_fchk(dbenv,
  417.     "DB_TXN->commit", flags, DB_TXN_NOSYNC | DB_TXN_SYNC) != 0)
  418. flags = DB_TXN_SYNC;
  419. if (__db_fcchk(dbenv,
  420.     "DB_TXN->commit", flags, DB_TXN_NOSYNC, DB_TXN_SYNC) != 0)
  421. flags = DB_TXN_SYNC;
  422. if (LF_ISSET(DB_TXN_NOSYNC)) {
  423. F_CLR(txnp, TXN_SYNC);
  424. F_SET(txnp, TXN_NOSYNC);
  425. }
  426. if (LF_ISSET(DB_TXN_SYNC)) {
  427. F_CLR(txnp, TXN_NOSYNC);
  428. F_SET(txnp, TXN_SYNC);
  429. }
  430. /*
  431.  * Commit any unresolved children.  If anyone fails to commit,
  432.  * then try to abort the rest of the kids and then abort the parent.
  433.  * Abort should never fail; if it does, we bail out immediately.
  434.  */
  435. while ((kid = TAILQ_FIRST(&txnp->kids)) != NULL)
  436. if ((ret = kid->commit(kid, flags)) != 0)
  437. while ((kid = TAILQ_FIRST(&txnp->kids)) != NULL)
  438. if ((t_ret = kid->abort(kid)) != 0)
  439. return (__db_panic(dbenv, t_ret));
  440. /*
  441.  * Process any aborted pages from our children.
  442.  * We delay putting pages on the free list that are newly
  443.  * allocated and then aborted so that we can undo other
  444.  * allocations, if necessary, without worrying about
  445.  * these pages which were not on the free list before.
  446.  */
  447. if (txnp->txn_list != NULL) {
  448. t_ret = __db_do_the_limbo(dbenv, NULL, txnp, txnp->txn_list);
  449. __db_txnlist_end(dbenv, txnp->txn_list);
  450. txnp->txn_list = NULL;
  451. if (t_ret != 0 && ret == 0)
  452. ret = t_ret;
  453. }
  454. if (ret != 0)
  455. goto err;
  456. /*
  457.  * If there are any log records, write a log record and sync the log,
  458.  * else do no log writes.  If the commit is for a child transaction,
  459.  * we do not need to commit the child synchronously since it may still
  460.  * abort (if its parent aborts), and otherwise its parent or ultimate
  461.  * ancestor will write synchronously.
  462.  */
  463. if (DBENV_LOGGING(dbenv) && !IS_ZERO_LSN(txnp->last_lsn)) {
  464. if (txnp->parent == NULL) {
  465. /*
  466.  * We are about to free all the read locks
  467.  * for this transaction below.  Some of those
  468.  * locks might be handle locks which should
  469.  * not be freed, because they will be freed
  470.  * when the handle is closed.  Check the
  471.  * events and preprocess any trades now so
  472.  * that we don't release the locks below.
  473.  */
  474. if ((ret = __txn_doevents(dbenv, txnp, 0, 1)) != 0)
  475. goto err;
  476. request.op = DB_LOCK_PUT_READ;
  477. if (LOCKING_ON(dbenv) && (ret = dbenv->lock_vec(
  478.    dbenv, txnp->txnid, 0, &request, 1, NULL)) != 0)
  479. goto err;
  480. SET_LOG_FLAGS(dbenv, txnp, lflags);
  481. if ((ret = __txn_regop_log(dbenv,
  482.     txnp, &txnp->last_lsn, lflags,
  483.     TXN_COMMIT, (int32_t)time(NULL))) != 0)
  484. goto err;
  485. } else {
  486. /* Log the commit in the parent! */
  487. if ((ret = __txn_child_log(dbenv,
  488.     txnp->parent, &txnp->parent->last_lsn,
  489.     0, txnp->txnid, &txnp->last_lsn)) != 0) {
  490. goto err;
  491. }
  492. F_SET(txnp->parent, TXN_CHILDCOMMIT);
  493. }
  494. }
  495. /* This is OK because __txn_end can only fail with a panic. */
  496. return (__txn_end(txnp, 1));
  497. err: /*
  498.  * If we are prepared, then we "must" be able to commit.  We
  499.  * panic here because even though the coordinator might be
  500.  * able to retry it is not clear it would know to do that.
  501.  * Otherwise  we'll try to abort.  If that is successful,
  502.  * then we return whatever was in ret (i.e., the reason we failed).
  503.  * If the abort was unsuccessful, then abort probably returned
  504.  * DB_RUNRECOVERY and we need to propagate that up.
  505.  */
  506. if (td->status == TXN_PREPARED)
  507. return (__db_panic(dbenv, ret));
  508. if ((t_ret = txnp->abort(txnp)) != 0)
  509. ret = t_ret;
  510. return (ret);
  511. }
  512. /*
  513.  * __txn_abort --
  514.  * Abort a transaction.
  515.  *
  516.  * PUBLIC: int __txn_abort __P((DB_TXN *));
  517.  */
  518. int
  519. __txn_abort(txnp)
  520. DB_TXN *txnp;
  521. {
  522. DB_ENV *dbenv;
  523. DB_LOCKREQ request;
  524. DB_TXN *kid;
  525. TXN_DETAIL *td;
  526. u_int32_t lflags;
  527. int ret;
  528. dbenv = txnp->mgrp->dbenv;
  529. PANIC_CHECK(dbenv);
  530. /* Ensure that abort always fails fatally. */
  531. if ((ret = __txn_isvalid(txnp, &td, TXN_OP_ABORT)) != 0)
  532. return (__db_panic(dbenv, ret));
  533. /*
  534.  * Try to abort any unresolved children.
  535.  *
  536.  * Abort either succeeds or panics the region.  As soon as we
  537.  * see any failure, we just get out of here and return the panic
  538.  * up.
  539.  */
  540. while ((kid = TAILQ_FIRST(&txnp->kids)) != NULL)
  541. if ((ret = kid->abort(kid)) != 0)
  542. return (ret);
  543. if (LOCKING_ON(dbenv)) {
  544. /*
  545.  * We are about to free all the read locks for this transaction
  546.  * below.  Some of those locks might be handle locks which
  547.  * should not be freed, because they will be freed when the
  548.  * handle is closed.  Check the events and preprocess any
  549.  * trades now so that we don't release the locks below.
  550.  */
  551. if ((ret = __txn_doevents(dbenv, txnp, 0, 1)) != 0)
  552. return (__db_panic(dbenv, ret));
  553. /* Turn off timeouts. */
  554. if ((ret = __lock_set_timeout(dbenv,
  555.     txnp->txnid, 0, DB_SET_TXN_TIMEOUT)) != 0)
  556. return (__db_panic(dbenv, ret));
  557. if ((ret = __lock_set_timeout(dbenv,
  558.     txnp->txnid, 0, DB_SET_LOCK_TIMEOUT)) != 0)
  559. return (__db_panic(dbenv, ret));
  560. request.op = DB_LOCK_UPGRADE_WRITE;
  561. if ((ret = dbenv->lock_vec(
  562.     dbenv, txnp->txnid, 0, &request, 1, NULL)) != 0)
  563. return (__db_panic(dbenv, ret));
  564. }
  565. if ((ret = __txn_undo(txnp)) != 0)
  566. return (__db_panic(dbenv, ret));
  567. /*
  568.  * Normally, we do not need to log aborts.  However, if we
  569.  * are a distributed transaction (i.e., we have a prepare),
  570.  * then we log the abort so we know that this transaction
  571.  * was actually completed.
  572.  */
  573. SET_LOG_FLAGS(dbenv, txnp, lflags);
  574. if (DBENV_LOGGING(dbenv) && td->status == TXN_PREPARED &&
  575.     (ret = __txn_regop_log(dbenv, txnp, &txnp->last_lsn,
  576.     lflags, TXN_ABORT, (int32_t)time(NULL))) != 0)
  577. return (__db_panic(dbenv, ret));
  578. /* __txn_end always panics if it errors, so pass the return along. */
  579. return (__txn_end(txnp, 0));
  580. }
  581. /*
  582.  * __txn_discard --
  583.  * Free the per-process resources associated with this txn handle.
  584.  *
  585.  * PUBLIC: int __txn_discard __P((DB_TXN *, u_int32_t flags));
  586.  */
  587. int
  588. __txn_discard(txnp, flags)
  589. DB_TXN *txnp;
  590. u_int32_t flags;
  591. {
  592. DB_ENV *dbenv;
  593. DB_TXN *freep;
  594. TXN_DETAIL *td;
  595. int ret;
  596. COMPQUIET(flags, 0);
  597. dbenv = txnp->mgrp->dbenv;
  598. freep = NULL;
  599. PANIC_CHECK(dbenv);
  600. if ((ret = __txn_isvalid(txnp, &td, TXN_OP_DISCARD)) != 0)
  601. return (ret);
  602. /* Should be no children. */
  603. DB_ASSERT(TAILQ_FIRST(&txnp->kids) == NULL);
  604. DB_ASSERT(F_ISSET(td, TXN_RESTORED));
  605. /* Free the space. */
  606. MUTEX_THREAD_LOCK(dbenv, txnp->mgrp->mutexp);
  607. txnp->mgrp->n_discards++;
  608. if (F_ISSET(txnp, TXN_MALLOC)) {
  609. TAILQ_REMOVE(&txnp->mgrp->txn_chain, txnp, links);
  610. freep = txnp;
  611. }
  612. MUTEX_THREAD_UNLOCK(dbenv, txnp->mgrp->mutexp);
  613. if (freep != NULL)
  614. __os_free(dbenv, freep);
  615. return (0);
  616. }
  617. /*
  618.  * __txn_prepare --
  619.  * Flush the log so a future commit is guaranteed to succeed.
  620.  *
  621.  * PUBLIC: int __txn_prepare __P((DB_TXN *, u_int8_t *));
  622.  */
  623. int
  624. __txn_prepare(txnp, gid)
  625. DB_TXN *txnp;
  626. u_int8_t *gid;
  627. {
  628. DBT xid;
  629. DB_ENV *dbenv;
  630. DB_TXN *kid;
  631. TXN_DETAIL *td;
  632. u_int32_t lflags;
  633. int ret;
  634. dbenv = txnp->mgrp->dbenv;
  635. PANIC_CHECK(dbenv);
  636. if ((ret = __txn_isvalid(txnp, &td, TXN_OP_PREPARE)) != 0)
  637. return (ret);
  638. /* Commit any unresolved children. */
  639. while ((kid = TAILQ_FIRST(&txnp->kids)) != NULL)
  640. if ((ret = kid->commit(kid, DB_TXN_NOSYNC)) != 0)
  641. return (ret);
  642. /*
  643.  * In XA, the global transaction ID in the txn_detail structure is
  644.  * already set; in a non-XA environment, we must set it here.  XA
  645.  * requires that the transaction be either ENDED or SUSPENDED when
  646.  * prepare is called, so we know that if the xa_status isn't in one
  647.  * of those states, then we are calling prepare directly and we need
  648.  * to fill in the td->xid.
  649.  */
  650. if (DBENV_LOGGING(dbenv)) {
  651. memset(&xid, 0, sizeof(xid));
  652. if (td->xa_status != TXN_XA_ENDED &&
  653.     td->xa_status != TXN_XA_SUSPENDED)
  654. /* Regular prepare; fill in the gid. */
  655. memcpy(td->xid, gid, sizeof(td->xid));
  656. xid.size = sizeof(td->xid);
  657. xid.data = td->xid;
  658. SET_LOG_FLAGS(dbenv, txnp, lflags);
  659. if ((ret = __txn_xa_regop_log(dbenv, txnp, &txnp->last_lsn,
  660.     lflags, TXN_PREPARE, &xid, td->format, td->gtrid, td->bqual,
  661.     &td->begin_lsn)) != 0) {
  662. __db_err(dbenv, "DB_TXN->prepare: log_write failed %s",
  663.     db_strerror(ret));
  664. return (ret);
  665. }
  666. }
  667. MUTEX_THREAD_LOCK(dbenv, txnp->mgrp->mutexp);
  668. td->status = TXN_PREPARED;
  669. MUTEX_THREAD_UNLOCK(dbenv, txnp->mgrp->mutexp);
  670. return (0);
  671. }
  672. /*
  673.  * __txn_id --
  674.  * Return the transaction ID.
  675.  *
  676.  * PUBLIC: u_int32_t __txn_id __P((DB_TXN *));
  677.  */
  678. u_int32_t
  679. __txn_id(txnp)
  680. DB_TXN *txnp;
  681. {
  682. return (txnp->txnid);
  683. }
  684. /*
  685.  * __txn_set_timeout --
  686.  * Set timeout values in the txn structure.
  687.  */
  688. static int
  689. __txn_set_timeout(txnp, timeout, op)
  690. DB_TXN *txnp;
  691. db_timeout_t timeout;
  692. u_int32_t op;
  693. {
  694. if (op != DB_SET_TXN_TIMEOUT &&  op != DB_SET_LOCK_TIMEOUT)
  695. return (__db_ferr(txnp->mgrp->dbenv, "DB_TXN->set_timeout", 0));
  696. return (__lock_set_timeout(
  697.     txnp->mgrp->dbenv, txnp->txnid, timeout, op));
  698. }
  699. /*
  700.  * __txn_isvalid --
  701.  * Return 0 if the txnp is reasonable, otherwise panic.
  702.  */
  703. static int
  704. __txn_isvalid(txnp, tdp, op)
  705. const DB_TXN *txnp;
  706. TXN_DETAIL **tdp;
  707. txnop_t op;
  708. {
  709. DB_TXNMGR *mgrp;
  710. DB_TXNREGION *region;
  711. TXN_DETAIL *tp;
  712. mgrp = txnp->mgrp;
  713. region = mgrp->reginfo.primary;
  714. /* Check for recovery. */
  715. if (!F_ISSET(txnp, TXN_COMPENSATE) &&
  716.     F_ISSET(region, TXN_IN_RECOVERY)) {
  717. __db_err(mgrp->dbenv,
  718.     "operation not permitted during recovery");
  719. goto err;
  720. }
  721. /* Check for live cursors. */
  722. if (txnp->cursors != 0) {
  723. __db_err(mgrp->dbenv, "transaction has active cursors");
  724. goto err;
  725. }
  726. /* Check transaction's state. */
  727. tp = (TXN_DETAIL *)R_ADDR(&mgrp->reginfo, txnp->off);
  728. if (tdp != NULL)
  729. *tdp = tp;
  730. /* Handle any operation specific checks. */
  731. switch (op) {
  732. case TXN_OP_DISCARD:
  733. /*
  734.  * Since we're just tossing the per-process space; there are
  735.  * a lot of problems with the transaction that we can tolerate.
  736.  */
  737. /* Transaction is already been reused. */
  738. if (txnp->txnid != tp->txnid)
  739. return (0);
  740. /* What we've got had better be a restored transaction. */
  741. if (!F_ISSET(tp, TXN_RESTORED)) {
  742. __db_err(mgrp->dbenv, "not a restored transaction");
  743. return (__db_panic(mgrp->dbenv, EINVAL));
  744. }
  745. return (0);
  746. case TXN_OP_PREPARE:
  747. if (txnp->parent != NULL) {
  748. /*
  749.  * This is not fatal, because you could imagine an
  750.  * application that simply prepares everybody because
  751.  * it doesn't distinguish between children and parents.
  752.  * I'm not arguing this is good, but I could imagine
  753.  * someone doing it.
  754.  */
  755. __db_err(mgrp->dbenv,
  756.     "Prepare disallowed on child transactions");
  757. return (EINVAL);
  758. }
  759. break;
  760. case TXN_OP_ABORT:
  761. case TXN_OP_COMMIT:
  762. default:
  763. break;
  764. }
  765. switch (tp->status) {
  766. case TXN_PREPARED:
  767. if (op == TXN_OP_PREPARE) {
  768. __db_err(mgrp->dbenv, "transaction already prepared");
  769. /*
  770.  * Txn_prepare doesn't blow away the user handle, so
  771.  * in this case, give the user the opportunity to
  772.  * abort or commit.
  773.  */
  774. return (EINVAL);
  775. }
  776. break;
  777. case TXN_RUNNING:
  778. break;
  779. case TXN_ABORTED:
  780. case TXN_COMMITTED:
  781. default:
  782. __db_err(mgrp->dbenv, "transaction already %s",
  783.     tp->status == TXN_COMMITTED ? "committed" : "aborted");
  784. goto err;
  785. }
  786. return (0);
  787. err: /*
  788.  * If there's a serious problem with the transaction, panic.  TXN
  789.  * handles are dead by definition when we return, and if you use
  790.  * a cursor you forgot to close, we have no idea what will happen.
  791.  */
  792. return (__db_panic(mgrp->dbenv, EINVAL));
  793. }
  794. /*
  795.  * __txn_end --
  796.  * Internal transaction end routine.
  797.  */
  798. static int
  799. __txn_end(txnp, is_commit)
  800. DB_TXN *txnp;
  801. int is_commit;
  802. {
  803. DB_ENV *dbenv;
  804. DB_LOCKREQ request;
  805. DB_TXNMGR *mgr;
  806. DB_TXNREGION *region;
  807. TXN_DETAIL *tp;
  808. int do_closefiles, ret;
  809. mgr = txnp->mgrp;
  810. dbenv = mgr->dbenv;
  811. region = mgr->reginfo.primary;
  812. do_closefiles = 0;
  813. /* Process commit events. */
  814. if ((ret = __txn_doevents(dbenv, txnp, is_commit, 0)) != 0)
  815. return (__db_panic(dbenv, ret));
  816. /* Release the locks. */
  817. request.op = txnp->parent == NULL ||
  818.     is_commit == 0 ? DB_LOCK_PUT_ALL : DB_LOCK_INHERIT;
  819. /*
  820.  * __txn_end cannot return an simple error, we MUST return
  821.  * success/failure from commit or abort, ignoring any internal
  822.  * errors.  So, we panic if something goes wrong.  We can't
  823.  * deadlock here because we're not acquiring any new locks,
  824.  * so DB_LOCK_DEADLOCK is just as fatal as any other error.
  825.  */
  826. if (LOCKING_ON(dbenv) && (ret = dbenv->lock_vec(
  827.     dbenv, txnp->txnid, DB_LOCK_FREE_LOCKER, &request, 1, NULL)) != 0)
  828. return (__db_panic(dbenv, ret));
  829. /* End the transaction. */
  830. R_LOCK(dbenv, &mgr->reginfo);
  831. tp = (TXN_DETAIL *)R_ADDR(&mgr->reginfo, txnp->off);
  832. SH_TAILQ_REMOVE(&region->active_txn, tp, links, __txn_detail);
  833. if (F_ISSET(tp, TXN_RESTORED)) {
  834. region->stat.st_nrestores--;
  835. do_closefiles = region->stat.st_nrestores == 0;
  836. }
  837. __db_shalloc_free(mgr->reginfo.addr, tp);
  838. if (is_commit)
  839. region->stat.st_ncommits++;
  840. else
  841. region->stat.st_naborts++;
  842. --region->stat.st_nactive;
  843. R_UNLOCK(dbenv, &mgr->reginfo);
  844. /*
  845.  * The transaction cannot get more locks, remove its locker info,
  846.  * if any.
  847.  */
  848. if (LOCKING_ON(dbenv) && (ret =
  849.     __lock_freefamilylocker(dbenv->lk_handle, txnp->txnid)) != 0)
  850. return (__db_panic(dbenv, ret));
  851. if (txnp->parent != NULL)
  852. TAILQ_REMOVE(&txnp->parent->kids, txnp, klinks);
  853. /* Free the space. */
  854. if (F_ISSET(txnp, TXN_MALLOC)) {
  855. MUTEX_THREAD_LOCK(dbenv, mgr->mutexp);
  856. TAILQ_REMOVE(&mgr->txn_chain, txnp, links);
  857. MUTEX_THREAD_UNLOCK(dbenv, mgr->mutexp);
  858. __os_free(dbenv, txnp);
  859. }
  860. if (do_closefiles) {
  861. F_SET((DB_LOG *)dbenv->lg_handle, DBLOG_RECOVER);
  862. (void)__dbreg_close_files(dbenv);
  863. F_CLR((DB_LOG *)dbenv->lg_handle, DBLOG_RECOVER);
  864. mgr->n_discards = 0;
  865. (void)dbenv->txn_checkpoint(dbenv, 0, 0, DB_FORCE);
  866. }
  867. return (0);
  868. }
  869. /*
  870.  * __txn_undo --
  871.  * Undo the transaction with id txnid.  Returns 0 on success and
  872.  * errno on failure.
  873.  */
  874. static int
  875. __txn_undo(txnp)
  876. DB_TXN *txnp;
  877. {
  878. DBT rdbt;
  879. DB_ENV *dbenv;
  880. DB_LOGC *logc;
  881. DB_LSN key_lsn;
  882. DB_TXN *ptxn;
  883. DB_TXNMGR *mgr;
  884. int ret, t_ret;
  885. void *txnlist;
  886. mgr = txnp->mgrp;
  887. dbenv = mgr->dbenv;
  888. logc = NULL;
  889. txnlist = NULL;
  890. if (!DBENV_LOGGING(dbenv))
  891. return (0);
  892. /*
  893.  * This is the simplest way to code this, but if the mallocs during
  894.  * recovery turn out to be a performance issue, we can do the
  895.  * allocation here and use DB_DBT_USERMEM.
  896.  */
  897. memset(&rdbt, 0, sizeof(rdbt));
  898. key_lsn = txnp->last_lsn;
  899. /*
  900.  * Allocate a txnlist for children and aborted page allocs.
  901.  * We need to associate the list with the maximal parent
  902.  * so that aborted pages are recovered when that transaction
  903.  * is commited or aborted.
  904.  */
  905. for (ptxn = txnp->parent; ptxn != NULL && ptxn->parent != NULL;)
  906. ptxn = ptxn->parent;
  907. if (ptxn != NULL && ptxn->txn_list != NULL)
  908. txnlist = ptxn->txn_list;
  909. else if (txnp->txn_list != NULL)
  910. txnlist = txnp->txn_list;
  911. else if ((ret = __db_txnlist_init(dbenv, 0, 0, NULL, &txnlist)) != 0)
  912. return (ret);
  913. else if (ptxn != NULL)
  914. ptxn->txn_list = txnlist;
  915. if (F_ISSET(txnp, TXN_CHILDCOMMIT) &&
  916.     (ret = __db_txnlist_lsninit(dbenv, txnlist, &txnp->last_lsn)) != 0)
  917. return (ret);
  918. if ((ret = dbenv->log_cursor(dbenv, &logc, 0)) != 0)
  919. goto err;
  920. while (ret == 0 && !IS_ZERO_LSN(key_lsn)) {
  921. /*
  922.  * The dispatch routine returns the lsn of the record
  923.  * before the current one in the key_lsn argument.
  924.  */
  925. if ((ret = logc->get(logc, &key_lsn, &rdbt, DB_SET)) == 0) {
  926. ret = __db_dispatch(dbenv, dbenv->recover_dtab,
  927.     dbenv->recover_dtab_size, &rdbt, &key_lsn,
  928.     DB_TXN_ABORT, txnlist);
  929. if (F_ISSET(txnp, TXN_CHILDCOMMIT))
  930. (void)__db_txnlist_lsnadd(dbenv,
  931.     txnlist, &key_lsn, 0);
  932. }
  933. if (ret == DB_SURPRISE_KID) {
  934. if ((ret = __db_txnlist_lsninit(
  935.     dbenv, txnlist, &key_lsn)) == 0)
  936. F_SET(txnp, TXN_CHILDCOMMIT);
  937. } else if (ret != 0) {
  938. __db_err(txnp->mgrp->dbenv,
  939.     "DB_TXN->abort: Log undo failed for LSN: %lu %lu: %s",
  940.     (u_long)key_lsn.file, (u_long)key_lsn.offset,
  941.     db_strerror(ret));
  942. goto err;
  943. }
  944. }
  945. ret = __db_do_the_limbo(dbenv, ptxn, txnp, txnlist);
  946. err: if (logc != NULL && (t_ret = logc->close(logc, 0)) != 0 && ret == 0)
  947. ret = t_ret;
  948. if (ptxn == NULL && txnlist != NULL)
  949. __db_txnlist_end(dbenv, txnlist);
  950. return (ret);
  951. }
  952. /*
  953.  * Transaction checkpoint.
  954.  * If either kbytes or minutes is non-zero, then we only take the checkpoint
  955.  * more than "minutes" minutes have passed since the last checkpoint or if
  956.  * more than "kbytes" of log data have been written since the last checkpoint.
  957.  * When taking a checkpoint, find the oldest active transaction and figure out
  958.  * its first LSN.  This is the lowest LSN we can checkpoint, since any record
  959.  * written after since that point may be involved in a transaction and may
  960.  * therefore need to be undone in the case of an abort.
  961.  *
  962.  * PUBLIC: int __txn_checkpoint
  963.  * PUBLIC:     __P((DB_ENV *, u_int32_t, u_int32_t, u_int32_t));
  964.  */
  965. int
  966. __txn_checkpoint(dbenv, kbytes, minutes, flags)
  967. DB_ENV *dbenv;
  968. u_int32_t kbytes, minutes, flags;
  969. {
  970. DB_LSN ckp_lsn, last_ckp;
  971. DB_TXNMGR *mgr;
  972. DB_TXNREGION *region;
  973. TXN_DETAIL *txnp;
  974. time_t last_ckp_time, now;
  975. u_int32_t bytes, mbytes;
  976. int ret;
  977. PANIC_CHECK(dbenv);
  978. ENV_REQUIRES_CONFIG(dbenv,
  979.     dbenv->tx_handle, "txn_checkpoint", DB_INIT_TXN);
  980. /*
  981.  * On a replication client, all transactions are read-only;  therefore,
  982.  * a checkpoint is a null-op.
  983.  *
  984.  * We permit txn_checkpoint, instead of just rendering it illegal,
  985.  * so that an application can just let a checkpoint thread continue
  986.  * to operate as it gets promoted or demoted between being a
  987.  * master and a client.
  988.  */
  989. if (F_ISSET(dbenv, DB_ENV_REP_CLIENT))
  990. return (0);
  991. mgr = dbenv->tx_handle;
  992. region = mgr->reginfo.primary;
  993. /*
  994.  * The checkpoint LSN is an LSN such that all transactions begun before
  995.  * it are complete.  Our first guess (corrected below based on the list
  996.  * of active transactions) is the last-written LSN.
  997.  */
  998. __log_txn_lsn(dbenv, &ckp_lsn, &mbytes, &bytes);
  999. if (!LF_ISSET(DB_FORCE)) {
  1000. /* Don't checkpoint a quiescent database. */
  1001. if (bytes == 0 && mbytes == 0)
  1002. return (0);
  1003. if (kbytes != 0 &&
  1004.     mbytes * 1024 + bytes / 1024 >= (u_int32_t)kbytes)
  1005. goto do_ckp;
  1006. if (minutes != 0) {
  1007. (void)time(&now);
  1008. R_LOCK(dbenv, &mgr->reginfo);
  1009. last_ckp_time = region->time_ckp;
  1010. R_UNLOCK(dbenv, &mgr->reginfo);
  1011. if (now - last_ckp_time >= (time_t)(minutes * 60))
  1012. goto do_ckp;
  1013. }
  1014. /*
  1015.  * If we checked time and data and didn't go to checkpoint,
  1016.  * we're done.
  1017.  */
  1018. if (minutes != 0 || kbytes != 0)
  1019. return (0);
  1020. }
  1021. do_ckp: /* Look through the active transactions for the lowest begin LSN. */
  1022. R_LOCK(dbenv, &mgr->reginfo);
  1023. for (txnp = SH_TAILQ_FIRST(&region->active_txn, __txn_detail);
  1024.     txnp != NULL;
  1025.     txnp = SH_TAILQ_NEXT(txnp, links, __txn_detail))
  1026. if (!IS_ZERO_LSN(txnp->begin_lsn) &&
  1027.     log_compare(&txnp->begin_lsn, &ckp_lsn) < 0)
  1028. ckp_lsn = txnp->begin_lsn;
  1029. R_UNLOCK(dbenv, &mgr->reginfo);
  1030. if (MPOOL_ON(dbenv) && (ret = dbenv->memp_sync(dbenv, NULL)) != 0) {
  1031. __db_err(dbenv,
  1032.     "txn_checkpoint: failed to flush the buffer cache %s",
  1033.     db_strerror(ret));
  1034. return (ret);
  1035. }
  1036. /*
  1037.  * Because we can't be a replication client here, and because
  1038.  * recovery (somewhat unusually) calls txn_checkpoint and expects
  1039.  * it to write a log message, LOGGING_ON is the correct macro here.
  1040.  */
  1041. if (LOGGING_ON(dbenv)) {
  1042. R_LOCK(dbenv, &mgr->reginfo);
  1043. last_ckp = region->last_ckp;
  1044. R_UNLOCK(dbenv, &mgr->reginfo);
  1045. /*
  1046.  * Put out records for the open files before we log
  1047.  * the checkpoint.  The records are certain to be at
  1048.  * or after ckp_lsn, but before the checkpoint record
  1049.  * itself, so they're sure to be included if we start
  1050.  * recovery from the ckp_lsn contained in this
  1051.  * checkpoint.
  1052.  */
  1053. if ((ret = __dbreg_open_files(dbenv)) != 0 ||
  1054.     (ret = __txn_ckp_log(dbenv,
  1055.     NULL, &ckp_lsn, DB_PERMANENT | DB_FLUSH, &ckp_lsn,
  1056.     &last_ckp, (int32_t)time(NULL))) != 0) {
  1057. __db_err(dbenv,
  1058.     "txn_checkpoint: log failed at LSN [%ld %ld] %s",
  1059.     (long)ckp_lsn.file, (long)ckp_lsn.offset,
  1060.     db_strerror(ret));
  1061. return (ret);
  1062. }
  1063. __txn_updateckp(dbenv, &ckp_lsn);
  1064. }
  1065. return (0);
  1066. }
  1067. /*
  1068.  * __txn_getckp --
  1069.  * Get the LSN of the last transaction checkpoint.
  1070.  *
  1071.  * PUBLIC: int __txn_getckp __P((DB_ENV *, DB_LSN *));
  1072.  */
  1073. int
  1074. __txn_getckp(dbenv, lsnp)
  1075. DB_ENV *dbenv;
  1076. DB_LSN *lsnp;
  1077. {
  1078. DB_LSN lsn;
  1079. DB_TXNMGR *mgr;
  1080. DB_TXNREGION *region;
  1081. mgr = dbenv->tx_handle;
  1082. region = mgr->reginfo.primary;
  1083. R_LOCK(dbenv, &mgr->reginfo);
  1084. lsn = region->last_ckp;
  1085. R_UNLOCK(dbenv, &mgr->reginfo);
  1086. if (IS_ZERO_LSN(lsn))
  1087. return (DB_NOTFOUND);
  1088. *lsnp = lsn;
  1089. return (0);
  1090. }
  1091. /*
  1092.  * __txn_activekids --
  1093.  * Return if this transaction has any active children.
  1094.  *
  1095.  * PUBLIC: int __txn_activekids __P((DB_ENV *, u_int32_t, DB_TXN *));
  1096.  */
  1097. int
  1098. __txn_activekids(dbenv, rectype, txnp)
  1099. DB_ENV *dbenv;
  1100. u_int32_t rectype;
  1101. DB_TXN *txnp;
  1102. {
  1103. /*
  1104.  * On a child commit, we know that there are children (i.e., the
  1105.  * commiting child at the least.  In that case, skip this check.
  1106.  */
  1107. if (F_ISSET(txnp, TXN_COMPENSATE) || rectype == DB___txn_child)
  1108. return (0);
  1109. if (TAILQ_FIRST(&txnp->kids) != NULL) {
  1110. __db_err(dbenv, "Child transaction is active");
  1111. return (EPERM);
  1112. }
  1113. return (0);
  1114. }
  1115. /*
  1116.  * __txn_force_abort --
  1117.  * Force an abort record into the log if the commit record
  1118.  * failed to get to disk.
  1119.  *
  1120.  * PUBLIC: int __txn_force_abort __P((DB_ENV *, u_int8_t *));
  1121.  */
  1122. int
  1123. __txn_force_abort(dbenv, buffer)
  1124. DB_ENV *dbenv;
  1125. u_int8_t *buffer;
  1126. {
  1127. DB_CIPHER *db_cipher;
  1128. HDR *hdr;
  1129. u_int32_t offset, opcode, rec_len, rec_type, sum_len;
  1130. u_int8_t *bp, *key, chksum[DB_MAC_KEY];
  1131. size_t hdrsize;
  1132. int ret;
  1133. db_cipher = dbenv->crypto_handle;
  1134. /*
  1135.  * This routine depends on the layout of HDR and the __txn_regop
  1136.  * record in txn.src.  We are passed the beginning of the commit
  1137.  * record in the log buffer and overwrite the commit with an abort
  1138.  * and recalculate the checksum.  We may be passed a txn_xa_regop
  1139.  * that is, an XA prepare), there's no need to overwrite that one.
  1140.  */
  1141. hdr = (HDR *)buffer;
  1142. memcpy(&rec_type, hdr, sizeof(rec_type));
  1143. if (rec_type == DB___txn_xa_regop)
  1144. return (0);
  1145. offset = sizeof(u_int32_t) + sizeof(u_int32_t) + sizeof(DB_LSN);
  1146. rec_len = offset + sizeof(u_int32_t) + sizeof(int32_t);
  1147. if (CRYPTO_ON(dbenv)) {
  1148. key = db_cipher->mac_key;
  1149. hdrsize = HDR_CRYPTO_SZ;
  1150. sum_len = DB_MAC_KEY;
  1151. if ((ret = db_cipher->decrypt(dbenv, db_cipher->data,
  1152.     &hdr->iv[0], buffer + hdrsize, rec_len)) != 0)
  1153. return (__db_panic(dbenv, ret));
  1154. } else {
  1155. key = NULL;
  1156. hdrsize = HDR_NORMAL_SZ;
  1157. sum_len = sizeof(u_int32_t);
  1158. }
  1159. bp = buffer + hdrsize + offset;
  1160. opcode = TXN_ABORT;
  1161. memcpy(bp, &opcode, sizeof(opcode));
  1162. if (CRYPTO_ON(dbenv) &&
  1163.     (ret = db_cipher->encrypt(dbenv,
  1164.     db_cipher->data, &hdr->iv[0], buffer + hdrsize, rec_len)) != 0)
  1165. return (__db_panic(dbenv, ret));
  1166. __db_chksum(buffer + hdrsize, rec_len, key, chksum);
  1167. memcpy(buffer + SSZ(HDR, chksum), &chksum, sum_len);
  1168. return (0);
  1169. }
  1170. /*
  1171.  * __txn_preclose
  1172.  * Before we can close an environment, we need to check if we
  1173.  * were in the midst of taking care of restored transactions.  If
  1174.  * so, then we need to close the files that we opened.
  1175.  *
  1176.  * PUBLIC: int __txn_preclose __P((DB_ENV *));
  1177.  */
  1178. int
  1179. __txn_preclose(dbenv)
  1180. DB_ENV *dbenv;
  1181. {
  1182. DB_TXNMGR *mgr;
  1183. DB_TXNREGION *region;
  1184. int do_closefiles, ret;
  1185. mgr = (DB_TXNMGR *)dbenv->tx_handle;
  1186. region = mgr->reginfo.primary;
  1187. do_closefiles = 0;
  1188. R_LOCK(dbenv, &mgr->reginfo);
  1189. if (region != NULL &&
  1190.     region->stat.st_nrestores
  1191.     <= mgr->n_discards && mgr->n_discards != 0)
  1192. do_closefiles = 1;
  1193. R_UNLOCK(dbenv, &mgr->reginfo);
  1194. if (do_closefiles) {
  1195. /*
  1196.  * Set the DBLOG_RECOVER flag while closing these
  1197.  * files so they do not create additional log records
  1198.  * that will confuse future recoveries.
  1199.  */
  1200. F_SET((DB_LOG *)dbenv->lg_handle, DBLOG_RECOVER);
  1201. ret = __dbreg_close_files(dbenv);
  1202. F_CLR((DB_LOG *)dbenv->lg_handle, DBLOG_RECOVER);
  1203. } else
  1204. ret = 0;
  1205. return (ret);
  1206. }
  1207. /*
  1208.  * __txn_reset --
  1209.  * Reset the last txnid to its minimum value, and log the reset.
  1210.  *
  1211.  * PUBLIC: int __txn_reset __P((DB_ENV *));
  1212.  */
  1213. int
  1214. __txn_reset(dbenv)
  1215. DB_ENV *dbenv;
  1216. {
  1217. DB_LSN scrap;
  1218. DB_TXNREGION *region;
  1219. region = ((DB_TXNMGR *)dbenv->tx_handle)->reginfo.primary;
  1220. region->last_txnid = TXN_MINIMUM;
  1221. DB_ASSERT(LOGGING_ON(dbenv));
  1222. return (__txn_recycle_log(dbenv,
  1223.     NULL, &scrap, 0, TXN_MINIMUM, TXN_MAXIMUM));
  1224. }
  1225. /*
  1226.  * __txn_updateckp --
  1227.  * Update the last_ckp field in the transaction region.  This happens
  1228.  * at the end of a normal checkpoint and also when a replication client
  1229.  * receives a checkpoint record.
  1230.  *
  1231.  * PUBLIC: void __txn_updateckp __P((DB_ENV *, DB_LSN *));
  1232.  */
  1233. void
  1234. __txn_updateckp(dbenv, lsnp)
  1235. DB_ENV *dbenv;
  1236. DB_LSN *lsnp;
  1237. {
  1238. DB_TXNMGR *mgr;
  1239. DB_TXNREGION *region;
  1240. mgr = dbenv->tx_handle;
  1241. region = mgr->reginfo.primary;
  1242. /*
  1243.  * We want to make sure last_ckp only moves forward;  since
  1244.  * we drop locks above and in log_put, it's possible
  1245.  * for two calls to __txn_ckp_log to finish in a different
  1246.  * order from how they were called.
  1247.  */
  1248. R_LOCK(dbenv, &mgr->reginfo);
  1249. if (log_compare(&region->last_ckp, lsnp) < 0) {
  1250. region->last_ckp = *lsnp;
  1251. (void)time(&region->time_ckp);
  1252. }
  1253. R_UNLOCK(dbenv, &mgr->reginfo);
  1254. }