void NetworkInterfaceMock::_enqueueOperation_inlock( mongo::executor::NetworkInterfaceMock::NetworkOperation&& op) { auto insertBefore = std::upper_bound(std::begin(_unscheduled), std::end(_unscheduled), op, [](const NetworkOperation& a, const NetworkOperation& b) { return a.getNextConsiderationDate() < b.getNextConsiderationDate(); }); _unscheduled.emplace(insertBefore, std::move(op)); if (op.getRequest().timeout != RemoteCommandRequest::kNoTimeout) { invariant(op.getRequest().timeout >= Milliseconds(0)); ResponseStatus response(ErrorCodes::NetworkTimeout, "Network timeout"); auto action = stdx::bind( &NetworkInterfaceMock::_cancelCommand_inlock, this, op.getCallbackHandle(), response); _alarms.emplace(_now_inlock() + op.getRequest().timeout, action); } }
Status NetworkInterfaceMock::startCommand(const CallbackHandle& cbHandle, RemoteCommandRequest& request, const RemoteCommandCompletionFn& onFinish) { if (inShutdown()) { return {ErrorCodes::ShutdownInProgress, "NetworkInterfaceMock shutdown in progress"}; } stdx::lock_guard<stdx::mutex> lk(_mutex); const Date_t now = _now_inlock(); auto op = NetworkOperation(cbHandle, request, now, onFinish); // If we don't have a hook, or we have already 'connected' to this host, enqueue the op. if (!_hook || _connections.count(request.target)) { _enqueueOperation_inlock(std::move(op)); } else { _connectThenEnqueueOperation_inlock(request.target, std::move(op)); } return Status::OK(); }
void NetworkInterfaceMock::_runReadyNetworkOperations_inlock(stdx::unique_lock<stdx::mutex>* lk) { while (!_scheduled.empty() && _scheduled.front().getResponseDate() <= _now_inlock()) { invariant(_currentlyRunning == kNetworkThread); NetworkOperation op = _scheduled.front(); _scheduled.pop_front(); _waitingToRunMask |= kExecutorThread; lk->unlock(); op.finishResponse(); lk->lock(); } invariant(_currentlyRunning == kNetworkThread); if (!(_waitingToRunMask & kExecutorThread)) { return; } _shouldWakeExecutorCondition.notify_one(); _currentlyRunning = kNoThread; while (!_isNetworkThreadRunnable_inlock()) { _shouldWakeNetworkCondition.wait(*lk); } _currentlyRunning = kNetworkThread; _waitingToRunMask &= ~kNetworkThread; }
void NetworkInterfaceMock::_connectThenEnqueueOperation_inlock(const HostAndPort& target, NetworkOperation&& op) { invariant(_hook); // if there is no hook, we shouldn't even hit this codepath invariant(!_connections.count(target)); auto handshakeReplyIter = _handshakeReplies.find(target); auto handshakeReply = (handshakeReplyIter != std::end(_handshakeReplies)) ? handshakeReplyIter->second : RemoteCommandResponse(BSONObj(), BSONObj(), Milliseconds(0)); auto valid = _hook->validateHost(target, handshakeReply); if (!valid.isOK()) { op.setResponse(_now_inlock(), valid); op.finishResponse(); return; } auto swHookPostconnectCommand = _hook->makeRequest(target); if (!swHookPostconnectCommand.isOK()) { op.setResponse(_now_inlock(), swHookPostconnectCommand.getStatus()); op.finishResponse(); return; } boost::optional<RemoteCommandRequest> hookPostconnectCommand = std::move(swHookPostconnectCommand.getValue()); if (!hookPostconnectCommand) { // If we don't have a post connect command, enqueue the actual command. _enqueueOperation_inlock(std::move(op)); _connections.emplace(op.getRequest().target); return; } // The completion handler for the postconnect command schedules the original command. auto postconnectCompletionHandler = [this, op](ResponseStatus rs) mutable { stdx::lock_guard<stdx::mutex> lk(_mutex); if (!rs.isOK()) { op.setResponse(_now_inlock(), rs); op.finishResponse(); return; } auto handleStatus = _hook->handleReply(op.getRequest().target, std::move(rs)); if (!handleStatus.isOK()) { op.setResponse(_now_inlock(), handleStatus); op.finishResponse(); return; } _enqueueOperation_inlock(std::move(op)); _connections.emplace(op.getRequest().target); }; auto postconnectOp = NetworkOperation(op.getCallbackHandle(), std::move(*hookPostconnectCommand), _now_inlock(), std::move(postconnectCompletionHandler)); _enqueueOperation_inlock(std::move(postconnectOp)); }
Date_t NetworkInterfaceMock::now() { stdx::lock_guard<stdx::mutex> lk(_mutex); return _now_inlock(); }