void M8MPoolConnectingApp::Refresh(std::vector<Network::SocketInterface*> &toRead, std::vector<Network::SocketInterface*> &toWrite) { M8MConfiguredApp::Tick(); // First of all, shut down pools whose connection has gone down. auto goodbye = [this](Network::SocketInterface *test) { if(!test) return; // this happens as Network::SleepOn clears dormient sockets. if(test->Works()) return; auto entry(std::find_if(pools.begin(), pools.end(), [test](Pool &entry) { return entry.route == test; })); if(entry == pools.end()) return; // this could be for example a mini-server web socket ConnectionState(*entry->source, ce_failed); entry->source->Disconnected(); network.CloseConnection(*entry->route); entry->route = nullptr; auto zero = std::chrono::system_clock::time_point(); if(entry->activated != zero) { auto now(std::chrono::system_clock::now()); entry->totalTime += now - entry->activated; entry->activated = zero; entry->nextReconnect = now + reconnectDelay; } }; for(auto entry : toRead) goodbye(entry); for(auto entry : toWrite) goodbye(entry); // Then do proper IO. auto activated = [](const std::vector<Network::SocketInterface*> &sockets, Network::SocketInterface *check) { return std::find(sockets.cbegin(), sockets.cend(), check) != sockets.cend(); }; for(auto &entry : pools) { if(entry.source->Ready() == false) continue; auto r(activated(toRead, entry.route)); auto w(activated(toWrite, entry.route)); if(!w && !r) continue; auto &pool(*entry.source); auto happens(pool.Refresh(r, w)); if(happens.connFailed) goodbye(entry.route); else { if(happens.bytesReceived) PoolCommand(pool); if(happens.diffChanged) DiffChange(pool, pool.GetCurrentDiff()); if(happens.newWork) WorkChange(pool, std::unique_ptr<stratum::AbstractWorkFactory>(pool.GenWork())); } } // Then initialize pools which have just connected. for(auto &entry : pools) { if(entry.source->Ready()) continue; // already fully enabled, not connecting if(entry.route == nullptr) continue; // disabled, different algo, not even trying if(!activated(toWrite, entry.route)) continue; entry.activated = std::chrono::system_clock::now(); entry.numActivations++; entry.source->Use(entry.route); // what if connection failed? Nothing. We try anyway and then bail out. ConnectionState(*entry.source, ce_ready); } AttemptReconnections(); }
Connection* ConnectionClose(Connection* connection, bool force) { Log(AIO4C_LOG_LEVEL_DEBUG, "closing connection %s (force: %s)", connection->string, (force?"true":"false")); if (force) { ConnectionState(connection, AIO4C_CONNECTION_STATE_CLOSED); } else { ConnectionState(connection, AIO4C_CONNECTION_STATE_PENDING_CLOSE); } return connection; }
asizei M8MPoolConnectingApp::BeginPoolActivation(const char *algo) { asizei activated = 0; for(auto &entry : pools) { if(_stricmp(entry.config.algo.c_str(), algo)) { // different algos get disabled. if(entry.route) { entry.source->Shutdown(); network.CloseConnection(*entry.route); entry.route = nullptr; } auto zero = std::chrono::system_clock::time_point(); if(entry.activated != zero) { entry.totalTime += std::chrono::system_clock::now() - entry.activated; entry.activated = zero; } } else if(entry.route) { // A spurious call. Let's just ignore it, when connection will complete, it will be activated. // Or perhaps not, if connection fails but that's nothing I can fix there! activated++; // they still count however. } else { const char *port = entry.config.explicitPort.length()? entry.config.explicitPort.c_str() : entry.config.service.c_str(); auto conn(network.BeginConnection(entry.config.host.c_str(), port)); if(conn.first == nullptr) { ConnectionState(*entry.source, MapError(conn.second)); // A failing connection is a very bad thing. It probably just makes sense to bail out but in case it's transient, // let's retry with a multiple of retry delay. It's really odd so give it quite some time to clear out. entry.nextReconnect = std::chrono::system_clock::now() + reconnectDelay * 4; } else { entry.route = conn.first; activated++; ConnectionState(*entry.source, ce_connecting); } // note pool is not activated yet; they are activated in Refresh() when their connection is ready. } } return activated; }
void M8MPoolConnectingApp::AttemptReconnections() { asizei restarted = 0; auto now(std::chrono::system_clock::now()); auto zero = std::chrono::system_clock::time_point(); // for some reason writing this with init syntax makes Intellisense think it's a function for(auto &entry : pools) { if(entry.nextReconnect == zero) continue; if(entry.nextReconnect > now) continue; const char *port = entry.config.explicitPort.length()? entry.config.explicitPort.c_str() : entry.config.service.c_str(); auto conn(network.BeginConnection(entry.config.host.c_str(), port)); if(conn.first == nullptr) { ConnectionState(*entry.source, MapError(conn.second)); // A failing connection is a very bad thing. It probably just makes sense to bail out but in case it's transient, // let's retry with a multiple of retry delay. It's really odd so give it quite some time to clear out. entry.nextReconnect = std::chrono::system_clock::now() + reconnectDelay * 4; } else { entry.route = conn.first; restarted++; entry.nextReconnect = zero; ConnectionState(*entry.source, ce_connecting); } } }
void ConnectionManagedBy(Connection* connection, ConnectionOwner owner) { bool managedByAll = true; int i = 0; TakeLock(connection->managedByLock); connection->managedBy[owner] = true; Log(AIO4C_LOG_LEVEL_DEBUG, "connection %s managed by: [reader:%u,worker:%u,writer:%u]", connection->string, connection->managedBy[AIO4C_CONNECTION_OWNER_READER], connection->managedBy[AIO4C_CONNECTION_OWNER_WORKER], connection->managedBy[AIO4C_CONNECTION_OWNER_WRITER]); for (i = 0; i < AIO4C_CONNECTION_OWNER_MAX; i++) { managedByAll = (managedByAll && connection->managedBy[i]); } ReleaseLock(connection->managedByLock); if (managedByAll) { Log(AIO4C_LOG_LEVEL_DEBUG, "connection %s is managed by all threads", connection->string); ConnectionState(connection, AIO4C_CONNECTION_STATE_CONNECTED); } }
Connection* ConnectionInit(Connection* connection) { ErrorCode code = AIO4C_ERROR_CODE_INITIALIZER; #ifndef AIO4C_WIN32 if ((connection->socket = socket(PF_INET, SOCK_STREAM, 0)) == -1) { code.error = errno; #else /* AIO4C_WIN32 */ if ((connection->socket = socket(PF_INET, SOCK_STREAM, 0)) == SOCKET_ERROR) { code.source = AIO4C_ERRNO_SOURCE_WSA; #endif /* AIO4C_WIN32 */ return _ConnectionHandleError(connection, AIO4C_LOG_LEVEL_ERROR, AIO4C_SOCKET_ERROR, &code); } #ifndef AIO4C_WIN32 if (fcntl(connection->socket, F_SETFL, O_NONBLOCK) == -1) { code.error = errno; #else /* AIO4C_WIN32 */ unsigned long ioctl = 1; if (ioctlsocket(connection->socket, FIONBIO, &ioctl) == SOCKET_ERROR) { code.source = AIO4C_ERRNO_SOURCE_WSA; #endif /* AIO4C_WIN32 */ return _ConnectionHandleError(connection, AIO4C_LOG_LEVEL_ERROR, AIO4C_FCNTL_ERROR, &code); } ConnectionState(connection, AIO4C_CONNECTION_STATE_INITIALIZED); return connection; } Connection* ConnectionConnect(Connection* connection) { ErrorCode code = AIO4C_ERROR_CODE_INITIALIZER; ConnectionState newState = AIO4C_CONNECTION_STATE_CONNECTING; if (connection->state != AIO4C_CONNECTION_STATE_INITIALIZED) { code.expected = AIO4C_CONNECTION_STATE_INITIALIZED; return _ConnectionHandleError(connection, AIO4C_LOG_LEVEL_DEBUG, AIO4C_CONNECTION_STATE_ERROR, &code); } if (connect(connection->socket, AddressGetAddr(connection->address), AddressGetAddrSize(connection->address)) == -1) { #ifndef AIO4C_WIN32 code.error = errno; if (errno == EINPROGRESS) { #else /* AIO4C_WIN32 */ int error = WSAGetLastError(); code.source = AIO4C_ERRNO_SOURCE_WSA; if (error == WSAEINPROGRESS || error == WSAEALREADY || error == WSAEWOULDBLOCK) { #endif /* AIO4C_WIN32 */ newState = AIO4C_CONNECTION_STATE_CONNECTING; } else { return _ConnectionHandleError(connection, AIO4C_LOG_LEVEL_ERROR, AIO4C_CONNECT_ERROR, &code); } } else { newState = AIO4C_CONNECTION_STATE_CONNECTED; } ConnectionState(connection, newState); return connection; } Connection* ConnectionFinishConnect(Connection* connection) { ErrorCode code = AIO4C_ERROR_CODE_INITIALIZER; int soError = 0; socklen_t soSize = sizeof(int); #ifdef AIO4C_HAVE_POLL aio4c_poll_t polls[1] = { { .fd = connection->socket, .events = POLLOUT, .revents = 0 } }; #ifndef AIO4C_WIN32 if (poll(polls, 1, -1) == -1) { code.error = errno; #else /* AIO4C_WIN32 */ if (WSAPoll(polls, 1, -1) == SOCKET_ERROR) { code.source = AIO4C_ERRNO_SOURCE_WSA; #endif /* AIO4C_WIN32 */ return _ConnectionHandleError(connection, AIO4C_LOG_LEVEL_ERROR, AIO4C_POLL_ERROR, &code); } if (polls[0].revents > 0) { #else /* AIO4C_HAVE_POLL */ fd_set writeSet; fd_set errorSet; FD_ZERO(&writeSet); FD_ZERO(&errorSet); FD_SET(connection->socket, &writeSet); FD_SET(connection->socket, &errorSet); #ifndef AIO4C_WIN32 if (select(connection->socket + 1, NULL, &writeSet, &errorSet, NULL) == -1) { code.error = errno; #else /* AIO4C_WIN32 */ if (select(connection->socket + 1, NULL, &writeSet, &errorSet, NULL) == SOCKET_ERROR) { code.source = AIO4C_ERRNO_SOURCE_WSA; #endif /* AIO4C_WIN32 */ return _ConnectionHandleError(connection, AIO4C_LOG_LEVEL_ERROR, AIO4C_SELECT_ERROR, &code); } if (FD_ISSET(connection->socket, &writeSet) || FD_ISSET(connection->socket, &errorSet)) { #endif /* AIO4C_HAVE_POLL */ #ifndef AIO4C_WIN32 if (getsockopt(connection->socket, SOL_SOCKET, SO_ERROR, &soError, &soSize) != 0) { code.error = errno; #else /* AI4OC_WIN32 */ if (getsockopt(connection->socket, SOL_SOCKET, SO_ERROR, (char*)&soError, &soSize) == SOCKET_ERROR) { code.source = AIO4C_ERRNO_SOURCE_WSA; #endif /* AIO4C_WIN32 */ return _ConnectionHandleError(connection, AIO4C_LOG_LEVEL_ERROR, AIO4C_GETSOCKOPT_ERROR, &code); } if (soError != 0) { #ifndef AIO4C_WIN32 code.error = soError; #else /* AIO4C_WIN32 */ code.source = AIO4C_ERRNO_SOURCE_SOE; code.soError = soError; #endif /* AIO4C_WIN32 */ return _ConnectionHandleError(connection, AIO4C_LOG_LEVEL_ERROR, AIO4C_FINISH_CONNECT_ERROR, &code); } } return connection; } Connection* ConnectionRead(Connection* connection) { Buffer* buffer = NULL; ssize_t nbRead = 0; ErrorCode code = AIO4C_ERROR_CODE_INITIALIZER; aio4c_byte_t* data = NULL; buffer = connection->readBuffer; if (!BufferHasRemaining(buffer)) { code.buffer = buffer; return _ConnectionHandleError(connection, AIO4C_LOG_LEVEL_ERROR, AIO4C_BUFFER_OVERFLOW_ERROR, &code); } data = BufferGetBytes(buffer); if ((nbRead = recv(connection->socket, (void*)&data[BufferGetPosition(buffer)], BufferRemaining(buffer), 0)) < 0) { #ifndef AIO4C_WIN32 code.error = errno; #else /* AIO4C_WIN32 */ code.source = AIO4C_ERRNO_SOURCE_WSA; #endif /* AIO4C_WIN32 */ return _ConnectionHandleError(connection, AIO4C_LOG_LEVEL_ERROR, AIO4C_READ_ERROR, &code); } ProbeSize(AIO4C_PROBE_NETWORK_READ_SIZE, nbRead); if (nbRead == 0) { if (connection->state == AIO4C_CONNECTION_STATE_PENDING_CLOSE) { ConnectionState(connection, AIO4C_CONNECTION_STATE_CLOSED); return connection; } else { return _ConnectionHandleError(connection, AIO4C_LOG_LEVEL_INFO, AIO4C_CONNECTION_DISCONNECTED, &code); } } if (!connection->canRead) { Log(AIO4C_LOG_LEVEL_WARN, "received data on connection %s when reading is not allowed", connection->string); BufferReset(buffer); return connection; } BufferPosition(buffer, BufferGetPosition(buffer) + nbRead); _ConnectionEventHandle(connection, AIO4C_INBOUND_DATA_EVENT); return connection; }