Commit ce155a1a authored by Volker Krause's avatar Volker Krause Committed by Torsten Rahn
Browse files

Redo the communication code with the Tirex server

This is using a slightly weird setup with a 1:N UDP socket connection
for distributing work to multiple render processes. Qt's UDP code doesn't
really get along with that, and over time degenerates to only one of the
backends still being able to communicate.

To address this, bypass Qt entirely for the communication and do this
manually. That reduces portability, but that doesn't matter given that
Tirex and mod_tile aren't more portable either.
parent 1a082a3e
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <QDir> #include <QDir>
#include <QSettings> #include <QSettings>
#include <QSocketNotifier>
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
...@@ -18,14 +19,13 @@ TirexBackend::TirexBackend(QObject *parent) ...@@ -18,14 +19,13 @@ TirexBackend::TirexBackend(QObject *parent)
{ {
// setup command socket // setup command socket
const auto socketFd = getenv("TIREX_BACKEND_SOCKET_FILENO"); const auto socketFd = getenv("TIREX_BACKEND_SOCKET_FILENO");
if (!socketFd) { if (socketFd) {
m_commandSocketFd = std::atoi(socketFd);
m_socketNotifier = new QSocketNotifier(m_commandSocketFd, QSocketNotifier::Read, this);
connect(m_socketNotifier, &QSocketNotifier::activated, this, &TirexBackend::commandReadyRead);
} else {
qFatal("TIREX_BACKEND_SOCKET_FILENO not set!"); qFatal("TIREX_BACKEND_SOCKET_FILENO not set!");
} }
const auto cmdSocketResult = m_commandSocket.setSocketDescriptor(std::atoi(socketFd));
if (!cmdSocketResult) {
qFatal("Failed to use command socket fd: %s", qPrintable(m_commandSocket.errorString()));
}
connect(&m_commandSocket, &QUdpSocket::readyRead, this, &TirexBackend::commandReadyRead);
// setup heartbeat pipe and timer // setup heartbeat pipe and timer
const auto pipeFd = getenv("TIREX_BACKEND_PIPE_FILENO"); const auto pipeFd = getenv("TIREX_BACKEND_PIPE_FILENO");
...@@ -50,28 +50,35 @@ TirexBackend::~TirexBackend() = default; ...@@ -50,28 +50,35 @@ TirexBackend::~TirexBackend() = default;
void TirexBackend::commandReadyRead() void TirexBackend::commandReadyRead()
{ {
while (m_commandSocket.hasPendingDatagrams()) { while (true) {
m_renderTime.restart(); m_renderTime.restart();
const auto dgram = m_commandSocket.receiveDatagram();
TirexMetatileRequest req; TirexMetatileRequest req;
req.addrSize = sizeof(req.addr);
bzero(&req.addr, req.addrSize);
QByteArray data(0xffff, 0);
auto n = recvfrom(m_commandSocketFd, data.data(), 0xffff, MSG_DONTWAIT, reinterpret_cast<sockaddr*>(&req.addr), &req.addrSize);
if (n <= 0) {
break;
}
data.resize(n);
int nextIdx = 0; int nextIdx = 0;
const char *type = nullptr; const char *type = nullptr;
int typeLen = 0; int typeLen = 0;
while (nextIdx < dgram.data().size() && nextIdx >= 0) { while (nextIdx < data.size() && nextIdx >= 0) {
const auto endIdx = dgram.data().indexOf('\n', nextIdx); const auto endIdx = data.indexOf('\n', nextIdx);
if (endIdx < 0) { if (endIdx < 0) {
break; break;
} }
const auto midIdx = dgram.data().indexOf('=', nextIdx); const auto midIdx = data.indexOf('=', nextIdx);
if (midIdx < 0 || midIdx >= endIdx) { if (midIdx < 0 || midIdx >= endIdx) {
break; break;
} }
const auto key = dgram.data().constData() + nextIdx; const auto key = data.constData() + nextIdx;
const auto keyLen = midIdx - nextIdx; const auto keyLen = midIdx - nextIdx;
const auto value = dgram.data().constData() + midIdx + 1; const auto value = data.constData() + midIdx + 1;
const auto valueLen = endIdx - midIdx - 1; const auto valueLen = endIdx - midIdx - 1;
if (keyLen == 4 && std::strncmp(key, "type", 4) == 0) { if (keyLen == 4 && std::strncmp(key, "type", 4) == 0) {
...@@ -97,12 +104,10 @@ void TirexBackend::commandReadyRead() ...@@ -97,12 +104,10 @@ void TirexBackend::commandReadyRead()
errorMsg += "id=" + req.id + "\n"; errorMsg += "id=" + req.id + "\n";
errorMsg += "type=" + QByteArray(type, typeLen) + "\n"; errorMsg += "type=" + QByteArray(type, typeLen) + "\n";
errorMsg += "result=error\nerrormsg=unsupported requested\n"; errorMsg += "result=error\nerrormsg=unsupported requested\n";
auto msg = dgram.makeReply(errorMsg); sendto(m_commandSocketFd, errorMsg.constData(), errorMsg.size(), 0, reinterpret_cast<const sockaddr*>(&req.addr), req.addrSize);
m_commandSocket.writeDatagram(msg);
continue; continue;
} }
req.reply = dgram.makeReply({});
emit tileRequested(req); emit tileRequested(req);
} }
} }
...@@ -115,17 +120,13 @@ void TirexBackend::tileDone(const TirexMetatileRequest &req) ...@@ -115,17 +120,13 @@ void TirexBackend::tileDone(const TirexMetatileRequest &req)
+ "\nz=" + QByteArray::number(req.tile.z) + "\nz=" + QByteArray::number(req.tile.z)
+ "\nmetatile=" + metatileFileName(req).toUtf8() + "\nmetatile=" + metatileFileName(req).toUtf8()
+ "\nrender_time=" + QByteArray::number(m_renderTime.elapsed()) + "\n"; + "\nrender_time=" + QByteArray::number(m_renderTime.elapsed()) + "\n";
auto reply = std::move(req.reply); sendto(m_commandSocketFd, msg.constData(), msg.size(), 0, reinterpret_cast<const sockaddr*>(&req.addr), req.addrSize);
reply.setData(msg);
m_commandSocket.writeDatagram(reply);
} }
void TirexBackend::tileError(const TirexMetatileRequest &req, const QString &errMsg) void TirexBackend::tileError(const TirexMetatileRequest &req, const QString &errMsg)
{ {
QByteArray msg = "id=" + req.id + "\ntype=metatile_render_request\nresult=error\nerrmsg=" + errMsg.toUtf8() + "\n"; QByteArray msg = "id=" + req.id + "\ntype=metatile_render_request\nresult=error\nerrmsg=" + errMsg.toUtf8() + "\n";
auto reply = std::move(req.reply); sendto(m_commandSocketFd, msg.constData(), msg.size(), 0, reinterpret_cast<const sockaddr*>(&req.addr), req.addrSize);
reply.setData(msg);
m_commandSocket.writeDatagram(reply);
} }
QVariant TirexBackend::configValue(const QString &key) const QVariant TirexBackend::configValue(const QString &key) const
......
...@@ -11,7 +11,10 @@ ...@@ -11,7 +11,10 @@
#include <QNetworkDatagram> #include <QNetworkDatagram>
#include <QObject> #include <QObject>
#include <QTimer> #include <QTimer>
#include <QUdpSocket>
#include <netinet/in.h>
class QSocketNotifier;
/** A Tirex meta tile. */ /** A Tirex meta tile. */
class TirexMetatile class TirexMetatile
...@@ -29,7 +32,8 @@ public: ...@@ -29,7 +32,8 @@ public:
TirexMetatile tile; TirexMetatile tile;
QByteArray id; QByteArray id;
QByteArray map; QByteArray map;
QNetworkDatagram reply; sockaddr_in addr;
socklen_t addrSize;
}; };
...@@ -76,7 +80,8 @@ Q_SIGNALS: ...@@ -76,7 +80,8 @@ Q_SIGNALS:
private: private:
void commandReadyRead(); void commandReadyRead();
QUdpSocket m_commandSocket; int m_commandSocketFd = -1;
QSocketNotifier *m_socketNotifier = nullptr;
QTimer m_heartbeatTimer; QTimer m_heartbeatTimer;
QString m_tileDir; QString m_tileDir;
QElapsedTimer m_renderTime; QElapsedTimer m_renderTime;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment