void CBThreadPoolQueueAdd(CBThreadPoolQueue * self, CBQueueItem * item){ item->next = NULL; item->active = false; item->cleared = false; // Find worker with least items to process uint16_t workerI = 0; for (uint16_t x = 1; x < self->numThreads; x++){ CBMutexLock(self->workers[x].queueMutex); if (self->workers[x].queue.itemNum < self->workers[workerI].queue.itemNum) workerI = x; CBMutexUnlock(self->workers[x].queueMutex); // Queue may change in the meantime but this wont cause any problems } CBWorker * worker = self->workers + workerI; CBMutexLock(self->finishMutex); self->finished = false; CBMutexLock(worker->queueMutex); worker->queue.itemNum++; if (!worker->queue.start) { worker->queue.end = worker->queue.start = item; // We have added the first item to the queue so wake up the thread. assert(worker->queue.itemNum); CBConditionSignal(worker->waitCond); }else worker->queue.end = worker->queue.end->next = item; CBMutexUnlock(worker->queueMutex); CBMutexUnlock(self->finishMutex); }
void CBNodeProcessMessages(void * node){ CBNode * self = node; CBMutexLock(self->messageProcessMutex); for (;;) { if (self->messageQueue == NULL && !self->shutDownThread){ // Wait for more messages CBConditionWait(self->messageProcessWaitCond, self->messageProcessMutex); CBLogVerbose("Process thread as been woken"); } // Check to see if the thread should terminate if (self->shutDownThread){ CBMutexUnlock(self->messageProcessMutex); return; } // Process next message on queue CBMessageQueue * toProcess = self->messageQueue; CBMutexUnlock(self->messageProcessMutex); // Handle alerts CBOnMessageReceivedAction action; if (toProcess->message->type == CB_MESSAGE_TYPE_ALERT) // The peer sent us an alert message action = CBNodeProcessAlert(self, toProcess->peer, CBGetAlert(toProcess->message)); else action = self->onMessageReceived(self, toProcess->peer, toProcess->message); if (action == CB_MESSAGE_ACTION_DISCONNECT) // We need to disconnect the node CBNodeDisconnectPeer(toProcess->peer); CBMutexLock(self->messageProcessMutex); // Remove this from queue CBReleaseObject(toProcess->message); CBReleaseObject(toProcess->peer); self->messageQueue = toProcess->next; free(toProcess); } }
void CBThreadPoolQueueClear(CBThreadPoolQueue * self){ CBMutexLock(self->finishMutex); for (uint16_t x = 0; x < self->numThreads; x++) { CBMutexLock(self->workers[x].queueMutex); // Empty queue CBFreeQueue(&self->workers[x].queue, self->destroy); self->workers[x].queue.itemNum = 0; CBMutexUnlock(self->workers[x].queueMutex); } self->finished = true; CBConditionSignal(self->finishCond); CBMutexUnlock(self->finishMutex); }
void CBNodeOnValidatorError(void * vself){ CBNode * self = vself; CBMutexUnlock(CBGetNode(self)->blockAndTxMutex); CBLogError("There was a validation error."); self->callbacks.onFatalNodeError(self, CB_ERROR_VALIDATION); CBReleaseObject(self); }
void CBNodeOnBadTime(void * vself){ CBNode * self = vself; CBMutexUnlock(CBGetNode(self)->blockAndTxMutex); CBLogError("The system time is potentially incorrect. It does not match with the network time."); self->callbacks.onFatalNodeError(self, CB_ERROR_BAD_TIME); CBReleaseObject(self); }
void CBThreadPoolQueueThreadLoop(void * vself){ CBWorker * self = vself; CBMutexLock(self->queueMutex); for (;;) { while (self->queue.itemNum == 0 && !self->threadPoolQueue->shutdown) // Wait for more items to process. We require a loop because when the condition is signaled, the mutex is unlocked but may not be given to this thread. Another thread may use the mutex to clear the queue again, so check in a loop. CBConditionWait(self->waitCond, self->queueMutex); assert(self->queue.itemNum || self->threadPoolQueue->shutdown); // Check to see if the thread should terminate if (self->threadPoolQueue->shutdown){ CBMutexUnlock(self->queueMutex); return; } // Process the next item. CBQueueItem * item = self->queue.start; item->active = true; // Prevent deletion. CBMutexUnlock(self->queueMutex); self->threadPoolQueue->process(self->threadPoolQueue, item); // Now we have finished with the item, remove it from the queue CBMutexLock(self->queueMutex); // Maybe we cleared the queue. Check that it hasn't been. if (!item->cleared) { self->queue.start = item->next; if (--self->queue.itemNum == 0){ // Look for complete emptiness CBMutexLock(self->threadPoolQueue->finishMutex); bool empty = true; for (uint16_t x = 0; x < self->threadPoolQueue->numThreads; x++) if (self->threadPoolQueue->workers[x].queue.itemNum) { empty = false; break; } if (empty){ self->threadPoolQueue->finished = true; CBConditionSignal(self->threadPoolQueue->finishCond); } CBMutexUnlock(self->threadPoolQueue->finishMutex); } } // Now we can destroy the item. self->threadPoolQueue->destroy(item); free(item); } }
void CBDestroyNode(void * vself){ CBNode * self = vself; // Exit thread. self->shutDownThread = true; CBMutexLock(self->messageProcessMutex); if (self->messageQueue == NULL) // The thread is waiting for messages so wake it. CBConditionSignal(self->messageProcessWaitCond); CBMutexUnlock(self->messageProcessMutex); CBThreadJoin(self->messageProcessThread); CBFreeBlockChainStorage(self->blockChainStorage); CBFreeAccounterStorage(self->accounterStorage); CBReleaseObject(self->validator); CBFreeMutex(self->blockAndTxMutex); CBFreeMutex(self->messageProcessMutex); CBFreeCondition(self->messageProcessWaitCond); CBFreeThread(self->messageProcessThread); CBDestroyNetworkCommunicator(vself); }
void CBDestroyThreadPoolQueue(CBThreadPoolQueue * self){ self->shutdown = true; for (uint16_t x = 0; x < self->numThreads; x++) { CBMutexLock(self->workers[x].queueMutex); if (self->workers[x].queue.itemNum == 0) // The thread is waiting for items, so wake it. CBConditionSignal(self->workers[x].waitCond); CBMutexUnlock(self->workers[x].queueMutex); CBThreadJoin(self->workers[x].thread); CBFreeThread(self->workers[x].thread); CBFreeCondition(self->workers[x].waitCond); CBFreeMutex(self->workers[x].queueMutex); // Free queue CBFreeQueue(&self->workers[x].queue, self->destroy); } CBFreeCondition(self->finishCond); CBFreeMutex(self->finishMutex); free(self->workers); }
CBOnMessageReceivedAction CBNodeOnMessageReceived(CBNetworkCommunicator * comm, CBPeer * peer, CBMessage * message){ CBNode * self = CBGetNode(comm); // Add message to queue CBRetainObject(message); CBRetainObject(peer); CBMutexLock(self->messageProcessMutex); if (self->messageQueue == NULL) self->messageQueue = self->messageQueueBack = malloc(sizeof(*self->messageQueueBack)); else{ self->messageQueueBack->next = malloc(sizeof(*self->messageQueueBack)); self->messageQueueBack = self->messageQueueBack->next; } self->messageQueueBack->peer = peer; self->messageQueueBack->message = message; self->messageQueueBack->next = NULL; // Wakeup thread if this is the first in the queue if (self->messageQueue == self->messageQueueBack) // We have just added a block to the queue when there was not one before so wake-up the processing thread. CBConditionSignal(self->messageProcessWaitCond); CBMutexUnlock(self->messageProcessMutex); return CB_MESSAGE_ACTION_CONTINUE; }
CBOnMessageReceivedAction CBNodeSendBlocksInvOrHeaders(CBNode * self, CBPeer * peer, CBGetBlocks * getBlocks, bool full){ CBChainDescriptor * chainDesc = getBlocks->chainDescriptor; if (chainDesc->hashNum == 0) // Why do this? return CB_MESSAGE_ACTION_DISCONNECT; uint8_t branch; uint32_t blockIndex; // Go though the chain descriptor until a hash is found that we own bool found = false; // Lock block mutex for block chain access CBMutexLock(self->blockAndTxMutex); for (uint16_t x = 0; x < chainDesc->hashNum; x++) { CBErrBool exists = CBBlockChainStorageGetBlockLocation(CBGetNode(self)->validator, CBByteArrayGetData(chainDesc->hashes[x]), &branch, &blockIndex); if (exists == CB_ERROR) { CBMutexUnlock(self->blockAndTxMutex); return CBNodeReturnError(self, "Could not look for block with chain descriptor hash."); } if (exists == CB_TRUE) { // We have a block that we own. found = true; break; } } // Get the chain path for the main chain CBChainPath mainChainPath = CBValidatorGetMainChainPath(self->validator); CBChainPathPoint intersection; if (found){ // Get the chain path for the block header we have found CBChainPath peerChainPath = CBValidatorGetChainPath(self->validator, branch, blockIndex); // Determine where the intersection is on the main chain intersection = CBValidatorGetChainIntersection(&mainChainPath, &peerChainPath); }else{ // Bad chain? CBMutexUnlock(self->blockAndTxMutex); return CB_MESSAGE_ACTION_DISCONNECT; } CBMessage * message; // Now provide headers from this intersection up to 2000 blocks or block inventory items up to 500, the last one we have or the stopAtHash. for (uint16_t x = 0; x < (full ? 500 : 2000); x++) { // Check to see if we reached the last block in the main chain. if (intersection.chainPathIndex == 0 && intersection.blockIndex == mainChainPath.points[0].blockIndex) { if (x == 0){ // The intersection is at the last block. The peer is up-to-date with us peer->upload = false; peer->upToDate = true; CBMutexUnlock(self->blockAndTxMutex); return CB_MESSAGE_ACTION_CONTINUE; } break; } // Move to the next block if (intersection.blockIndex == mainChainPath.points[intersection.chainPathIndex].blockIndex) { // Move to next branch intersection.chainPathIndex--; intersection.blockIndex = 0; }else // Move to the next block intersection.blockIndex++; // Get the hash uint8_t hash[32]; if (! CBBlockChainStorageGetBlockHash(self->validator, mainChainPath.points[intersection.chainPathIndex].branch, intersection.blockIndex, hash)) { if (x != 0) CBReleaseObject(message); CBMutexUnlock(self->blockAndTxMutex); return CBNodeReturnError(self, "Could not obtain a hash for a block."); } // Check to see if we are at stopAtHash if (getBlocks->stopAtHash && memcmp(hash, CBByteArrayGetData(getBlocks->stopAtHash), 32) == 0){ if (x == 0) { CBMutexUnlock(self->blockAndTxMutex); return CB_MESSAGE_ACTION_CONTINUE; } break; } if (x == 0){ // Create inventory or block headers object to send to peer. if (full) { message = CBGetMessage(CBNewInventory()); message->type = CB_MESSAGE_TYPE_INV; }else{ message = CBGetMessage(CBNewBlockHeaders()); message->type = CB_MESSAGE_TYPE_HEADERS; } } // Add block header or add inventory item if (full) { CBByteArray * hashObj = CBNewByteArrayWithDataCopy(hash, 32); char blkStr[CB_BLOCK_HASH_STR_SIZE]; CBByteArrayToString(hashObj, 0, CB_BLOCK_HASH_STR_BYTES, blkStr, true); CBInventoryTakeInventoryItem(CBGetInventory(message), CBNewInventoryItem(CB_INVENTORY_ITEM_BLOCK, hashObj)); CBReleaseObject(hashObj); }else CBBlockHeadersTakeBlockHeader(CBGetBlockHeaders(message), CBBlockChainStorageGetBlockHeader(self->validator, mainChainPath.points[intersection.chainPathIndex].branch, intersection.blockIndex)); } CBMutexUnlock(self->blockAndTxMutex); // Send the message CBNodeSendMessageOnNetworkThread(CBGetNetworkCommunicator(self), peer, message, NULL); CBReleaseObject(message); // We are uploading to the peer peer->upload = true; return CB_MESSAGE_ACTION_CONTINUE; }
void CBThreadPoolQueueWaitUntilFinished(CBThreadPoolQueue * self) { CBMutexLock(self->finishMutex); if (!self->finished) // Do not check in loop as no threads ought to add to the queue whilst waiting to finish. CBConditionWait(self->finishCond, self->finishMutex); CBMutexUnlock(self->finishMutex); }