8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-23 22:03:03 +01:00

The merge is being fixed.

This commit is contained in:
dimitr 2008-01-16 12:22:11 +00:00
parent 453cdd809d
commit 48e81dd58a
10 changed files with 378 additions and 448 deletions

View File

@ -262,7 +262,6 @@
#undef AIX #undef AIX
#define WIN_NT #define WIN_NT
#undef SCO_EV #undef SCO_EV
#undef SINIXZ
#define FB_PREFIX "c:\\Program Files\\Firebird\\" #define FB_PREFIX "c:\\Program Files\\Firebird\\"

View File

@ -2831,7 +2831,7 @@ static dsc* evlTrunc(Jrd::thread_db* tdbb, const SysFunction* function, Jrd::jrd
} }
static dsc* evlUuidToChar(Jrd::thread_db* tdbb, SysFunction* function, Jrd::jrd_nod* args, Jrd::impure_value* impure) static dsc* evlUuidToChar(Jrd::thread_db* tdbb, const SysFunction* function, Jrd::jrd_nod* args, Jrd::impure_value* impure)
{ {
fb_assert(args->nod_count == 1); fb_assert(args->nod_count == 1);

View File

@ -2713,7 +2713,7 @@ static jrd_nod* looper(thread_db* tdbb, jrd_req* request, jrd_nod* in_node)
request->req_transaction = TRA_start(tdbb, request->req_transaction = TRA_start(tdbb,
request->req_transaction->tra_flags, request->req_transaction->tra_flags,
request->req_transaction->tra_lock_timeout); request->req_transaction->tra_lock_timeout);
tdbb->setTransaction(request->req_transaction)l tdbb->setTransaction(request->req_transaction);
if (!(tdbb->getAttachment()->att_flags & ATT_no_db_triggers)) if (!(tdbb->getAttachment()->att_flags & ATT_no_db_triggers))
{ {

View File

@ -1268,9 +1268,6 @@ static int blocking_ast_collation(void* ast_object)
LCK_release(tdbb, tt->existenceLock); LCK_release(tdbb, tt->existenceLock);
tt->existenceLock = NULL; tt->existenceLock = NULL;
// Restore the prior thread context
JRD_restore_thread_data();
} }
} }

View File

@ -83,17 +83,17 @@ static void set_lock_attachment(Lock*, Attachment*);
#ifdef SUPERSERVER #ifdef SUPERSERVER
inline LOCK_OWNER_T LCK_OWNER_ID_DBB(thread_db* tdbb) { inline LOCK_OWNER_T LCK_OWNER_ID_DBB(thread_db* tdbb) {
return (LOCK_OWNER_T) tdbb->tdbb_database; return (LOCK_OWNER_T) tdbb->getDatabase();
} }
inline LOCK_OWNER_T LCK_OWNER_ID_ATT(thread_db* tdbb) { inline LOCK_OWNER_T LCK_OWNER_ID_ATT(thread_db* tdbb) {
return (LOCK_OWNER_T) tdbb->tdbb_attachment; return (LOCK_OWNER_T) tdbb->getAttachment();
} }
inline SLONG* LCK_OWNER_HANDLE_DBB(thread_db* tdbb) { inline SLONG* LCK_OWNER_HANDLE_DBB(thread_db* tdbb) {
return &tdbb->tdbb_database->dbb_lock_owner_handle; return &tdbb->getDatabase()->dbb_lock_owner_handle;
} }
inline SLONG* LCK_OWNER_HANDLE_ATT(thread_db* tdbb) { inline SLONG* LCK_OWNER_HANDLE_ATT(thread_db* tdbb) {
return &tdbb->tdbb_attachment->att_lock_owner_handle; return &tdbb->getAttachment()->att_lock_owner_handle;
} }
#else /* SUPERSERVER */ #else /* SUPERSERVER */
@ -593,7 +593,7 @@ void LCK_init(thread_db* tdbb, enum lck_owner_t owner_type)
owner_id, owner_type, owner_handle_ptr)) owner_id, owner_type, owner_handle_ptr))
{ {
if (tdbb->tdbb_status_vector[1] == isc_lockmanerr) if (tdbb->tdbb_status_vector[1] == isc_lockmanerr)
tdbb->tdbb_database->dbb_flags |= DBB_bugcheck; tdbb->getDatabase()->dbb_flags |= DBB_bugcheck;
ERR_punt(); ERR_punt();
} }
} }

View File

@ -36,11 +36,11 @@ bool LCK_convert(Jrd::thread_db*, Jrd::Lock*, USHORT, SSHORT);
int LCK_convert_non_blocking(Jrd::thread_db*, Jrd::Lock*, USHORT, SSHORT); int LCK_convert_non_blocking(Jrd::thread_db*, Jrd::Lock*, USHORT, SSHORT);
int LCK_convert_opt(Jrd::thread_db*, Jrd::Lock*, USHORT); int LCK_convert_opt(Jrd::thread_db*, Jrd::Lock*, USHORT);
int LCK_downgrade(Jrd::thread_db*, Jrd::Lock*); int LCK_downgrade(Jrd::thread_db*, Jrd::Lock*);
void LCK_fini(Jrd::thread_db*, lck_owner_t); void LCK_fini(Jrd::thread_db*, Jrd::lck_owner_t);
SLONG LCK_get_owner_handle(Jrd::thread_db*, Jrd::lck_t); SLONG LCK_get_owner_handle(Jrd::thread_db*, Jrd::lck_t);
SLONG LCK_get_owner_handle_by_type(Jrd::thread_db*, lck_owner_t); SLONG LCK_get_owner_handle_by_type(Jrd::thread_db*, Jrd::lck_owner_t);
bool LCK_set_owner_handle(Jrd::thread_db*, Jrd::Lock*, SLONG); bool LCK_set_owner_handle(Jrd::thread_db*, Jrd::Lock*, SLONG);
void LCK_init(Jrd::thread_db*, lck_owner_t); void LCK_init(Jrd::thread_db*, Jrd::lck_owner_t);
int LCK_lock(Jrd::thread_db*, Jrd::Lock*, USHORT, SSHORT); int LCK_lock(Jrd::thread_db*, Jrd::Lock*, USHORT, SSHORT);
int LCK_lock_non_blocking(Jrd::thread_db*, Jrd::Lock*, USHORT, SSHORT); int LCK_lock_non_blocking(Jrd::thread_db*, Jrd::Lock*, USHORT, SSHORT);
int LCK_lock_opt(Jrd::thread_db*, Jrd::Lock*, USHORT, SSHORT); int LCK_lock_opt(Jrd::thread_db*, Jrd::Lock*, USHORT, SSHORT);

View File

@ -276,7 +276,7 @@ Firebird::MetaName MET_get_relation_field(thread_db* tdbb, const Firebird::MetaN
* *
**************************************/ **************************************/
SET_TDBB(tdbb); SET_TDBB(tdbb);
Database* dbb = tdbb->tdbb_database; Database* dbb = tdbb->getDatabase();
bool found = false; bool found = false;
Firebird::MetaName sourceName; Firebird::MetaName sourceName;
@ -4456,7 +4456,7 @@ static void make_relation_scope_name(const TEXT* rel_name,
static jrd_nod* parse_field_blr(thread_db* tdbb, bid* blob_id, const Firebird::MetaName name) static jrd_nod* parse_field_blr(thread_db* tdbb, bid* blob_id, const Firebird::MetaName name)
{ {
SET_TDBB(tdbb); SET_TDBB(tdbb);
Database* dbb = tdbb->tdbb_database; Database* dbb = tdbb->getDatabase();
CompilerScratch* csb = CompilerScratch::newCsb(*tdbb->getDefaultPool(), 5, name); CompilerScratch* csb = CompilerScratch::newCsb(*tdbb->getDefaultPool(), 5, name);

View File

@ -130,18 +130,18 @@ Lock* RLCK_transaction_relation_lock(thread_db* tdbb,
const SSHORT relLockLen = relation->getRelLockKeyLength(); const SSHORT relLockLen = relation->getRelLockKeyLength();
lock = FB_NEW_RPT(*transaction->tra_pool, relLockLen) Lock(); lock = FB_NEW_RPT(*transaction->tra_pool, relLockLen) Lock();
lock->lck_dbb = tdbb->tdbb_database; lock->lck_dbb = tdbb->getDatabase();
lock->lck_length = relLockLen; lock->lck_length = relLockLen;
relation->getRelLockKey(tdbb, &lock->lck_key.lck_string[0]); relation->getRelLockKey(tdbb, &lock->lck_key.lck_string[0]);
lock->lck_type = LCK_relation; lock->lck_type = LCK_relation;
lock->lck_owner_handle = LCK_get_owner_handle(tdbb, lock->lck_type); lock->lck_owner_handle = LCK_get_owner_handle(tdbb, lock->lck_type);
lock->lck_parent = tdbb->tdbb_database->dbb_lock; lock->lck_parent = tdbb->getDatabase()->dbb_lock;
// the lck_object is used here to find the relation // the lck_object is used here to find the relation
// block from the lock block // block from the lock block
lock->lck_object = relation; lock->lck_object = relation;
// enter all relation locks into the intra-process lock manager and treat // enter all relation locks into the intra-process lock manager and treat
// them as compatible within the attachment according to IPLM rules // them as compatible within the attachment according to IPLM rules
lock->lck_compatible = tdbb->tdbb_attachment; lock->lck_compatible = tdbb->getAttachment();
// for relations locked within a transaction, add a second level of // for relations locked within a transaction, add a second level of
// compatibility within the intra-process lock manager which specifies // compatibility within the intra-process lock manager which specifies
// that relation locks are incompatible with locks taken out by other // that relation locks are incompatible with locks taken out by other

View File

@ -42,7 +42,7 @@
#include "../jrd/rse.h" #include "../jrd/rse.h"
#include "../jrd/intl_classes.h" #include "../jrd/intl_classes.h"
#include "../jrd/jrd_pwd.h" #include "../jrd/jrd_pwd.h"
#include "../jrd/thd.h" #include "../jrd/ThreadStart.h"
#include "../jrd/blb_proto.h" #include "../jrd/blb_proto.h"
#include "../jrd/cch_proto.h" #include "../jrd/cch_proto.h"
#include "../jrd/cmp_proto.h" #include "../jrd/cmp_proto.h"
@ -66,32 +66,11 @@
#include "../jrd/jrd_proto.h" #include "../jrd/jrd_proto.h"
#include "../common/classes/ClumpletWriter.h" #include "../common/classes/ClumpletWriter.h"
#include "../common/classes/TriState.h" #include "../common/classes/TriState.h"
#ifndef VMS
#include "../lock/lock_proto.h" #include "../lock/lock_proto.h"
#endif
const int DYN_MSG_FAC = 8; const int DYN_MSG_FAC = 8;
#ifdef VMS
#include ssdef
#include lckdef
const int EVENT_FLAG = 15;
static const SCHAR lock_types[] =
{
0,
LCK$K_NLMODE,
LCK$K_CRMODE,
LCK$K_CWMODE,
LCK$K_PRMODE,
LCK$K_PWMODE,
LCK$K_EXMODE
};
#endif /* VMS */
static const SLONG MAX_TRA_NUMBER = MAX_SLONG; static const SLONG MAX_TRA_NUMBER = MAX_SLONG;
using namespace Jrd; using namespace Jrd;
@ -122,9 +101,7 @@ static void start_sweeper(thread_db*, Database*);
static THREAD_ENTRY_DECLARE sweep_database(THREAD_ENTRY_PARAM); static THREAD_ENTRY_DECLARE sweep_database(THREAD_ENTRY_PARAM);
#endif #endif
static void transaction_options(thread_db*, jrd_tra*, const UCHAR*, USHORT); static void transaction_options(thread_db*, jrd_tra*, const UCHAR*, USHORT);
#ifdef VMS static jrd_tra* transaction_start(thread_db* tdbb, jrd_tra* temp);
static void vms_convert(Lock*, SLONG*, SCHAR, bool);
#endif
static const UCHAR sweep_tpb[] = static const UCHAR sweep_tpb[] =
{ {
@ -752,10 +729,9 @@ void TRA_init(thread_db* tdbb)
Database* dbb = tdbb->getDatabase(); Database* dbb = tdbb->getDatabase();
CHECK_DBB(dbb); CHECK_DBB(dbb);
jrd_tra* trans = FB_NEW_RPT(*dbb->dbb_permanent, 0) jrd_tra(*dbb->dbb_permanent); jrd_tra* trans = FB_NEW_RPT(*dbb->dbb_permanent, 0) jrd_tra(dbb->dbb_permanent);
dbb->dbb_sys_trans = trans; dbb->dbb_sys_trans = trans;
trans->tra_flags |= TRA_system | TRA_ignore_limbo; trans->tra_flags |= TRA_system | TRA_ignore_limbo;
trans->tra_pool = dbb->dbb_permanent;
} }
@ -987,8 +963,7 @@ jrd_tra* TRA_reconnect(thread_db* tdbb, const UCHAR* id, USHORT length)
Jrd::ContextPoolHolder context(tdbb, JrdMemoryPool::createPool()); Jrd::ContextPoolHolder context(tdbb, JrdMemoryPool::createPool());
jrd_tra* trans = FB_NEW_RPT(*tdbb->getDefaultPool(), 0) jrd_tra(*tdbb->getDefaultPool()); jrd_tra* trans = FB_NEW_RPT(*tdbb->getDefaultPool(), 0) jrd_tra(tdbb->getDefaultPool());
trans->tra_pool = tdbb->getDefaultPool();
trans->tra_number = gds__vax_integer(id, length); trans->tra_number = gds__vax_integer(id, length);
trans->tra_flags |= TRA_prepared | TRA_reconnected | TRA_write; trans->tra_flags |= TRA_prepared | TRA_reconnected | TRA_write;
@ -1490,6 +1465,42 @@ int TRA_snapshot_state(thread_db* tdbb, const jrd_tra* trans, SLONG number)
} }
jrd_tra* TRA_start(thread_db* tdbb, ULONG flags, SSHORT lock_timeout)
{
/**************************************
*
* T R A _ s t a r t
*
**************************************
*
* Functional description
* Start a user transaction.
*
**************************************/
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
Attachment* attachment = tdbb->getAttachment();
if (dbb->dbb_ast_flags & DBB_shut_tran)
{
ERR_post(isc_shutinprog, isc_arg_string,
ERR_string(tdbb->getAttachment()->att_filename),
0);
}
// To handle the problems of relation locks, allocate a temporary
// transaction block first, seize relation locks, then go ahead and
// make up the real transaction block.
Jrd::ContextPoolHolder context(tdbb, JrdMemoryPool::createPool());
jrd_tra* temp = FB_NEW_RPT(*tdbb->getDefaultPool(), 0) jrd_tra(tdbb->getDefaultPool());
temp->tra_flags = flags;
temp->tra_lock_timeout = lock_timeout;
return transaction_start(tdbb, temp);
}
jrd_tra* TRA_start(thread_db* tdbb, int tpb_length, const UCHAR* tpb) jrd_tra* TRA_start(thread_db* tdbb, int tpb_length, const UCHAR* tpb)
{ {
/************************************** /**************************************
@ -1505,338 +1516,23 @@ jrd_tra* TRA_start(thread_db* tdbb, int tpb_length, const UCHAR* tpb)
SET_TDBB(tdbb); SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase(); Database* dbb = tdbb->getDatabase();
Attachment* attachment = tdbb->getAttachment(); Attachment* attachment = tdbb->getAttachment();
WIN window(DB_PAGE_SPACE, -1);
if (dbb->dbb_ast_flags & DBB_shut_tran) { if (dbb->dbb_ast_flags & DBB_shut_tran)
{
ERR_post(isc_shutinprog, isc_arg_string, ERR_post(isc_shutinprog, isc_arg_string,
ERR_string(tdbb->getAttachment()->att_filename), ERR_string(tdbb->getAttachment()->att_filename),
0); 0);
} }
/* To handle the problems of relation locks, allocate a temporary // To handle the problems of relation locks, allocate a temporary
transaction block first, seize relation locks, the go ahead and // transaction block first, seize relation locks, then go ahead and
make up the real transaction block. */ // make up the real transaction block.
Jrd::ContextPoolHolder context(tdbb, JrdMemoryPool::createPool()); Jrd::ContextPoolHolder context(tdbb, JrdMemoryPool::createPool());
jrd_tra* temp = FB_NEW_RPT(*tdbb->getDefaultPool(), 0) jrd_tra(*tdbb->getDefaultPool()); jrd_tra* temp = FB_NEW_RPT(*tdbb->getDefaultPool(), 0) jrd_tra(tdbb->getDefaultPool());
temp->tra_pool = tdbb->getDefaultPool();
transaction_options(tdbb, temp, tpb, tpb_length); transaction_options(tdbb, temp, tpb, tpb_length);
Lock* lock = TRA_transaction_lock(tdbb, temp); return transaction_start(tdbb, temp);
/* Read header page and allocate transaction number. Since
the transaction inventory page was initialized to zero, it
transaction is automatically marked active. */
ULONG oldest, number, active, oldest_active, oldest_snapshot;
#ifdef SUPERSERVER_V2
number = bump_transaction_id(tdbb, &window);
oldest = dbb->dbb_oldest_transaction;
active = MAX(dbb->dbb_oldest_active, dbb->dbb_oldest_transaction);
oldest_active = dbb->dbb_oldest_active;
oldest_snapshot = dbb->dbb_oldest_snapshot;
#else /* SUPERSERVER_V2 */
if (dbb->dbb_flags & DBB_read_only) {
number = ++dbb->dbb_next_transaction;
oldest = dbb->dbb_oldest_transaction;
oldest_active = dbb->dbb_oldest_active;
oldest_snapshot = dbb->dbb_oldest_snapshot;
}
else {
const header_page* header = bump_transaction_id(tdbb, &window);
number = header->hdr_next_transaction;
oldest = header->hdr_oldest_transaction;
oldest_active = header->hdr_oldest_active;
oldest_snapshot = header->hdr_oldest_snapshot;
}
// oldest (OIT) > oldest_active (OAT) if OIT was advanced by sweep
// and no transactions was started after the sweep starts
active = MAX(oldest_active, oldest);
#endif /* SUPERSERVER_V2 */
/* Allocate pool and transactions block. Since, by policy,
all transactions older than the oldest are either committed
or cleaned up, they can be all considered as committed. To
make everything simpler, round down the oldest to a multiple
of four, which puts the transaction on a byte boundary. */
ULONG base = oldest & ~TRA_MASK;
jrd_tra* trans;
if (temp->tra_flags & TRA_read_committed)
trans = FB_NEW_RPT(*tdbb->getDefaultPool(), 0) jrd_tra(*tdbb->getDefaultPool());
else {
trans = FB_NEW_RPT(*tdbb->getDefaultPool(), (number - base + TRA_MASK) / 4) jrd_tra(*tdbb->getDefaultPool());
}
trans->tra_pool = temp->tra_pool;
trans->tra_relation_locks = temp->tra_relation_locks;
trans->tra_lock_timeout = temp->tra_lock_timeout;
trans->tra_flags = temp->tra_flags;
trans->tra_number = number;
trans->tra_top = number;
trans->tra_oldest = oldest;
trans->tra_oldest_active = active;
delete temp;
trans->tra_lock = lock;
lock->lck_key.lck_long = number;
// Put the TID of the oldest active transaction (from the header page)
// in the new transaction's lock.
// hvlad: it is important to put transaction number for read-committed
// transaction instead of oldest active to correctly calculate new oldest
// active value (look at call to LCK_query_data below which will take into
// account this new lock too)
lock->lck_data = (trans->tra_flags & TRA_read_committed) ? number : active;
lock->lck_object = trans;
if (!LCK_lock_non_blocking(tdbb, lock, LCK_write, LCK_WAIT)) {
#ifndef SUPERSERVER_V2
if (!(dbb->dbb_flags & DBB_read_only))
CCH_RELEASE(tdbb, &window);
#endif
delete trans;
ERR_post(isc_lock_conflict, 0);
}
/* Link the transaction to the attachment block before releasing
header page for handling signals. */
link_transaction(tdbb, trans);
#ifndef SUPERSERVER_V2
if (!(dbb->dbb_flags & DBB_read_only))
CCH_RELEASE(tdbb, &window);
#endif
if (dbb->dbb_flags & DBB_read_only) {
/* Set transaction flags to TRA_precommitted, TRA_readonly */
trans->tra_flags |= (TRA_readonly | TRA_precommitted);
}
/* Next, take a snapshot of all transactions between the oldest interesting
transaction and the current. Don't bother to get a snapshot for
read-committed transactions; they use the snapshot off the dbb block
since they need to know what is currently committed. */
if (trans->tra_flags & TRA_read_committed)
TPC_initialize_tpc(tdbb, number);
else
TRA_get_inventory(tdbb, trans->tra_transactions, base, number);
/* Next task is to find the oldest active transaction on the system. This
is needed for garbage collection. Things are made ever so slightly
more complicated by the fact that existing transaction may have oldest
actives older than they are. */
Lock temp_lock;
temp_lock.lck_dbb = dbb;
temp_lock.lck_object = trans;
temp_lock.lck_type = LCK_tra;
temp_lock.lck_owner_handle =
LCK_get_owner_handle(tdbb, temp_lock.lck_type);
temp_lock.lck_parent = dbb->dbb_lock;
temp_lock.lck_length = sizeof(SLONG);
trans->tra_oldest_active = number;
base = oldest & ~TRA_MASK;
oldest_active = number;
bool cleanup = !(number % TRA_ACTIVE_CLEANUP);
USHORT oldest_state;
for (; active < number; active++) {
if (trans->tra_flags & TRA_read_committed)
oldest_state = TPC_cache_state(tdbb, active);
else {
const ULONG byte = TRANS_OFFSET(active - base);
const USHORT shift = TRANS_SHIFT(active);
oldest_state =
(trans->tra_transactions[byte] >> shift) & TRA_MASK;
}
if (oldest_state == tra_active) {
temp_lock.lck_key.lck_long = active;
SLONG data = LCK_read_data(&temp_lock);
if (!data) {
if (cleanup) {
if (TRA_wait(tdbb, trans, active, jrd_tra::tra_no_wait) == tra_committed)
cleanup = false;
continue;
}
else
data = active;
}
oldest_active = MIN(oldest_active, active);
/* Find the oldest record version that cannot be garbage collected yet
by taking the minimum of all all versions needed by all active
transactions. */
if (data < trans->tra_oldest_active)
trans->tra_oldest_active = data;
/* If the lock data for any active transaction matches a previously
computed value then there is no need to continue. There can't be
an older lock data in the remaining active transactions. */
if (trans->tra_oldest_active == (SLONG) oldest_snapshot)
break;
#ifndef VMS
/* Query the minimum lock data for all active transaction locks.
This will be the oldest active snapshot used for regulating
garbage collection. */
data = LCK_query_data(dbb->dbb_lock, LCK_tra, LCK_MIN);
if (data && data < trans->tra_oldest_active)
trans->tra_oldest_active = data;
break;
#endif
}
}
// Put the TID of the oldest active transaction (just calculated)
// in the new transaction's lock.
// hvlad: for read-committed transaction put tra_number to prevent
// unnecessary blocking of garbage collection by read-committed
// transactions
const ULONG lck_data =
(trans->tra_flags & TRA_read_committed) ? number : oldest_active;
if (lock->lck_data != (SLONG) lck_data)
LCK_write_data(lock, lck_data);
/* Scan commit retaining transactions which have started after us but which
want to preserve an oldest active from an already committed transaction.
If a previously computed oldest snapshot was matched then there's no
need to worry about commit retaining transactions. */
#ifdef VMS
if (trans->tra_oldest_active != oldest_snapshot)
compute_oldest_retaining(tdbb, trans, false);
#endif
/* Finally, scan transactions looking for the oldest interesting transaction -- the oldest
non-commited transaction. This will not be updated immediately, but saved until the
next update access to the header page */
oldest_state = tra_committed;
for (oldest = trans->tra_oldest; oldest < number; oldest++) {
if (trans->tra_flags & TRA_read_committed)
oldest_state = TPC_cache_state(tdbb, oldest);
else {
const ULONG byte = TRANS_OFFSET(oldest - base);
const USHORT shift = TRANS_SHIFT(oldest);
oldest_state =
(trans->tra_transactions[byte] >> shift) & TRA_MASK;
}
if (oldest_state != tra_committed && oldest_state != tra_precommitted)
break;
}
#ifdef MULTI_THREAD
if (--oldest > (ULONG) dbb->dbb_oldest_transaction)
dbb->dbb_oldest_transaction = oldest;
if (oldest_active > (ULONG) dbb->dbb_oldest_active)
dbb->dbb_oldest_active = oldest_active;
#else
dbb->dbb_oldest_transaction = oldest - 1;
dbb->dbb_oldest_active = oldest_active;
#endif
if (trans->tra_oldest_active > dbb->dbb_oldest_snapshot) {
dbb->dbb_oldest_snapshot = trans->tra_oldest_active;
#if defined(GARBAGE_THREAD)
if (!(dbb->dbb_flags & DBB_gc_active) &&
(dbb->dbb_flags & DBB_gc_background) )
{
dbb->dbb_flags |= DBB_gc_pending;
ISC_event_post(dbb->dbb_gc_event);
}
#endif
}
/* If the transaction block is getting out of hand, force a sweep */
if (dbb->dbb_sweep_interval &&
!(tdbb->getAttachment()->att_flags & ATT_no_cleanup) &&
(trans->tra_oldest_active - trans->tra_oldest >
dbb->dbb_sweep_interval) && oldest_state != tra_limbo)
{
#ifdef SWEEP_THREAD
// Why nobody checks the result? Changed the function to return nothing.
start_sweeper(tdbb, dbb);
#else
// force a sweep
TRA_sweep(tdbb, trans);
#endif
}
/* Check in with external file system */
EXT_trans_start(trans);
/* Start a 'transaction-level' savepoint, unless this is the
system transaction, or unless the transactions doesn't want
a savepoint to be started. This savepoint will be used to
undo the transaction if it rolls back. */
if ((trans != dbb->dbb_sys_trans) &&
!(trans->tra_flags & TRA_no_auto_undo))
{
VIO_start_save_point(tdbb, trans);
trans->tra_save_point->sav_flags |= SAV_trans_level;
}
/* Allocate the cancellation lock */
lock = FB_NEW_RPT(*trans->tra_pool, sizeof(SLONG)) Lock();
trans->tra_cancel_lock = lock;
lock->lck_type = LCK_cancel;
lock->lck_owner_handle = LCK_get_owner_handle(tdbb, lock->lck_type);
lock->lck_parent = dbb->dbb_lock;
lock->lck_length = sizeof(SLONG);
lock->lck_key.lck_long = trans->tra_number;
lock->lck_dbb = dbb;
lock->lck_ast = blocking_ast_transaction;
lock->lck_object = trans;
/* if the user asked us to restart all requests in this attachment,
do so now using the new transaction */
if (trans->tra_flags & TRA_restart_requests)
restart_requests(tdbb, trans);
/* If the transaction is read-only and read committed, it can be
precommitted because it can't modify any records and doesn't
need a snapshot preserved. This transaction type can run
forever without impacting garbage collection or causing
transaction bitmap growth. */
if (trans->tra_flags & TRA_readonly &&
trans->tra_flags & TRA_read_committed)
{
TRA_set_state(tdbb, trans, trans->tra_number, tra_committed);
LCK_release(tdbb, trans->tra_lock);
delete trans->tra_lock;
trans->tra_lock = NULL;
trans->tra_flags |= TRA_precommitted;
}
if (trans->tra_flags & TRA_precommitted)
TRA_precommited(tdbb, 0, trans->tra_number);
return trans;
} }
@ -1932,6 +1628,7 @@ bool TRA_sweep(thread_db* tdbb, jrd_tra* trans)
during the course of the database sweep. Since it is used during the course of the database sweep. Since it is used
below to advance the OIT we must save it before it changes. */ below to advance the OIT we must save it before it changes. */
if (!(transaction = trans)) if (!(transaction = trans))
transaction = TRA_start(tdbb, sizeof(sweep_tpb), sweep_tpb); transaction = TRA_start(tdbb, sizeof(sweep_tpb), sweep_tpb);
@ -2091,7 +1788,6 @@ int TRA_wait(thread_db* tdbb, jrd_tra* trans, SLONG number, jrd_tra::wait_t wait
temp_lock.lck_parent = dbb->dbb_lock; temp_lock.lck_parent = dbb->dbb_lock;
temp_lock.lck_length = sizeof(SLONG); temp_lock.lck_length = sizeof(SLONG);
temp_lock.lck_key.lck_long = number; temp_lock.lck_key.lck_long = number;
temp_lock.lck_owner = trans;
const SSHORT timeout = const SSHORT timeout =
(wait == jrd_tra::tra_wait) ? trans->getLockWait() : 0; (wait == jrd_tra::tra_wait) ? trans->getLockWait() : 0;
@ -2154,8 +1850,7 @@ static int blocking_ast_transaction(void* ast_object)
jrd_tra* transaction = static_cast<jrd_tra*>(ast_object); jrd_tra* transaction = static_cast<jrd_tra*>(ast_object);
fb_assert(transaction); fb_assert(transaction);
thread_db thd_context, *tdbb; ThreadContextHolder tdbb;
JRD_set_thread_data(tdbb, thd_context);
tdbb->setDatabase(transaction->tra_cancel_lock->lck_dbb); tdbb->setDatabase(transaction->tra_cancel_lock->lck_dbb);
tdbb->setAttachment(transaction->tra_cancel_lock->lck_attachment); tdbb->setAttachment(transaction->tra_cancel_lock->lck_attachment);
@ -2168,8 +1863,6 @@ static int blocking_ast_transaction(void* ast_object)
LCK_release(tdbb, transaction->tra_cancel_lock); LCK_release(tdbb, transaction->tra_cancel_lock);
transaction->tra_flags |= TRA_cancel_request; transaction->tra_flags |= TRA_cancel_request;
JRD_restore_thread_data();
return 0; return 0;
} }
@ -2318,16 +2011,7 @@ static void compute_oldest_retaining(
lock->lck_parent = dbb->dbb_lock; lock->lck_parent = dbb->dbb_lock;
lock->lck_length = sizeof(SLONG); lock->lck_length = sizeof(SLONG);
lock->lck_object = reinterpret_cast<blk*>(dbb); lock->lck_object = reinterpret_cast<blk*>(dbb);
#ifdef VMS
if (LCK_lock(tdbb, lock, LCK_EX, LCK_NO_WAIT)) {
number = 0;
vms_convert(lock, &number, LCK_SR, true);
}
else
LCK_lock(tdbb, lock, LCK_SR, LCK_WAIT);
#else
LCK_lock_non_blocking(tdbb, lock, LCK_SR, LCK_WAIT); LCK_lock_non_blocking(tdbb, lock, LCK_SR, LCK_WAIT);
#endif
dbb->dbb_retaining_lock = lock; dbb->dbb_retaining_lock = lock;
} }
@ -2339,28 +2023,16 @@ static void compute_oldest_retaining(
readers and writers don't interfere. */ readers and writers don't interfere. */
SLONG youngest_retaining; SLONG youngest_retaining;
if (write_flag) { if (write_flag) {
#ifdef VMS
vms_convert(lock, &youngest_retaining, LCK_PW, true);
if (number > youngest_retaining)
vms_convert(lock, &number, LCK_SR, true);
else
vms_convert(lock, 0, LCK_SR, true);
#else
LCK_convert(tdbb, lock, LCK_PW, TRUE); LCK_convert(tdbb, lock, LCK_PW, TRUE);
youngest_retaining = LOCK_read_data(lock->lck_id); youngest_retaining = LOCK_read_data(lock->lck_id);
if (number > youngest_retaining) if (number > youngest_retaining)
LCK_write_data(lock, number); LCK_write_data(lock, number);
LCK_convert(tdbb, lock, LCK_SR, TRUE); LCK_convert(tdbb, lock, LCK_SR, TRUE);
#endif
} }
else { else {
#ifdef VMS
vms_convert(lock, &youngest_retaining, LCK_SR, true);
#else
youngest_retaining = LOCK_read_data(lock->lck_id); youngest_retaining = LOCK_read_data(lock->lck_id);
#endif
if (number > youngest_retaining) if (number > youngest_retaining)
return; return;
@ -2403,7 +2075,7 @@ static void expand_view_lock(jrd_tra* transaction, jrd_rel* relation, SCHAR lock
/* set up the lock on the relation/view */ /* set up the lock on the relation/view */
Lock* lock = RLCK_transaction_relation_lock(transaction, relation); Lock* lock = RLCK_transaction_relation_lock(tdbb, transaction, relation);
lock->lck_logical = lock_type; lock->lck_logical = lock_type;
@ -2821,18 +2493,12 @@ static THREAD_ENTRY_DECLARE sweep_database(THREAD_ENTRY_PARAM database)
**************************************/ **************************************/
Firebird::ClumpletWriter dpb(Firebird::ClumpletReader::Tagged, MAX_DPB_SIZE, isc_dpb_version1); Firebird::ClumpletWriter dpb(Firebird::ClumpletReader::Tagged, MAX_DPB_SIZE, isc_dpb_version1);
const char* szAuthenticator = "sweeper";
dpb.insertString(isc_dpb_user_name,
szAuthenticator, strlen(szAuthenticator));
const char* szPassword = "none";
dpb.insertString(isc_dpb_password,
szPassword, strlen(szPassword));
dpb.insertByte(isc_dpb_sweep, isc_dpb_records); dpb.insertByte(isc_dpb_sweep, isc_dpb_records);
// sometimes security database is also to be swept // sometimes security database is also to be swept
dpb.insertByte(isc_dpb_gsec_attach, 1); dpb.insertByte(isc_dpb_gsec_attach, 1);
// use trusted authentication to attach database
// Temporary disable security for this thread to proceed with internal attachment const char* szAuthenticator = "sweeper";
JRD_thread_security_disable(true); dpb.insertString(isc_dpb_trusted_auth, szAuthenticator, strlen(szAuthenticator));
ISC_STATUS_ARRAY status_vector = {0}; ISC_STATUS_ARRAY status_vector = {0};
isc_db_handle db_handle = 0; isc_db_handle db_handle = 0;
@ -2841,8 +2507,6 @@ static THREAD_ENTRY_DECLARE sweep_database(THREAD_ENTRY_PARAM database)
&db_handle, dpb.getBufferLength(), &db_handle, dpb.getBufferLength(),
reinterpret_cast<const char*>(dpb.getBuffer())); reinterpret_cast<const char*>(dpb.getBuffer()));
JRD_thread_security_disable(false);
if (db_handle) if (db_handle)
{ {
isc_detach_database(status_vector, &db_handle); isc_detach_database(status_vector, &db_handle);
@ -3112,51 +2776,331 @@ static void transaction_options(
} }
#ifdef VMS static jrd_tra* transaction_start(thread_db* tdbb, jrd_tra* temp)
static void vms_convert(Lock* lock, SLONG* data, SCHAR type, bool wait)
{ {
/************************************** /**************************************
* *
* v m s _ c o n v e r t * t r a n s a c t i o n _ s t a r t
* *
************************************** **************************************
* *
* Functional description * Functional description
* Comply with VMS protocol for lock I/O. * Start a transaction.
* *
**************************************/ **************************************/
lock_status lksb; SET_TDBB(tdbb);
lksb.lksb_lock_id = lock->lck_id; Database* dbb = tdbb->getDatabase();
Attachment* attachment = tdbb->getAttachment();
WIN window(DB_PAGE_SPACE, -1);
if (data && type < lock->lck_physical) Lock* lock = TRA_transaction_lock(tdbb, temp);
lksb.lksb_value[0] = *data;
SLONG flags = LCK$M_CONVERT; /* Read header page and allocate transaction number. Since
the transaction inventory page was initialized to zero, it
transaction is automatically marked active. */
if (data) ULONG oldest, number, active, oldest_active, oldest_snapshot;
flags |= LCK$M_VALBLK;
if (!wait) #ifdef SUPERSERVER_V2
flags |= LCK$M_NOQUEUE; number = bump_transaction_id(tdbb, &window);
oldest = dbb->dbb_oldest_transaction;
active = MAX(dbb->dbb_oldest_active, dbb->dbb_oldest_transaction);
oldest_active = dbb->dbb_oldest_active;
oldest_snapshot = dbb->dbb_oldest_snapshot;
SLONG status = sys$enqw(EVENT_FLAG, lock_types[type], &lksb, flags, #else /* SUPERSERVER_V2 */
NULL, NULL, NULL, // AST routine when granted if (dbb->dbb_flags & DBB_read_only) {
NULL, // ast_argument number = ++dbb->dbb_next_transaction;
NULL, // ast_routine oldest = dbb->dbb_oldest_transaction;
NULL, NULL); oldest_active = dbb->dbb_oldest_active;
oldest_snapshot = dbb->dbb_oldest_snapshot;
}
else {
const header_page* header = bump_transaction_id(tdbb, &window);
number = header->hdr_next_transaction;
oldest = header->hdr_oldest_transaction;
oldest_active = header->hdr_oldest_active;
oldest_snapshot = header->hdr_oldest_snapshot;
}
if (!wait && status == SS$_NOTQUEUED) // oldest (OIT) > oldest_active (OAT) if OIT was advanced by sweep
return; // false; // and no transactions was started after the sweep starts
active = MAX(oldest_active, oldest);
if (!(status & 1) || !((status = lksb.lksb_status) & 1)) #endif /* SUPERSERVER_V2 */
ERR_post(isc_sys_request, isc_arg_string,
"sys$enqw (commit retaining lock)", isc_arg_vms, status, 0);
if (data && type >= lock->lck_physical) /* Allocate pool and transactions block. Since, by policy,
*data = lksb.lksb_value[0]; all transactions older than the oldest are either committed
or cleaned up, they can be all considered as committed. To
make everything simpler, round down the oldest to a multiple
of four, which puts the transaction on a byte boundary. */
lock->lck_physical = lock->lck_logical = type; ULONG base = oldest & ~TRA_MASK;
return; // true; jrd_tra* trans;
} if (temp->tra_flags & TRA_read_committed)
trans = FB_NEW_RPT(*tdbb->getDefaultPool(), 0) jrd_tra(tdbb->getDefaultPool());
else {
trans = FB_NEW_RPT(*tdbb->getDefaultPool(), (number - base + TRA_MASK) / 4) jrd_tra(tdbb->getDefaultPool());
}
fb_assert(trans->tra_pool == temp->tra_pool);
trans->tra_relation_locks = temp->tra_relation_locks;
trans->tra_lock_timeout = temp->tra_lock_timeout;
trans->tra_flags = temp->tra_flags;
trans->tra_number = number;
trans->tra_top = number;
trans->tra_oldest = oldest;
trans->tra_oldest_active = active;
delete temp;
trans->tra_lock = lock;
lock->lck_key.lck_long = number;
// Put the TID of the oldest active transaction (from the header page)
// in the new transaction's lock.
// hvlad: it is important to put transaction number for read-committed
// transaction instead of oldest active to correctly calculate new oldest
// active value (look at call to LCK_query_data below which will take into
// account this new lock too)
lock->lck_data = (trans->tra_flags & TRA_read_committed) ? number : active;
lock->lck_object = trans;
if (!LCK_lock_non_blocking(tdbb, lock, LCK_write, LCK_WAIT)) {
#ifndef SUPERSERVER_V2
if (!(dbb->dbb_flags & DBB_read_only))
CCH_RELEASE(tdbb, &window);
#endif #endif
delete trans;
ERR_post(isc_lock_conflict, 0);
}
/* Link the transaction to the attachment block before releasing
header page for handling signals. */
link_transaction(tdbb, trans);
#ifndef SUPERSERVER_V2
if (!(dbb->dbb_flags & DBB_read_only))
CCH_RELEASE(tdbb, &window);
#endif
if (dbb->dbb_flags & DBB_read_only) {
/* Set transaction flags to TRA_precommitted, TRA_readonly */
trans->tra_flags |= (TRA_readonly | TRA_precommitted);
}
/* Next, take a snapshot of all transactions between the oldest interesting
transaction and the current. Don't bother to get a snapshot for
read-committed transactions; they use the snapshot off the dbb block
since they need to know what is currently committed. */
if (trans->tra_flags & TRA_read_committed)
TPC_initialize_tpc(tdbb, number);
else
TRA_get_inventory(tdbb, trans->tra_transactions, base, number);
/* Next task is to find the oldest active transaction on the system. This
is needed for garbage collection. Things are made ever so slightly
more complicated by the fact that existing transaction may have oldest
actives older than they are. */
Lock temp_lock;
temp_lock.lck_dbb = dbb;
temp_lock.lck_object = trans;
temp_lock.lck_type = LCK_tra;
temp_lock.lck_owner_handle =
LCK_get_owner_handle(tdbb, temp_lock.lck_type);
temp_lock.lck_parent = dbb->dbb_lock;
temp_lock.lck_length = sizeof(SLONG);
trans->tra_oldest_active = number;
base = oldest & ~TRA_MASK;
oldest_active = number;
bool cleanup = !(number % TRA_ACTIVE_CLEANUP);
USHORT oldest_state;
for (; active < number; active++) {
if (trans->tra_flags & TRA_read_committed)
oldest_state = TPC_cache_state(tdbb, active);
else {
const ULONG byte = TRANS_OFFSET(active - base);
const USHORT shift = TRANS_SHIFT(active);
oldest_state =
(trans->tra_transactions[byte] >> shift) & TRA_MASK;
}
if (oldest_state == tra_active) {
temp_lock.lck_key.lck_long = active;
SLONG data = LCK_read_data(&temp_lock);
if (!data) {
if (cleanup) {
if (TRA_wait(tdbb, trans, active, jrd_tra::tra_no_wait) == tra_committed)
cleanup = false;
continue;
}
data = active;
}
oldest_active = MIN(oldest_active, active);
/* Find the oldest record version that cannot be garbage collected yet
by taking the minimum of all all versions needed by all active
transactions. */
if (data < trans->tra_oldest_active)
trans->tra_oldest_active = data;
/* If the lock data for any active transaction matches a previously
computed value then there is no need to continue. There can't be
an older lock data in the remaining active transactions. */
if (trans->tra_oldest_active == (SLONG) oldest_snapshot)
break;
#ifndef VMS
/* Query the minimum lock data for all active transaction locks.
This will be the oldest active snapshot used for regulating
garbage collection. */
data = LCK_query_data(dbb->dbb_lock, LCK_tra, LCK_MIN);
if (data && data < trans->tra_oldest_active)
trans->tra_oldest_active = data;
break;
#endif
}
}
// Put the TID of the oldest active transaction (just calculated)
// in the new transaction's lock.
// hvlad: for read-committed transaction put tra_number to prevent
// unnecessary blocking of garbage collection by read-committed
// transactions
const ULONG lck_data =
(trans->tra_flags & TRA_read_committed) ? number : oldest_active;
if (lock->lck_data != (SLONG) lck_data)
LCK_write_data(lock, lck_data);
/* Scan commit retaining transactions which have started after us but which
want to preserve an oldest active from an already committed transaction.
If a previously computed oldest snapshot was matched then there's no
need to worry about commit retaining transactions. */
#ifdef VMS
if (trans->tra_oldest_active != oldest_snapshot)
compute_oldest_retaining(tdbb, trans, false);
#endif
/* Finally, scan transactions looking for the oldest interesting transaction -- the oldest
non-commited transaction. This will not be updated immediately, but saved until the
next update access to the header page */
oldest_state = tra_committed;
for (oldest = trans->tra_oldest; oldest < number; oldest++) {
if (trans->tra_flags & TRA_read_committed)
oldest_state = TPC_cache_state(tdbb, oldest);
else {
const ULONG byte = TRANS_OFFSET(oldest - base);
const USHORT shift = TRANS_SHIFT(oldest);
oldest_state =
(trans->tra_transactions[byte] >> shift) & TRA_MASK;
}
if (oldest_state != tra_committed && oldest_state != tra_precommitted)
break;
}
if (--oldest > (ULONG) dbb->dbb_oldest_transaction)
dbb->dbb_oldest_transaction = oldest;
if (oldest_active > (ULONG) dbb->dbb_oldest_active)
dbb->dbb_oldest_active = oldest_active;
if (trans->tra_oldest_active > dbb->dbb_oldest_snapshot) {
dbb->dbb_oldest_snapshot = trans->tra_oldest_active;
#if defined(GARBAGE_THREAD)
if (!(dbb->dbb_flags & DBB_gc_active) &&
(dbb->dbb_flags & DBB_gc_background) )
{
dbb->dbb_flags |= DBB_gc_pending;
ISC_event_post(dbb->dbb_gc_event);
}
#endif
}
/* If the transaction block is getting out of hand, force a sweep */
if (dbb->dbb_sweep_interval &&
!(tdbb->getAttachment()->att_flags & ATT_no_cleanup) &&
(trans->tra_oldest_active - trans->tra_oldest >
dbb->dbb_sweep_interval) && oldest_state != tra_limbo)
{
#ifdef SWEEP_THREAD
// Why nobody checks the result? Changed the function to return nothing.
start_sweeper(tdbb, dbb);
#else
// force a sweep
TRA_sweep(tdbb, trans);
#endif
}
/* Check in with external file system */
EXT_trans_start(trans);
/* Start a 'transaction-level' savepoint, unless this is the
system transaction, or unless the transactions doesn't want
a savepoint to be started. This savepoint will be used to
undo the transaction if it rolls back. */
if ((trans != dbb->dbb_sys_trans) &&
!(trans->tra_flags & TRA_no_auto_undo))
{
VIO_start_save_point(tdbb, trans);
trans->tra_save_point->sav_flags |= SAV_trans_level;
}
/* Allocate the cancellation lock */
lock = FB_NEW_RPT(*trans->tra_pool, sizeof(SLONG)) Lock();
trans->tra_cancel_lock = lock;
lock->lck_type = LCK_cancel;
lock->lck_owner_handle = LCK_get_owner_handle(tdbb, lock->lck_type);
lock->lck_parent = dbb->dbb_lock;
lock->lck_length = sizeof(SLONG);
lock->lck_key.lck_long = trans->tra_number;
lock->lck_dbb = dbb;
lock->lck_ast = blocking_ast_transaction;
lock->lck_object = trans;
/* if the user asked us to restart all requests in this attachment,
do so now using the new transaction */
if (trans->tra_flags & TRA_restart_requests)
restart_requests(tdbb, trans);
/* If the transaction is read-only and read committed, it can be
precommitted because it can't modify any records and doesn't
need a snapshot preserved. This transaction type can run
forever without impacting garbage collection or causing
transaction bitmap growth. */
if (trans->tra_flags & TRA_readonly &&
trans->tra_flags & TRA_read_committed)
{
TRA_set_state(tdbb, trans, trans->tra_number, tra_committed);
LCK_release(tdbb, trans->tra_lock);
delete trans->tra_lock;
trans->tra_lock = NULL;
trans->tra_flags |= TRA_precommitted;
}
if (trans->tra_flags & TRA_precommitted)
TRA_precommited(tdbb, 0, trans->tra_number);
return trans;
}

View File

@ -24,16 +24,6 @@
#ifndef LOCK_LOCK_PROTO_H #ifndef LOCK_LOCK_PROTO_H
#define LOCK_LOCK_PROTO_H #define LOCK_LOCK_PROTO_H
// Lock owner types
// Placing it here helps avoid massive unneeded includes in lock/manager.cpp
enum lck_owner_t {
LCK_OWNER_process = 1, /* A process is the owner of the lock */
LCK_OWNER_database, /* A database is the owner of the lock */
LCK_OWNER_attachment, /* An atttachment is the owner of the lock */
LCK_OWNER_transaction /* A transaction is the owner of the lock */
};
bool LOCK_convert(SLONG, UCHAR, SSHORT, lock_ast_t, void*, bool LOCK_convert(SLONG, UCHAR, SSHORT, lock_ast_t, void*,
ISC_STATUS*); ISC_STATUS*);
int LOCK_deq(SLONG); int LOCK_deq(SLONG);