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();
}
Example #5
0
void ASIOConnection::indicateSuccess() {
    indicateUsed();
    _status = Status::OK();
}