Skip to content

Commit a20f9bb

Browse files
authored
This should fix bug #8296 : Crash in TipCache::findStates (#8550)
* This should fix bug #8296 : Crash in TipCache::findStates * Correction
1 parent f7e5f55 commit a20f9bb

File tree

2 files changed

+56
-45
lines changed

2 files changed

+56
-45
lines changed

src/jrd/tpc.cpp

Lines changed: 51 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -205,14 +205,11 @@ CommitNumber TipCache::cacheState(TraNumber number)
205205
if (number < oldest)
206206
return CN_PREHISTORIC;
207207

208-
// It is possible to amortize barrier in getTransactionStatusBlock
209-
// over large number of operations, if our callers are made aware of
210-
// TransactionStatusBlock granularity and iterate over transactions
211-
// directly. But since this function is not really called too frequently,
212-
// it should not matter and we leave interface "as is" for now.
213208
TpcBlockNumber blockNumber = number / m_transactionsPerBlock;
214209
ULONG offset = number % m_transactionsPerBlock;
215-
TransactionStatusBlock* block = getTransactionStatusBlock(header, blockNumber);
210+
211+
Sync sync(&m_sync_status, FB_FUNCTION);
212+
TransactionStatusBlock* block = getTransactionStatusBlock(header, blockNumber, sync);
216213

217214
// This should not really happen ever
218215
fb_assert(block);
@@ -334,7 +331,9 @@ void TipCache::loadInventoryPages(thread_db* tdbb, GlobalTpcHeader* header)
334331

335332
TpcBlockNumber blockNumber = hdr_oldest_transaction / m_transactionsPerBlock;
336333
ULONG transOffset = hdr_oldest_transaction % m_transactionsPerBlock;
337-
TransactionStatusBlock* statusBlock = getTransactionStatusBlock(header, blockNumber);
334+
335+
SyncLockGuard sync(&m_sync_status, SYNC_EXCLUSIVE, FB_FUNCTION);
336+
TransactionStatusBlock* statusBlock = createTransactionStatusBlock(header->tpc_block_size, blockNumber);
338337

339338
for (TraNumber t = hdr_oldest_transaction; ; )
340339
{
@@ -353,7 +352,7 @@ void TipCache::loadInventoryPages(thread_db* tdbb, GlobalTpcHeader* header)
353352
{
354353
blockNumber++;
355354
transOffset = 0;
356-
statusBlock = getTransactionStatusBlock(header, blockNumber);
355+
statusBlock = createTransactionStatusBlock(header->tpc_block_size, blockNumber);
357356
}
358357
}
359358
}
@@ -364,7 +363,10 @@ void TipCache::mapInventoryPages(GlobalTpcHeader* header)
364363
const TpcBlockNumber lastNumber = header->latest_transaction_id / m_transactionsPerBlock;
365364

366365
for (; blockNumber <= lastNumber; blockNumber++)
367-
getTransactionStatusBlock(header, blockNumber);
366+
{
367+
Sync sync(&m_sync_status, FB_FUNCTION);
368+
getTransactionStatusBlock(header, blockNumber, sync);
369+
}
368370
}
369371

370372
TipCache::StatusBlockData::StatusBlockData(thread_db* tdbb, TipCache* tipCache, ULONG blockSize, TpcBlockNumber blkNumber)
@@ -498,20 +500,24 @@ TipCache::TransactionStatusBlock* TipCache::createTransactionStatusBlock(ULONG b
498500
return blockData->memory->getHeader();
499501
}
500502

501-
TipCache::TransactionStatusBlock* TipCache::getTransactionStatusBlock(GlobalTpcHeader* header, TpcBlockNumber blockNumber)
503+
TipCache::TransactionStatusBlock* TipCache::getTransactionStatusBlock(GlobalTpcHeader* header, TpcBlockNumber blockNumber, Sync& sync)
502504
{
505+
fb_assert(sync.getState() == SYNC_NONE);
506+
503507
// This is a double-checked locking pattern. SyncLockGuard uses atomic ops internally and should be cheap
504508
TransactionStatusBlock* block = NULL;
505509
{
506-
SyncLockGuard sync(&m_sync_status, SYNC_SHARED, "TipCache::getTransactionStatusBlock");
510+
sync.lock(SYNC_SHARED);
507511
BlocksMemoryMap::ConstAccessor acc(&m_blocks_memory);
508512
if (acc.locate(blockNumber))
509513
block = acc.current()->memory->getHeader();
514+
else
515+
sync.unlock();
510516
}
511517

512518
if (!block)
513519
{
514-
SyncLockGuard sync(&m_sync_status, SYNC_EXCLUSIVE, "TipCache::getTransactionStatusBlock");
520+
sync.lock(SYNC_EXCLUSIVE);
515521
BlocksMemoryMap::ConstAccessor acc(&m_blocks_memory);
516522
if (acc.locate(blockNumber))
517523
block = acc.current()->memory->getHeader();
@@ -521,6 +527,8 @@ TipCache::TransactionStatusBlock* TipCache::getTransactionStatusBlock(GlobalTpcH
521527
TraNumber oldest = header->oldest_transaction.load(std::memory_order_relaxed);
522528
if (blockNumber >= oldest / m_transactionsPerBlock)
523529
block = createTransactionStatusBlock(header->tpc_block_size, blockNumber);
530+
else
531+
sync.unlock();
524532
}
525533
}
526534
return block;
@@ -532,24 +540,36 @@ TraNumber TipCache::findStates(TraNumber minNumber, TraNumber maxNumber, ULONG m
532540
fb_assert(m_tpcHeader);
533541
GlobalTpcHeader* header = m_tpcHeader->getHeader();
534542

535-
TransactionStatusBlock* statusBlock;
536-
TpcBlockNumber blockNumber;
537-
ULONG transOffset;
543+
TransactionStatusBlock* statusBlock = nullptr;
544+
ULONG transOffset = 0;
538545

539-
do
546+
Sync sync(&m_sync_status, FB_FUNCTION);
547+
for (TraNumber tran = minNumber; ; tran++, transOffset++)
540548
{
541-
TraNumber oldest = header->oldest_transaction.load(std::memory_order_relaxed);
549+
if (transOffset == m_transactionsPerBlock)
550+
{
551+
sync.unlock();
552+
statusBlock = nullptr;
553+
}
554+
555+
while (!statusBlock)
556+
{
557+
const TraNumber oldest = header->oldest_transaction.load(std::memory_order_relaxed);
542558

543-
if (minNumber < oldest)
544-
minNumber = oldest;
559+
if (tran < oldest)
560+
tran = oldest;
545561

546-
blockNumber = minNumber / m_transactionsPerBlock;
547-
transOffset = minNumber % m_transactionsPerBlock;
548-
statusBlock = getTransactionStatusBlock(header, blockNumber);
549-
} while (!statusBlock);
562+
const TpcBlockNumber blockNumber = tran / m_transactionsPerBlock;
563+
transOffset = tran % m_transactionsPerBlock;
564+
statusBlock = getTransactionStatusBlock(header, blockNumber, sync);
565+
566+
if (sync.getState() == SYNC_EXCLUSIVE)
567+
sync.downgrade(SYNC_SHARED);
568+
}
569+
570+
if (tran >= maxNumber)
571+
break;
550572

551-
for (TraNumber t = minNumber; ; )
552-
{
553573
// Barrier is not needed here. Slightly out-dated information shall be ok here.
554574
// Such transaction shall already be considered active by our caller.
555575
// TODO: check if this assumption is indeed correct.
@@ -577,18 +597,8 @@ TraNumber TipCache::findStates(TraNumber minNumber, TraNumber maxNumber, ULONG m
577597
break;
578598
}
579599

580-
if (((1 << state) & mask) != 0)
581-
return t;
582-
583-
if (++t >= maxNumber)
584-
break;
585-
586-
if (++transOffset == m_transactionsPerBlock)
587-
{
588-
blockNumber++;
589-
transOffset = 0;
590-
statusBlock = getTransactionStatusBlock(header, blockNumber);
591-
}
600+
if (((1UL << state) & mask) != 0)
601+
return tran;
592602
}
593603

594604
return 0;
@@ -600,13 +610,11 @@ CommitNumber TipCache::setState(TraNumber number, int state)
600610
fb_assert(m_tpcHeader);
601611
GlobalTpcHeader* header = m_tpcHeader->getHeader();
602612

603-
// over large number of operations, if our callers are made aware of
604-
// TransactionStatusBlock granularity and iterate over transactions
605-
// directly. But since this function is not really called too frequently,
606-
// it should not matter and we leave interface "as is" for now.
607613
TpcBlockNumber blockNumber = number / m_transactionsPerBlock;
608614
ULONG offset = number % m_transactionsPerBlock;
609-
TransactionStatusBlock* block = getTransactionStatusBlock(header, blockNumber);
615+
616+
Sync sync(&m_sync_status, FB_FUNCTION);
617+
TransactionStatusBlock* block = getTransactionStatusBlock(header, blockNumber, sync);
610618

611619
// This should not really happen
612620
if (!block)
@@ -799,7 +807,7 @@ void TipCache::releaseSharedMemory(thread_db* tdbb, TraNumber oldest_old, TraNum
799807
if (blocksToCleanup.isEmpty())
800808
return;
801809

802-
SyncLockGuard sync(&m_sync_status, SYNC_EXCLUSIVE, "TipCache::releaseSharedMemory");
810+
SyncLockGuard sync(&m_sync_status, SYNC_EXCLUSIVE, FB_FUNCTION);
803811
while (blocksToCleanup.hasData())
804812
{
805813
TpcBlockNumber blockNumber = blocksToCleanup.pop();

src/jrd/tpc_proto.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -311,9 +311,12 @@ class TipCache
311311

312312
// Returns block holding transaction state.
313313
// Returns NULL if requested block is too old and is no longer cached.
314-
TransactionStatusBlock* getTransactionStatusBlock(GlobalTpcHeader* header, TpcBlockNumber blockNumber);
314+
// Sync should be bound to m_sync_status. On enter, sync must be unlocked.
315+
// If returns not NULL then sync remains locked.
316+
TransactionStatusBlock* getTransactionStatusBlock(GlobalTpcHeader* header, TpcBlockNumber blockNumber, Firebird::Sync& sync);
315317

316-
// Map shared memory for a block
318+
// Map shared memory for a block.
319+
// Assume exclusive lock of m_sync_status.
317320
TransactionStatusBlock* createTransactionStatusBlock(ULONG blockSize, TpcBlockNumber blockNumber);
318321

319322
// Release shared memory blocks, if possible.

0 commit comments

Comments
 (0)