void ConnectionImpl::pushSetup(PushSetupCallback status) { _pushSetupQueue.push_back(status); if (_setupQueue.size()) { auto connPtr = _setupQueue.front(); auto callback = _pushSetupQueue.front(); _setupQueue.pop_front(); _pushSetupQueue.pop_front(); auto cb = connPtr->_setupCallback; connPtr->indicateUsed(); cb(connPtr, callback()); } }
void ConnectionImpl::pushRefresh(PushRefreshCallback status) { _pushRefreshQueue.push_back(status); if (_refreshQueue.size()) { auto connPtr = _refreshQueue.front(); auto callback = _pushRefreshQueue.front(); _refreshQueue.pop_front(); _pushRefreshQueue.pop_front(); auto cb = connPtr->_refreshCallback; connPtr->indicateUsed(); cb(connPtr, callback()); } }
void ConnectionImpl::setup(Milliseconds timeout, SetupCallback cb) { _setupCallback = std::move(cb); _timer.setTimeout(timeout, [this] { _setupCallback(this, Status(ErrorCodes::NetworkInterfaceExceededTimeLimit, "timeout")); }); _setupQueue.push_back(this); if (_pushSetupQueue.size()) { auto connPtr = _setupQueue.front(); auto callback = _pushSetupQueue.front(); _setupQueue.pop_front(); _pushSetupQueue.pop_front(); auto refreshCb = connPtr->_setupCallback; connPtr->indicateUsed(); refreshCb(connPtr, callback()); } }
// NOTE: This method may only be called by ASIO threads // (do not call from methods entered by TaskExecutor threads) void NetworkInterfaceASIO::_completeOperation(AsyncOp* op, ResponseStatus resp) { auto metadata = op->getResponseMetadata(); if (!metadata.isEmpty()) { resp.metadata = metadata; } // Cancel this operation's timeout. Note that the timeout callback may already be running, // may have run, or may have already been scheduled to run in the near future. if (op->_timeoutAlarm) { op->_timeoutAlarm->cancel(); } if (resp.status.code() == ErrorCodes::ExceededTimeLimit) { _numTimedOutOps.fetchAndAdd(1); } if (op->_inSetup) { // If we are in setup we should only be here if we failed to connect. MONGO_ASIO_INVARIANT(!resp.isOK(), "Failed to connect in setup", op); // If we fail during connection, we won't be able to access any of op's members after // calling finish(), so we return here. log() << "Failed to connect to " << op->request().target << " - " << resp.status; _numFailedOps.fetchAndAdd(1); op->finish(resp); return; } if (op->_inRefresh) { // If we are in refresh we should only be here if we failed to heartbeat. MONGO_ASIO_INVARIANT(!resp.isOK(), "In refresh, but did not fail to heartbeat", op); // If we fail during heartbeating, we won't be able to access any of op's members after // calling finish(), so we return here. log() << "Failed asio heartbeat to " << op->request().target << " - " << redact(resp.status); _numFailedOps.fetchAndAdd(1); op->finish(resp); return; } if (!resp.isOK()) { // In the case that resp is not OK, but _inSetup is false, we are using a connection // that // we got from the pool to execute a command, but it failed for some reason. LOG(2) << "Failed to execute command: " << redact(op->request().toString()) << " reason: " << redact(resp.status); if (resp.status.code() != ErrorCodes::CallbackCanceled) { _numFailedOps.fetchAndAdd(1); } } else { _numSucceededOps.fetchAndAdd(1); } std::unique_ptr<AsyncOp> ownedOp; { stdx::lock_guard<stdx::mutex> lk(_inProgressMutex); auto iter = _inProgress.find(op); MONGO_ASIO_INVARIANT_INLOCK( iter != _inProgress.end(), "Could not find AsyncOp in _inProgress", op); ownedOp = std::move(iter->second); _inProgress.erase(iter); } op->finish(resp); MONGO_ASIO_INVARIANT(static_cast<bool>(ownedOp), "Invalid AsyncOp", op); auto conn = std::move(op->_connectionPoolHandle); auto asioConn = static_cast<connection_pool_asio::ASIOConnection*>(conn.get()); // Prevent any other threads or callbacks from accessing this op so we may safely complete // and destroy it. It is key that we do this after we remove the op from the _inProgress map // or someone else in cancelCommand could read the bumped generation and cancel the next // command that uses this op. See SERVER-20556. { stdx::lock_guard<stdx::mutex> lk(op->_access->mutex); ++(op->_access->id); } // We need to bump the generation BEFORE we call reset() or we could flip the timeout in the // timeout callback before returning the AsyncOp to the pool. ownedOp->reset(); asioConn->bindAsyncOp(std::move(ownedOp)); if (!resp.isOK()) { asioConn->indicateFailure(resp.status); } else { asioConn->indicateUsed(); asioConn->indicateSuccess(); } signalWorkAvailable(); }
void ASIOConnection::indicateSuccess() { indicateUsed(); _status = Status::OK(); }