void LLMediaDataClient::swapCurrentQueue() { // Swap mCurrentQueueIsTheSortedQueue = !mCurrentQueueIsTheSortedQueue; // If its empty, swap back if (getCurrentQueue()->empty()) { mCurrentQueueIsTheSortedQueue = !mCurrentQueueIsTheSortedQueue; } }
void AsyncFileWriter::ioThread() { folly::setThreadName("log_writer"); while (true) { // With the lock held, grab a pointer to the current queue, then increment // the ioThreadCounter index so that other threads will write into the // other queue as we process this one. std::vector<std::string>* ioQueue; size_t numDiscarded; bool stop; { auto data = data_.lock(); ioQueue = data->getCurrentQueue(); while (ioQueue->empty() && !data->stop) { messageReady_.wait(data.getUniqueLock()); } ++data->ioThreadCounter; numDiscarded = data->numDiscarded; data->numDiscarded = 0; data->currentBufferSize = 0; stop = data->stop; } ioCV_.notify_all(); // Write the log messages now that we have released the lock try { performIO(ioQueue); } catch (const std::exception& ex) { onIoError(ex); } // clear() empties the vector, but the allocated capacity remains so we can // just reuse it without having to re-allocate in most cases. ioQueue->clear(); if (numDiscarded > 0) { auto msg = getNumDiscardedMsg(numDiscarded); if (!msg.empty()) { auto ret = folly::writeFull(file_.fd(), msg.data(), msg.size()); // We currently ignore errors from writeFull() here. // There's not much we can really do. (void)ret; } } if (stop) { data_->ioThreadDone = true; break; } } }
void AsyncFileWriter::writeMessage(std::string&& buffer, uint32_t flags) { auto data = data_.lock(); if ((data->currentBufferSize >= data->maxBufferBytes) && !(flags & NEVER_DISCARD)) { ++data->numDiscarded; return; } data->currentBufferSize += buffer.size(); auto* queue = data->getCurrentQueue(); queue->emplace_back(std::move(buffer)); messageReady_.notify_one(); }
void AsyncFileWriter::flush() { auto data = data_.lock(); auto start = data->ioThreadCounter; // Wait until ioThreadCounter increments by at least two. // Waiting for a single increment is not sufficient, as this happens after // the I/O thread has swapped the queues, which is before it has actually // done the I/O. while (data->ioThreadCounter < start + 2) { if (data->ioThreadDone) { return; } // Enqueue an empty string and wake the I/O thread. // The empty string ensures that the I/O thread will break out of its wait // loop and increment the ioThreadCounter, even if there is no other work // to do. data->getCurrentQueue()->emplace_back(); messageReady_.notify_one(); // Wait for notification from the I/O thread that it has done work. ioCV_.wait(data.getUniqueLock()); } }
void LLMediaDataClient::serviceQueue() { request_queue_t *queue_p = getCurrentQueue(); // quick retry loop for cases where we shouldn't wait for the next timer tick while(true) { if (queue_p->empty()) { LL_DEBUGS("LLMediaDataClient") << "queue empty: " << (*queue_p) << LL_ENDL; break; } // Peel one off of the items from the queue, and execute request request_ptr_t request = queue_p->front(); llassert(!request.isNull()); const LLMediaDataClientObject *object = (request.isNull()) ? NULL : request->getObject(); llassert(NULL != object); // Check for conditions that would make us just pop and rapidly loop through // the queue. if(request.isNull() || request->isMarkedSent() || NULL == object || object->isDead() || !object->hasMedia()) { if (request.isNull()) { LL_WARNS("LLMediaDataClient") << "Skipping NULL request" << LL_ENDL; } else { LL_INFOS("LLMediaDataClient") << "Skipping : " << *request << " " << ((request->isMarkedSent()) ? " request is marked sent" : ((NULL == object) ? " object is NULL " : ((object->isDead()) ? "object is dead" : ((!object->hasMedia()) ? "object has no media!" : "BADNESS!")))) << LL_ENDL; } queue_p->pop_front(); continue; // jump back to the start of the quick retry loop } // Next, ask if this is "interesting enough" to fetch. If not, just stop // and wait for the next timer go-round. Only do this for the sorted // queue. if (mCurrentQueueIsTheSortedQueue && !object->isInterestingEnough()) { LL_DEBUGS("LLMediaDataClient") << "Not fetching " << *request << ": not interesting enough" << LL_ENDL; break; } // Finally, try to send the HTTP message to the cap url std::string url = request->getCapability(); bool maybe_retry = false; if (!url.empty()) { const LLSD &sd_payload = request->getPayload(); LL_INFOS("LLMediaDataClient") << "Sending request for " << *request << LL_ENDL; // Call the subclass for creating the responder LLHTTPClient::post(url, sd_payload, createResponder(request)); } else { LL_INFOS("LLMediaDataClient") << "NOT Sending request for " << *request << ": empty cap url!" << LL_ENDL; maybe_retry = true; } bool exceeded_retries = request->getRetryCount() > mMaxNumRetries; if (maybe_retry && ! exceeded_retries) // Try N times before giving up { // We got an empty cap, but in that case we will retry again next // timer fire. request->incRetryCount(); } else { if (exceeded_retries) { LL_WARNS("LLMediaDataClient") << "Could not send request " << *request << " for " << mMaxNumRetries << " tries...popping object id " << object->getID() << LL_ENDL; // XXX Should we bring up a warning dialog?? } queue_p->pop_front(); if (! mCurrentQueueIsTheSortedQueue) { // Round robin request->markSent(true); mRoundRobinQueue.push_back(request); } } // end of quick loop -- any cases where we want to loop will use 'continue' to jump back to the start. break; } swapCurrentQueue(); }