jrd_nod* InAutonomousTransactionNode::execute(thread_db* tdbb, jrd_req* request) { Database* const dbb = tdbb->getDatabase(); Attachment* const attachment = tdbb->getAttachment(); Impure* const impure = (Impure*) ((UCHAR*) request + impureOffset); if (request->req_operation == jrd_req::req_evaluate) { // Force unconditional reschedule. It prevents new transactions being // started after an attachment or a database shutdown has been initiated. JRD_reschedule(tdbb, 0, true); jrd_tra* const org_transaction = request->req_transaction; fb_assert(tdbb->getTransaction() == org_transaction); jrd_tra* const transaction = TRA_start(tdbb, org_transaction->tra_flags, org_transaction->tra_lock_timeout, org_transaction); TRA_attach_request(transaction, request); tdbb->setTransaction(transaction); request->req_auto_trans.push(org_transaction); impure->traNumber = transaction->tra_number; VIO_start_save_point(tdbb, transaction); impure->savNumber = transaction->tra_save_point->sav_number; if (!(attachment->att_flags & ATT_no_db_triggers)) { // run ON TRANSACTION START triggers EXE_execute_db_triggers(tdbb, transaction, jrd_req::req_trigger_trans_start); } return action; } jrd_tra* transaction = request->req_transaction; fb_assert(transaction); fb_assert(transaction != dbb->dbb_sys_trans); if (!impure->traNumber) return node->nod_parent; fb_assert(transaction->tra_number == impure->traNumber); switch (request->req_operation) { case jrd_req::req_return: if (!(attachment->att_flags & ATT_no_db_triggers)) { // run ON TRANSACTION COMMIT triggers EXE_execute_db_triggers(tdbb, transaction, jrd_req::req_trigger_trans_commit); } if (transaction->tra_save_point && !(transaction->tra_save_point->sav_flags & SAV_user) && !transaction->tra_save_point->sav_verb_count) { VIO_verb_cleanup(tdbb, transaction); } { // scope Firebird::AutoSetRestore2<jrd_req*, thread_db> autoNullifyRequest( tdbb, &thread_db::getRequest, &thread_db::setRequest, NULL); TRA_commit(tdbb, transaction, false); } // end scope break; case jrd_req::req_unwind: if (request->req_flags & req_leave) { try { if (!(attachment->att_flags & ATT_no_db_triggers)) { // run ON TRANSACTION COMMIT triggers EXE_execute_db_triggers(tdbb, transaction, jrd_req::req_trigger_trans_commit); } if (transaction->tra_save_point && !(transaction->tra_save_point->sav_flags & SAV_user) && !transaction->tra_save_point->sav_verb_count) { VIO_verb_cleanup(tdbb, transaction); } Firebird::AutoSetRestore2<jrd_req*, thread_db> autoNullifyRequest( tdbb, &thread_db::getRequest, &thread_db::setRequest, NULL); TRA_commit(tdbb, transaction, false); } catch (...) { request->req_flags &= ~req_leave; throw; } } else { ThreadStatusGuard temp_status(tdbb); if (!(attachment->att_flags & ATT_no_db_triggers)) { try { // run ON TRANSACTION ROLLBACK triggers EXE_execute_db_triggers(tdbb, transaction, jrd_req::req_trigger_trans_rollback); } catch (const Firebird::Exception&) { if (dbb->dbb_flags & DBB_bugcheck) throw; } } try { Firebird::AutoSetRestore2<jrd_req*, thread_db> autoNullifyRequest( tdbb, &thread_db::getRequest, &thread_db::setRequest, NULL); // undo all savepoints up to our one for (const Savepoint* save_point = transaction->tra_save_point; save_point && impure->savNumber <= save_point->sav_number; save_point = transaction->tra_save_point) { ++transaction->tra_save_point->sav_verb_count; VIO_verb_cleanup(tdbb, transaction); } TRA_rollback(tdbb, transaction, false, false); } catch (const Firebird::Exception&) { if (dbb->dbb_flags & DBB_bugcheck) throw; } } break; default: fb_assert(false); } impure->traNumber = impure->savNumber = 0; transaction = request->req_auto_trans.pop(); TRA_attach_request(transaction, request); tdbb->setTransaction(transaction); return node->nod_parent; }
void CryptoManager::cryptThread() { ISC_STATUS_ARRAY status_vector; try { // Try to take crypt mutex // If can't take that mutex - nothing to do, cryptThread already runs in our process MutexEnsureUnlock guard(cryptThreadMtx, FB_FUNCTION); if (!guard.tryEnter()) { return; } // establish context UserId user; user.usr_user_name = "(Crypt thread)"; Jrd::Attachment* const attachment = Jrd::Attachment::create(&dbb); RefPtr<SysAttachment> jAtt(new SysAttachment(attachment)); attachment->att_interface = jAtt; attachment->att_filename = dbb.dbb_filename; attachment->att_user = &user; BackgroundContextHolder tdbb(&dbb, attachment, status_vector, FB_FUNCTION); tdbb->tdbb_quantum = SWEEP_QUANTUM; ULONG lastPage = getLastPage(tdbb); ULONG runpage = 1; Stack<ULONG> pages; // Take exclusive threadLock // If can't take that lock - nothing to do, cryptThread already runs somewhere if (!LCK_lock(tdbb, threadLock, LCK_EX, LCK_NO_WAIT)) { return; } bool lckRelease = false; try { do { // Check is there some job to do while ((runpage = currentPage.exchangeAdd(+1)) < lastPage) { // forced terminate if (down) { break; } // nbackup state check if (dbb.dbb_backup_manager && dbb.dbb_backup_manager->getState() != nbak_state_normal) { if (currentPage.exchangeAdd(-1) >= lastPage) { // currentPage was set to last page by thread, closing database break; } THD_sleep(100); continue; } // scheduling if (--tdbb->tdbb_quantum < 0) { JRD_reschedule(tdbb, SWEEP_QUANTUM, true); } // writing page to disk will change it's crypt status in usual way WIN window(DB_PAGE_SPACE, runpage); Ods::pag* page = CCH_FETCH(tdbb, &window, LCK_write, pag_undefined); if (page && page->pag_type <= pag_max && (bool(page->pag_flags & Ods::crypted_page) != crypt) && Ods::pag_crypt_page[page->pag_type]) { CCH_MARK(tdbb, &window); pages.push(runpage); } CCH_RELEASE_TAIL(tdbb, &window); // sometimes save currentPage into DB header ++runpage; if ((runpage & 0x3FF) == 0) { writeDbHeader(tdbb, runpage, pages); } } // At this moment of time all pages with number < lastpage // are guaranteed to change crypt state. Check for added pages. lastPage = getLastPage(tdbb); // forced terminate if (down) { break; } } while (runpage < lastPage); // Finalize crypt if (!down) { writeDbHeader(tdbb, 0, pages); } // Release exclusive lock on StartCryptThread lckRelease = true; LCK_release(tdbb, threadLock); } catch (const Exception&) { try { if (!lckRelease) { // try to save current state of crypt thread if (!down) { writeDbHeader(tdbb, runpage, pages); } // Release exclusive lock on StartCryptThread LCK_release(tdbb, threadLock); } } catch (const Exception&) { } throw; } } catch (const Exception& ex) { // Error during context creation - we can't even release lock iscLogException("Crypt thread:", ex); } }