void BlockchainScanner::scan(uint32_t scanFrom)
{
   startAt_ = scanFrom;

   auto& topBlock = blockchain_->top();
   StoredDBInfo subsshSdbi;

   //write address merkle in SSH sdbi
   {
      auto&& addrMerkle = scrAddrFilter_->getAddressMapMerkle();

      StoredDBInfo sshSdbi;
      LMDBEnv::Transaction historytx;
      db_->beginDBTransaction(&historytx, SSH, LMDB::ReadWrite);
      
      db_->getStoredDBInfo(SSH, sshSdbi);
      sshSdbi.metaHash_ = addrMerkle;
      db_->putStoredDBInfo(SSH, sshSdbi);
   }

   db_->getStoredDBInfo(SUBSSH, subsshSdbi);
   BlockHeader* sdbiblock = nullptr;

   //check if we need to scan anything
   try
   {
      sdbiblock = 
         &blockchain_->getHeaderByHash(subsshSdbi.topScannedBlkHash_);
   }
   catch (...)
   {
      sdbiblock = &blockchain_->getHeaderByHeight(0);
   }

   if (sdbiblock->isMainBranch())
   {
      if (sdbiblock->getBlockHeight() > scanFrom)
         scanFrom = sdbiblock->getBlockHeight();

      if (scanFrom != 0 && 
          scanFrom > topBlock.getBlockHeight())
      {
         LOGINFO << "no history to scan";
         topScannedBlockHash_ = topBlock.getThisHash();
         return;
      }
   }

   preloadUtxos();
   shared_ptr<BatchLink> batchLinkPtr;

   //lambdas
   auto scanBlockDataLambda = [&](shared_ptr<BlockDataBatch> batch)
   { scanBlockData(batch); };

   auto writeBlockDataLambda = [&](void)
   { writeBlockData(batchLinkPtr); };

   auto startHeight = scanFrom;
   unsigned endHeight = 0;

   //start write thread
   thread writeThreadID;
   shared_ptr<unique_lock<mutex>> batchLock;

   {
      batchLinkPtr = make_shared<BatchLink>();
      batchLock = make_shared<unique_lock<mutex>>(batchLinkPtr->readyToWrite_);

      writeThreadID = thread(writeBlockDataLambda);
   }

   //loop until there are no more blocks available
   try
   {
      while (startHeight <= topBlock.getBlockHeight())
      {
         //figure out how many blocks to pull for this batch
         //batches try to grab up nBlockFilesPerBatch_ worth of block data
         unsigned targetHeight = 0;
         try
         {
            BlockHeader* currentHeader =
               &(blockchain_->getHeaderByHeight(startHeight));
            auto currentBlkFileNum = currentHeader->getBlockFileNum();

            auto targetBlkFileNum = currentBlkFileNum + nBlockFilesPerBatch_;
            targetHeight = startHeight;

            while (currentHeader->getBlockFileNum() < targetBlkFileNum)
               currentHeader = &(blockchain_->getHeaderByHeight(++targetHeight));

         }
         catch (range_error& e)
         {
            //if getHeaderByHeight throws before targetHeight is topBlock's height,
            //something went wrong. Otherwise we just hit the end of the chain.
            if (targetHeight < topBlock.getBlockHeight())
               throw e;
            else
               targetHeight = topBlock.getBlockHeight();
         }

         #ifdef _DEBUG
            targetHeight = startHeight + totalThreadCount_;
            if(targetHeight > topBlock.getBlockHeight())
               targetHeight = topBlock.getBlockHeight();
         #endif

         endHeight = targetHeight;

         //start batch reader threads
         vector<thread> tIDs;
         vector<shared_ptr<BlockDataBatch>> batchVec;

         //start batch scanner threads
         vector<unique_lock<mutex>> lockVec;
         for (unsigned i = 0; i < totalThreadCount_; i++)
         {
            shared_ptr<BlockDataBatch> batch
               = make_shared<BlockDataBatch>(startHeight + i, endHeight);
            batchVec.push_back(batch);
            
            //lock each batch mutex before start scan thread
            lockVec.push_back(unique_lock<mutex>(batchVec[i]->parseTxinMutex_));
            
            tIDs.push_back(thread(scanBlockDataLambda, batch));
         }

         //wait for utxo scan to complete
         for (unsigned i = 0; i < totalThreadCount_; i++)
         {
            auto utxoScanFlag = batchVec[i]->doneScanningUtxos_;
            utxoScanFlag.get();

            if (batchVec[i]->exceptionPtr_ != nullptr)
               rethrow_exception(batchVec[i]->exceptionPtr_);
         }

         //update utxoMap_
         for (auto& batch : batchVec)
         {
            for (auto& txidMap : batch->utxos_)
            {
               utxoMap_[txidMap.first].insert(
                  txidMap.second.begin(), txidMap.second.end());
            }
         }

         //signal txin scan by releasing all mutexes
         lockVec.clear();

         //wait until txins are scanned
         for (auto& tID : tIDs)
         {
            if (tID.joinable())
               tID.join();
         }

         //push scanned batch to write thread
         accumulateDataBeforeBatchWrite(batchVec);

         auto currentBatchPtr = batchLinkPtr;
         batchLinkPtr = make_shared<BatchLink>();
         auto currentBatchLock = batchLock;
         batchLock = make_shared<unique_lock<mutex>>(batchLinkPtr->readyToWrite_);
         
         currentBatchPtr->topScannedBlockHash_ = topScannedBlockHash_;
         currentBatchPtr->batchVec_ = batchVec;
         currentBatchPtr->next_ = batchLinkPtr;
         
         currentBatchLock.reset();

         //TODO: add a mechanism to wait on the write thread so as to not
         //exhaust RAM with batches queued for writing

         //increment startBlock
         startHeight = endHeight + 1;
      }
   }
   catch (range_error&)
   {
      LOGERR << "failed to grab block data starting height: " << startHeight;
      if (startHeight == scanFrom)
         LOGERR << "no block data was scanned";
   }
   catch (...)
   {
      LOGWARN << "scanning halted unexpectedly";
      //let the scan terminate
   }

   //push termination batch to write thread and wait till it exits
   batchLinkPtr->next_ = nullptr;
   batchLock.reset();

   if (writeThreadID.joinable())
      writeThreadID.join();
}
Exemple #2
0
void ExodusWriter::writeItem( xdm::RefPtr< xdm::Item > item, const xdm::FileSystemPath& path ) {

  WritableExodusFile file( path );

  std::size_t timeStep = 0;

  ex_init_params meshParams;
  std::strcpy( meshParams.title, item->name().substr( 0, MAX_LINE_LENGTH + 1 ).c_str() );

  // First make up collections of pointers to everything that we know about. For now,
  // that is just Blocks.
  GatherExodusObjectsVisitor< EdgeBlock > edgeBlockGatherVisitor;
  GatherExodusObjectsVisitor< FaceBlock > faceBlockGatherVisitor;
  GatherExodusObjectsVisitor< ElementBlock > elementBlockGatherVisitor;
  item->traverse( edgeBlockGatherVisitor );
  item->traverse( faceBlockGatherVisitor );
  item->traverse( elementBlockGatherVisitor );
  meshParams.num_edge_blk = edgeBlockGatherVisitor.pointers().size();
  meshParams.num_face_blk = faceBlockGatherVisitor.pointers().size();
  meshParams.num_elem_blk = elementBlockGatherVisitor.pointers().size();

  if ( meshParams.num_edge_blk + meshParams.num_face_blk + meshParams.num_elem_blk < 1 ) {
    // There is nothing to write.
    return;
  }

  // There is only one Geometry (one unique set of nodes). To find it, we just need to find
  // the first one.
  xdmGrid::Geometry* geom;
  if ( meshParams.num_edge_blk > 0 ) {
    geom = dynamic_cast< Block* >( edgeBlockGatherVisitor.pointers().front() )->geometry().get();
  } else if ( meshParams.num_face_blk > 0 ) {
    geom = dynamic_cast< Block* >( faceBlockGatherVisitor.pointers().front() )->geometry().get();
  } else {
    geom = dynamic_cast< Block* >( elementBlockGatherVisitor.pointers().front() )->geometry().get();
  }
  meshParams.num_dim = geom->dimension();
  meshParams.num_nodes = geom->numberOfNodes();
  std::vector< void* > geomPtrs( 3, NULL );
  for ( std::size_t dim = 0; dim < meshParams.num_dim; ++dim ) {
    geomPtrs[ dim ] = geom->child( dim )->data()->array()->data();
  }
  EXODUS_CALL( ex_put_coord( file.id(), geomPtrs[0], geomPtrs[1], geomPtrs[2] ),
    "Unable to write coordinates." );
  std::vector< ExodusString > coordNames;
  coordNames.push_back( "X" );
  coordNames.push_back( "Y" );
  coordNames.push_back( "Z" );
  char* coordNamesCharArray[ 3 ];
  vectorToCharStarArray( coordNames, coordNamesCharArray );
  EXODUS_CALL( ex_put_coord_names( file.id(), coordNamesCharArray ),
    "Unable to write coordinate names." );

  // Count the entries in the blocks.
  meshParams.num_edge = edgeBlockGatherVisitor.totalNumberOfEntries();
  meshParams.num_face = faceBlockGatherVisitor.totalNumberOfEntries();
  meshParams.num_elem = elementBlockGatherVisitor.totalNumberOfEntries();

  // Not doing sets and maps at the moment...
  meshParams.num_node_sets = 0;
  meshParams.num_edge_sets = 0;
  meshParams.num_face_sets = 0;
  meshParams.num_side_sets = 0;
  meshParams.num_elem_sets = 0;
  meshParams.num_node_maps = 0;
  meshParams.num_edge_maps = 0;
  meshParams.num_face_maps = 0;
  meshParams.num_elem_maps = 0;

  EXODUS_CALL( ex_put_init_ext( file.id(), &meshParams ),
    "Unable to initialize database.\n" );

  // Write the time step info, if there is any.
  FindTimeVisitor timeVisit;
  item->traverse( timeVisit );
  if ( timeVisit.time() ) {
    double timeVal = timeVisit.time()->value();
    EXODUS_CALL( ex_put_time( file.id(), (int)( timeStep + 1 ), (void*)&timeVal ),
      "Unable to write time value." );
  }

  // Write the blocks.
  writeBlockData(
    file.id(),
    EX_EDGE_BLOCK,
    edgeBlockGatherVisitor.names(),
    edgeBlockGatherVisitor.pointers() );

  writeBlockData(
    file.id(),
    EX_FACE_BLOCK,
    faceBlockGatherVisitor.names(),
    faceBlockGatherVisitor.pointers() );

  writeBlockData(
    file.id(),
    EX_ELEM_BLOCK,
    elementBlockGatherVisitor.names(),
    elementBlockGatherVisitor.pointers() );

  // Write the variable data for this time step.
  for ( std::size_t blockIndex = 0; blockIndex < meshParams.num_edge_blk; ++blockIndex ) {
    edgeBlockGatherVisitor.pointers()[ blockIndex ]->writeTimeStep( file.id(), timeStep );
  }
  for ( std::size_t blockIndex = 0; blockIndex < meshParams.num_face_blk; ++blockIndex ) {
    faceBlockGatherVisitor.pointers()[ blockIndex ]->writeTimeStep( file.id(), timeStep );
  }
  for ( std::size_t blockIndex = 0; blockIndex < meshParams.num_elem_blk; ++blockIndex ) {
    elementBlockGatherVisitor.pointers()[ blockIndex ]->writeTimeStep( file.id(), timeStep );
  }

}