8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-24 06:03:02 +01:00
firebird-mirror/src/jrd/rlck.cpp
robocop 5c3c8abd9a Big cleanup.
God have pity on platform maintainers.
I only can compile/test Win32; sorry, folks.
2004-02-20 06:43:27 +00:00

1008 lines
27 KiB
C++

/*
* PROGRAM: JRD Access Method
* MODULE: rlck.cpp
* DESCRIPTION: Record and Relation Lock Manager
*
* The contents of this file are subject to the Interbase Public
* License Version 1.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy
* of the License at http://www.Inprise.com/IPL.html
*
* Software distributed under the License is distributed on an
* "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
* or implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code was created by Inprise Corporation
* and its predecessors. Portions created by Inprise Corporation are
* Copyright (C) Inprise Corporation.
*
* All Rights Reserved.
* Contributor(s): ______________________________________.
* 2001.07.06 Sean Leyne - Code Cleanup, removed "#ifdef READONLY_DATABASE"
* conditionals, as the engine now fully supports
* readonly databases.
*/
#include "firebird.h"
#include "../jrd/jrd.h"
#include "../jrd/tra.h"
#include "../jrd/lck.h"
#include "../jrd/req.h"
#include "gen/iberror.h"
#include "../jrd/all.h"
#include "../jrd/all_proto.h"
#include "../jrd/err_proto.h"
#include "../jrd/isc_proto.h"
#include "../jrd/lck_proto.h"
#include "../jrd/rlck_proto.h"
#include "../jrd/thd_proto.h"
#include "../jrd/vio_proto.h"
#ifdef PC_ENGINE
static lck* allocate_record_lock(jrd_tra*, RPB *);
#endif
static lck* allocate_relation_lock(MemoryPool*, jrd_rel*);
#ifdef PC_ENGINE
static lck* attachment_relation_lock(jrd_rel*);
static void drop_record_lock(lck*);
static lck* find_record_lock(RPB *);
static BOOLEAN obtain_lock(jrd_tra*, lck*, USHORT);
static void start_record_locking(jrd_rel*);
#endif
#ifdef PC_ENGINE
lck* RLCK_lock_record(RPB * rpb,
USHORT lock_level, int (*ast) (BLK), BLK ast_arg)
{
/**************************************
*
* R L C K _ l o c k _ r e c o r d
*
**************************************
*
* Functional description
* Lock the current record in the provided
* record parameter block at the specified level.
*
**************************************/
// SLONG process_count;
/* first get a record lock on the desired record;
if we can't get one then there is no point
in signalling that this process needs to acquire locks */
lck* lock = RLCK_lock_record_implicit(0, rpb, lock_level, ast, ast_arg);
if (!lock)
return NULL;
TDBB tdbb = GET_THREAD_DATA;
/*
if the record is trying to be locked, check
whether the record has already been updated on page
by a transaction whose updates we cannot see yet--
in which case we effectively cannot get the lock */
if (VIO_check_if_updated(tdbb, rpb)) {
RLCK_unlock_record_implicit(lock, rpb);
return NULL;
}
/* if this process has no record locks outstanding,
indicate through the lock manager that we are
starting the record locking protocol */
jrd_rel* relation = rpb->rpb_relation;
if (!relation->rel_explicit_locks) {
lck* record_locking = RLCK_record_locking(relation);
/* get a shared write lock to be compatible with other processes
that are doing record locking, but incompatible with those who aren't */
if (record_locking->lck_logical == LCK_none)
LCK_lock_non_blocking(tdbb, record_locking, LCK_SW, TRUE);
else
LCK_convert_non_blocking(tdbb, tdbb, record_locking, LCK_SW,
TRUE);
}
relation->rel_explicit_locks++;
return lock;
}
#endif
#ifdef PC_ENGINE
lck* RLCK_lock_record_implicit(jrd_tra* transaction,
RPB * rpb,
USHORT lock_level,
int (*ast) (BLK), BLK ast_arg)
{
/**************************************
*
* R L C K _ l o c k _ r e c o r d _ i m p l i c i t
*
**************************************
*
* Functional description
* Lock the given record for implicit access, a subset
* of what we need to do to lock it explicitly.
*
* transaction is NULL for explicit record locking.
*
**************************************/
USHORT interest_lock_level = 0;
jrd_rel* relation;
lck* lock = allocate_record_lock(transaction, rpb);
lock->lck_ast = ast;
lock->lck_object = ast_arg;
/* To ensure that relation and record locks respect each other,
utilize a multigranularity locking scheme, establishing
an interest lock level in the parent relation according to
the following scheme:
read lock on record := protected read
(implies) interest read lock on relation := shared read
write lock on record := exclusive
(implies) interest write lock on relation := shared write
Then as long as PR and EX locks are used to implement read
and write locking on relations, the relation locks will
be respected.
This also guarantees that implicit record locks won't result
in interest locks being taken out, since implicit record locks
are SW. Relations are already implicitly reserved in
RLCK_reserve_relation(), so there is no need for an additional
interest lock.
implicit read lock on record := NO LOCK
(implies) NO interest read lock on relation
implicit write lock on record := shared write
(implies) interest write lock on relation := shared write
*/
relation = rpb->rpb_relation;
if (lock_level == LCK_EX) {
interest_lock_level = LCK_SW;
relation->rel_write_locks++;
relation->rel_lock_total++;
}
else if (lock_level == LCK_PR) {
interest_lock_level = LCK_SR;
relation->rel_read_locks++;
relation->rel_lock_total++;
}
/* first attempt to get the parent lock then get the record lock,
using two-phase locking to help prevent deadlocks */
if (interest_lock_level
&& !obtain_lock(transaction, lock->lck_parent, interest_lock_level)
|| !obtain_lock(transaction, lock, lock_level))
{
RLCK_unlock_record_implicit(lock, rpb);
return NULL;
}
return lock;
}
#endif
#ifdef PC_ENGINE
lck* RLCK_lock_relation(jrd_rel* relation,
USHORT lock_level, int (*ast) (BLK), BLK ast_arg)
{
/**************************************
*
* R L C K _ l o c k _ r e l a t i o n
*
**************************************
*
* Functional description
* Lock a relation at the specified level.
*
**************************************/
/* allocate a relation lock hanging off the attachment
block, then keep a count of the number of times it
is used so that we know when to release it (bug #7478) */
lck* lock = attachment_relation_lock(relation);
lock->lck_count++;
lock->lck_ast = ast;
lock->lck_object = ast_arg;
if (!obtain_lock(0, lock, lock_level))
ERR_post(isc_relation_lock, isc_arg_string, relation->rel_name, 0);
return lock;
}
#endif
#ifdef PC_ENGINE
lck* RLCK_range_relation(jrd_tra* transaction,
jrd_rel* relation, int (*ast) (BLK), BLK ast_arg)
{
/**************************************
*
* R L C K _ r a n g e _ r e l a t i o n
*
**************************************
*
* Functional description
* Lock a relation for a refresh range.
*
**************************************/
USHORT level, wait;
USHORT result;
TDBB tdbb = GET_THREAD_DATA;
att* attachment = tdbb->tdbb_attachment;
if (transaction->tra_flags & TRA_system)
return NULL;
lck* lock = allocate_relation_lock(transaction->tra_pool, relation);
lock->lck_owner = (BLK) attachment;
lock->lck_ast = ast;
lock->lck_object = ast_arg;
lock->lck_type = LCK_range_relation;
level = LCK_PR;
wait = (transaction->tra_flags & TRA_nowait) ? FALSE : TRUE;
/* get lock */
result = LCK_lock_non_blocking(tdbb, lock, level, wait);
if (result)
return lock;
else
return NULL;
}
#endif
#ifdef PC_ENGINE
lck* RLCK_record_locking(jrd_rel* relation)
{
/**************************************
*
* R L C K _ r e c o r d _ l o c k i n g
*
**************************************
*
* Functional description
* Allocate a record locking lock, which determines
* when record locking is necessary for a particular
* relation.
*
**************************************/
if (relation->rel_record_locking)
return relation->rel_record_locking;
TDBB tdbb = GET_THREAD_DATA;
DBB dbb = GET_DBB;
lck* lock = FB_NEW_RPT(*dbb->dbb_permanent, sizeof(SLONG)) lck();
lock->lck_parent = dbb->dbb_lock;
lock->lck_dbb = dbb;
lock->lck_attachment = tdbb->tdbb_attachment;
lock->lck_type = LCK_record_locking;
lock->lck_owner_handle = LCK_get_owner_handle(tdbb, lock->lck_type);
/* make the lock specific to the relation */
lock->lck_length = sizeof(SLONG);
lock->lck_key.lck_long = relation->rel_id;
relation->rel_record_locking = lock;
/* set up an ast to start record locking if
someone gets an incompatible lock */
lock->lck_ast = start_record_locking;
lock->lck_object = reinterpret_cast<blk*>(relation);
/* now attempt to get a PR on the lock to detect when
anyone locks a record explicitly */
LCK_lock(tdbb, _non_blocking(lock, LCK_PR, FALSE));
return lock;
}
#endif
#ifdef PC_ENGINE
void RLCK_release_lock(lck* lock)
{
/**************************************
*
* R L C K _ r e l e a s e _ l o c k
*
**************************************
*
* Functional description
* Release a lock of record or relation
* type. The lock handle came from the
* user, so validate it carefully.
*
**************************************/
/* first do basic validation of the handle */
if (!lock)
ERR_post(isc_bad_lock_handle, 0);
if (((BLK) lock)->blk_type != (UCHAR) type_lck)
ERR_post(isc_bad_lock_handle, 0);
/* now use the lock type to determine the type
of lock to release */
if (lock->lck_type == LCK_relation)
RLCK_unlock_relation(0, (jrd_rel*) lock->lck_object);
else if (lock->lck_type == LCK_record)
RLCK_unlock_record(lock, 0);
else
ERR_post(isc_bad_lock_handle, 0);
}
#endif
#ifdef PC_ENGINE
void RLCK_release_locks(ATT attachment)
{
/**************************************
*
* R L C K _ r e l e a s e _ l o c k s
*
**************************************
*
* Functional description
* Release all relation and record locks
* explicitly taken out during this attachment.
*
**************************************/
VEC vector;
vec::iterator *lptr, *lend;
/* unlock all explicit relation locks */
if (vector = attachment->att_relation_locks) {
for (lptr = vector->begin(), lend = vector->end(); lptr < lend; lptr++) {
if (lock = ((lck*)(*lptr)) )
RLCK_unlock_relation(0, (jrd_rel*) lock->lck_object);
}
}
/* unlock all explicit record locks */
lck* lock;
while (lock = attachment->att_record_locks) {
RLCK_unlock_record(lock, 0);
}
/* clear the vector of user locks */
if (vector = attachment->att_lck_quick_ref) {
for (lptr = vector->begin(), lend = vector->end(); lptr < lend; lptr++) {
*lptr = NULL;
}
}
}
#endif
lck* RLCK_reserve_relation(TDBB tdbb,
jrd_tra* transaction,
jrd_rel* relation, USHORT write_flag, USHORT error_flag)
{
/**************************************
*
* R L C K _ r e s e r v e _ r e l a t i o n
*
**************************************
*
* Functional description
* Lock a relation within a transaction. If the relation
* is already locked at a lower level, upgrade the lock.
*
**************************************/
USHORT level, wait;
if (transaction->tra_flags & TRA_system)
return NULL;
if (write_flag && (tdbb->tdbb_database->dbb_flags & DBB_read_only))
ERR_post(isc_read_only_database, 0);
if (write_flag && (transaction->tra_flags & TRA_readonly))
ERR_post(isc_read_only_trans, 0);
lck* lock = RLCK_transaction_relation_lock(transaction, relation);
/* Next, figure out what kind of lock we need */
if (transaction->tra_flags & TRA_degree3) {
if (write_flag)
level = LCK_EX;
else
level = LCK_PR;
}
else {
if (write_flag)
level = LCK_SW;
else
level = LCK_none;
}
/* If the lock is already "good enough", we're done */
if (level <= lock->lck_logical)
return lock;
if (transaction->tra_flags & TRA_reserving)
ERR_post(isc_unres_rel, isc_arg_string, relation->rel_name, 0);
wait = (transaction->tra_flags & TRA_nowait) ? FALSE : TRUE;
/* get lock */
USHORT result;
if (lock->lck_logical)
result = LCK_convert_non_blocking(NULL, lock, level, wait);
else
result = LCK_lock_non_blocking(NULL, lock, level, wait);
if (result)
return lock;
else {
if (error_flag)
ERR_post((wait) ? isc_deadlock : isc_lock_conflict, 0);
return NULL;
}
}
void RLCK_shutdown_attachment(ATT attachment)
{
/**************************************
*
* R L C K _ s h u t d o w n _ a t t a c h m e n t
*
**************************************
*
* Functional description
* Shutdown the attachment's persistent record
* and relation locks. This runs at AST level.
*
**************************************/
TDBB tdbb = GET_THREAD_DATA;
/* Release child record locks before parent relation locks */
for (lck* record_lock = attachment->att_record_locks;
record_lock;
record_lock = record_lock->lck_att_next)
{
LCK_release(tdbb, record_lock);
}
VEC lock_vector = attachment->att_relation_locks;
if (lock_vector) {
for (vec::iterator lock = lock_vector->begin();
lock != lock_vector->end(); ++lock)
{
if (*lock) {
LCK_release(tdbb, (lck*)(*lock));
}
}
}
}
void RLCK_shutdown_database(DBB dbb)
{
/**************************************
*
* R L C K _ s h u t d o w n _ d a t a b a s e
*
**************************************
*
* Functional description
* Shutdown the database locks shared for relation
* interest and record locking locks. This can be called
* at AST level.
*
**************************************/
VEC vector;
TDBB tdbb = GET_THREAD_DATA;
if (!(vector = dbb->dbb_relations))
return;
vec::iterator ptr, end;
for (ptr = vector->begin(), end = vector->end(); ptr < end; ptr++)
{
jrd_rel* relation;
if ( (relation = ((jrd_rel*)(*ptr)) ) ) {
if (relation->rel_record_locking)
LCK_release(tdbb, relation->rel_record_locking);
if (relation->rel_interest_lock)
LCK_release(tdbb, relation->rel_interest_lock);
relation->rel_explicit_locks = 0;
relation->rel_read_locks = 0;
relation->rel_write_locks = 0;
relation->rel_lock_total = 0;
}
}
}
#ifdef PC_ENGINE
void RLCK_signal_refresh(jrd_tra* transaction)
{
/**************************************
*
* R L C K _ s i g n a l _ r e f r e s h
*
**************************************
*
* Functional description
* For each reserved relation, get a refresh relation
* lock to signal possible refresh range users.
*
**************************************/
USHORT i;
jrd_rel* relation;
TDBB tdbb = GET_THREAD_DATA;
DBB dbb = tdbb->tdbb_database;
/* for each relation, take out a range relation lock and then release it */
vec* vector = transaction->tra_relation_locks;
if (vector) {
/* allocate a local lock */
lck* local_lock = FB_NEW_RPT(*dbb->dbb_permanent, sizeof(SLONG)) lck();
local_lock->lck_dbb = dbb;
local_lock->lck_attachment = tdbb->tdbb_attachment;
local_lock->lck_length = sizeof(SLONG);
local_lock->lck_type = LCK_range_relation;
local_lock->lck_owner_handle =
LCK_get_owner_handle(tdbb, local_lock->lck_type);
local_lock->lck_parent = dbb->dbb_lock;
local_lock->lck_compatible = (BLK) tdbb->tdbb_attachment;
for (i = 0; i < vector->count(); i++) {
lck* lock = (lck**) ((*vector)[i]);
if (lock) {
relation = (jrd_rel*) lock->lck_object;
local_lock->lck_key.lck_long = relation->rel_id;
local_lock->lck_object = reinterpret_cast<blk*>(relation);
LCK_lock_non_blocking(tdbb, local_lock, LCK_SW, 0);
LCK_release(tdbb, local_lock);
}
}
ALL_release(local_lock);
}
}
#endif
lck* RLCK_transaction_relation_lock(jrd_tra* transaction, jrd_rel* relation)
{
/**************************************
*
* R L C K _ t r a n s a c t i o n _ r e l a t i o n _ l o c k
*
**************************************
*
* Functional description
* Take out a relation lock within the context of
* a transaction.
*
**************************************/
lck* lock;
VEC vector;
if ((vector = transaction->tra_relation_locks) &&
(relation->rel_id < vector->count()) &&
(lock = (lck*) (*vector)[relation->rel_id]))
{
return lock;
}
vector = transaction->tra_relation_locks =
vec::newVector(*transaction->tra_pool, transaction->tra_relation_locks,
relation->rel_id + 1);
if ( (lock = (lck*) (*vector)[relation->rel_id]) )
return lock;
lock = allocate_relation_lock(transaction->tra_pool, relation);
lock->lck_owner = (BLK) transaction;
/* for relations locked within a transaction, add a second level of
compatibility within the intra-process lock manager which specifies
that relation locks are incompatible with locks taken out by other
transactions, if a transaction is specified */
lock->lck_compatible2 = (BLK) transaction;
(*vector)[relation->rel_id] = (BLK) lock;
return lock;
}
#ifdef PC_ENGINE
void RLCK_unlock_record(lck* lock, RPB * rpb)
{
/**************************************
*
* R L C K _ u n l o c k _ r e c o r d
*
**************************************
*
* Functional description
* Unlock the specified record lock, or if
* it's not available use the current record
* in the specified record parameter block.
*
**************************************/
jrd_rel* relation;
// SLONG process_count;
if (rpb)
relation = rpb->rpb_relation;
else if (lock)
relation = (jrd_rel*) lock->lck_parent->lck_object;
else
relation = NULL; /* theoretically impossible */
RLCK_unlock_record_implicit(lock, rpb);
TDBB tdbb = GET_THREAD_DATA;
att* attachment = tdbb->tdbb_attachment;
if (attachment->att_flags & ATT_shutdown)
return;
/* decrement the count of explicit locks this process has taken out;
if there are none, go back to a PR on the lock--
if we cannot obtain this, it means someone else has explicit
record locks taken out and we should just release the lock */
if (relation && !--relation->rel_explicit_locks) {
lck* record_locking = relation->rel_record_locking;
if (!LCK_convert_non_blocking(tdbb, record_locking, LCK_PR, FALSE))
LCK_release(tdbb, record_locking);
}
}
#endif
#ifdef PC_ENGINE
void RLCK_unlock_record_implicit(lck* lock, RPB * rpb)
{
/**************************************
*
* R L C K _ u n l o c k _ r e c o r d _ i m p l i c i t
*
**************************************
*
* Functional description
* Unlock a record-level lock.
*
**************************************/
TDBB tdbb = GET_THREAD_DATA;
if (!lock)
lock = find_record_lock(rpb);
const USHORT lock_level = lock->lck_logical;
drop_record_lock(lock);
LCK_release(tdbb, lock);
att* attachment = tdbb->tdbb_attachment;
if (attachment->att_flags & ATT_shutdown) {
ALL_release(lock);
return;
}
/* handle the release half of the multigranularity locking scheme:
if there are no more write locks, downgrade the interest
lock on the parent relation; if there are no more read or
write locks, release the lock entirely
For implicit locks, there are no relation intrest locks.
So do nothing.
*/
jrd_rel* relation = (jrd_rel*) lock->lck_parent->lck_object;
if (lock_level == LCK_EX) {
if (!--relation->rel_write_locks)
if (!relation->rel_read_locks)
LCK_release(tdbb, relation->rel_interest_lock);
else
LCK_convert_non_blocking(tdbb, relation->rel_interest_lock,
LCK_SR, TRUE);
}
else if (lock_level == LCK_PR) {
if (!--relation->rel_read_locks && !relation->rel_write_locks)
LCK_release(tdbb, relation->rel_interest_lock);
}
ALL_release(lock);
}
#endif
#ifdef PC_ENGINE
void RLCK_unlock_relation(lck* lock, jrd_rel* relation)
{
/**************************************
*
* R L C K _ u n l o c k _ r e l a t i o n
*
**************************************
*
* Functional description
* Release the attachment's persistent lock
* on the specified relation.
*
**************************************/
VEC vector;
USHORT id;
TDBB tdbb = GET_THREAD_DATA;
att* attachment = tdbb->tdbb_attachment;
if (!(vector = attachment->att_relation_locks))
return;
if (relation) {
id = relation->rel_id;
if (id >= vector->count())
return;
lock = (lck*) (*vector)[id];
}
else
for (id = 0; id < vector->count(); id++)
if (lock == (lck*) (*vector)[id])
break;
if (!lock)
return;
/* decrement the use count; if it goes to zero,
there are no further locks taken out in this
attachment so we can release the lock (bug #7478) */
if (lock->lck_count > 1) {
lock->lck_count--;
return;
}
LCK_release(tdbb, lock);
ALL_release(lock);
(*vector)[id] = NULL;
}
#endif
#ifdef PC_ENGINE
static lck* allocate_record_lock(jrd_tra* transaction, RPB * rpb)
{
/**************************************
*
* a l l o c a t e _ r e c o r d _ l o c k
*
**************************************
*
* Functional description
* Create a record lock block for the current
* record in the passed rpb.
* transaction used only for implicit record locks.
*
**************************************/
TDBB tdbb = GET_THREAD_DATA;
DBB dbb = tdbb->tdbb_database;
att* attachment = tdbb->tdbb_attachment;
if (!rpb->rpb_record)
ERR_post(isc_no_cur_rec, 0);
/* allocate a lock block for the record lock */
lck* lock = FB_NEW_RPT(*dbb->dbb_permanent, sizeof(SLONG)) lck();
lock->lck_dbb = dbb;
lock->lck_attachment = attachment;
lock->lck_object = reinterpret_cast<blk*>(dbb);
lock->lck_type = LCK_record;
lock->lck_owner_handle = LCK_get_owner_handle(tdbb, lock->lck_type);
/* use the relation lock as the lock parent */
jrd_rel* relation = rpb->rpb_relation;
if (transaction)
lock->lck_parent =
RLCK_transaction_relation_lock(transaction, relation);
else {
if (!relation->rel_interest_lock)
relation->rel_interest_lock =
allocate_relation_lock(dbb->dbb_permanent, relation);
lock->lck_parent = relation->rel_interest_lock;
}
/* indicate that this lock should be compatible at the attachment
level--meaning that two record locks taken out within the same
attachment should always be compatible; ditto for the interest lock */
lock->lck_compatible = (BLK) attachment;
lock->lck_parent->lck_compatible = (BLK) attachment;
/* link in the record lock with the other record locks
taken out by this attachment */
lock->lck_att_next = attachment->att_record_locks;
attachment->att_record_locks = lock;
/* fill in the lock value using the record number */
lock->lck_length = sizeof(SLONG);
lock->lck_key.lck_long = rpb->rpb_number;
return lock;
}
#endif
static lck* allocate_relation_lock(MemoryPool* pool, jrd_rel* relation)
{
/**************************************
*
* a l l o c a t e _ r e l a t i o n _ l o c k
*
**************************************
*
* Functional description
* Allocate a lock block for a relation lock.
*
**************************************/
TDBB tdbb = GET_THREAD_DATA;
DBB dbb = tdbb->tdbb_database;
lck* lock = FB_NEW_RPT(*pool, sizeof(SLONG)) lck();
lock->lck_dbb = dbb;
lock->lck_attachment = tdbb->tdbb_attachment;
lock->lck_length = sizeof(SLONG);
lock->lck_key.lck_long = relation->rel_id;
lock->lck_type = LCK_relation;
lock->lck_owner_handle = LCK_get_owner_handle(tdbb, lock->lck_type);
lock->lck_parent = dbb->dbb_lock;
/* enter all relation locks into the intra-process lock manager and treat
them as compatible within the attachment according to IPLM rules */
lock->lck_compatible = (BLK) tdbb->tdbb_attachment;
/* the lck_object is used here to find the relation
block from the lock block */
lock->lck_object = reinterpret_cast<blk*>(relation);
return lock;
}
#ifdef PC_ENGINE
static lck* attachment_relation_lock(jrd_rel* relation)
{
/**************************************
*
* a t t a c h m e n t _ r e l a t i o n _ l o c k
*
**************************************
*
* Functional description
* Take out a persistent relation lock at the
* attachment level.
*
**************************************/
lck* lock;
VEC vector;
TDBB tdbb = GET_THREAD_DATA;
DBB dbb = tdbb->tdbb_database;
att* attachment = tdbb->tdbb_attachment;
if ((vector = attachment->att_relation_locks) &&
(relation->rel_id < vector->count()) &&
(lock = (lck*) (*vector)[relation->rel_id]))
return lock;
vector = attachment->att_relation_locks =
vec::newVector(*dbb->dbb_permanent, attachment->att_relation_locks,
relation->rel_id + 1);
if ( (lock = (lck*) (*vector)[relation->rel_id]) )
return lock;
lock = allocate_relation_lock(dbb->dbb_permanent, relation);
lock->lck_owner = (BLK) attachment;
(*vector)[relation->rel_id] = (BLK) lock;
return lock;
}
#endif
#ifdef PC_ENGINE
static void drop_record_lock(lck* record_lock)
{
/**************************************
*
* d r o p _ r e c o r d _ l o c k
*
**************************************
*
* Functional description
* Find the record lock in the linked list off
* the attachment block, and drop it from the list.
*
**************************************/
TDBB tdbb = GET_THREAD_DATA;
/* look through all the record locks taken out by this attachment
looking for one with the same record number and relation id */
att* attachment = tdbb->tdbb_attachment;
for (lck** lock = &attachment->att_record_locks; *lock;
lock = &(*lock)->lck_att_next)
{
if (*lock == record_lock) {
*lock = (*lock)->lck_att_next;
break;
}
}
}
#endif
#ifdef PC_ENGINE
static lck* find_record_lock(RPB * rpb)
{
/**************************************
*
* f i n d _ r e c o r d _ l o c k
*
**************************************
*
* Functional description
* Find the record lock previously
* defined for a record.
*
**************************************/
TDBB tdbb = GET_THREAD_DATA;
/* look through all the record locks taken out by this attachment
looking for one with the same record number and relation */
att* attachment = tdbb->tdbb_attachment;
lck* lock;
for (lock = attachment->att_record_locks; lock; lock = lock->lck_att_next)
{
if ((rpb->rpb_number == lock->lck_key.lck_long)
&& (rpb->rpb_relation == (jrd_rel*) lock->lck_parent->lck_object))
{
break;
}
}
return lock;
}
#endif
#ifdef PC_ENGINE
static BOOLEAN obtain_lock(jrd_tra* transaction, lck* lock, USHORT lock_level)
{
/**************************************
*
* o b t a i n _ l o c k
*
**************************************
*
* Functional description
* Obtain the specified lock at the
* necessary level.
*
**************************************/
USHORT wait_flag;
if (transaction)
wait_flag = (transaction->tra_flags & TRA_nowait) ? FALSE : TRUE;
else
wait_flag = FALSE;
/* return if lock level OK and if the lock has not been released
(like as part of a refresh range) */
if ((lock_level <= lock->lck_logical) && (lock->lck_id != -1))
return TRUE;
if ((lock->lck_logical) && (lock->lck_id != -1)) {
if (LCK_convert_non_blocking(NULL, lock, lock_level, wait_flag))
return TRUE;
}
else if (LCK_lock_non_blocking(NULL, lock, lock_level, wait_flag))
return TRUE;
return FALSE;
}
#endif
#ifdef PC_ENGINE
static void start_record_locking(jrd_rel* relation)
{
/**************************************
*
* s t a r t _ r e c o r d _ l o c k i n g
*
**************************************
*
* Functional description
* A blocking AST has been issued to give up
* the lock on the record locking semaphore.
* Flag the fact that record locking is now
* necessary for reading and writing records.
*
**************************************/
lck* record_locking = relation->rel_record_locking;
/* if we have shared write, it means we have records
locked; we won't give up this lock for anyone! */
if (record_locking->lck_physical == LCK_SW)
return;
ISC_ast_enter();
LCK_release(NULL, record_locking);
ISC_ast_exit();
}
#endif