@@ -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
370372TipCache::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 ();
0 commit comments