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(); }
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 ); } }