receiver on mac + gui style
Build on RHEL9 docker image / build (push) Successful in 3m55s
Build on RHEL8 docker image / build (push) Successful in 5m6s
Run Simulator Tests on local RHEL9 / build (push) Successful in 20m40s
Run Simulator Tests on local RHEL8 / build (push) Successful in 24m6s

This commit is contained in:
Erik Fröjdh committed 2026-10-26 20:57:03 +01:00
1 parent d438b37b64
commit 4461951f18
8 files changed
+141 -71

No files matched your search

+7
View File
@@ -18,6 +18,7 @@
#include <QFileDialog>
#include <QResizeEvent>
#include <QSizePolicy>
#include <QStyleFactory>
#include "sls/Version.h"
#include <getopt.h>
@@ -100,6 +101,12 @@ int main(int argc, char **argv) {
}
QApplication app(argc, argv);
// Use the same (platform independent) widget style everywhere so that the
// gui looks the same on linux and macOS. Fusion is built into Qt.
// Can still be overridden with -style <name> or QT_STYLE_OVERRIDE.
if (QStyleFactory::keys().contains("Fusion", Qt::CaseInsensitive)) {
app.setStyle(QStyleFactory::create("Fusion"));
}
try {
sls::qDetectorMain det(multiId, fname, isDeveloper);
det.show();
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: LGPL-3.0-or-other
// Copyright (C) 2021 Contributors to the SLS Detector Package
#include "ClientInterface.h"
#include "sls/ClientSocket.h"
#include "sls/ServerSocket.h"
#include "sls/StaticVector.h"
#include "sls/ToString.h"
@@ -34,6 +35,16 @@ ClientInterface::~ClientInterface() {
killTcpThread = true;
LOG(logINFO) << "Shutting down TCP Socket on port " << portNumber;
server.shutdown();
#ifdef __APPLE__
// On macOS shutdown() on a listening socket fails with ENOTCONN and does
// not wake up the blocking accept() in the tcp thread. Connect to our own
// port so that accept() returns and the thread sees killTcpThread.
try {
ReceiverSocket wakeup("localhost", portNumber);
} catch (const RuntimeError &e) {
LOG(logWARNING) << "Could not wake up TCP server thread: " << e.what();
}
#endif
LOG(logDEBUG) << "TCP Socket closed on port " << portNumber;
tcpThread->join();
}
@@ -82,6 +93,9 @@ void ClientInterface::startTCPServer() {
while (!killTcpThread) {
try {
auto socket = server.accept();
if (killTcpThread) {
break;
}
try {
verifyLock(); // lock should be checked only for set (not get),
// Move it back?
@@ -20,7 +20,6 @@
#include <cstring>
#include <mutex>
#include <ostream>
#include <semaphore.h>
#include <sys/socket.h>
#include <sys/wait.h> //wait
#include <thread>
@@ -489,19 +488,8 @@ void GetDataCallback(slsDetectorDefs::sls_receiver_header &header,
stat->available.release();
}
std::vector<sem_t> semaphores;
/**
* Control+C Interrupt Handler
* to let all the processes know to exit properly
* Only the main thread will call this handler
*/
void sigInterruptHandler(int p) {
(void)signal; // suppress unused warning if needed
for (auto &s : semaphores) {
sem_post(&s);
}
}
/** Released once per receiver thread by the main thread on Ctrl+C */
sls::counting_semaphore<> stopSemaphore{0};
int main(int argc, char *argv[]) {
CommandLineOptions cli(AppType::FrameSynchronizer);
@@ -519,16 +507,12 @@ int main(int argc, char *argv[]) {
LOG(sls::logINFOBLUE) << "Current Process [ Tid: " << sls::getThreadId()
<< ']';
// close files on ctrl+c
sls::setupSignalHandler(SIGINT, sigInterruptHandler);
// close files on ctrl+c: SIGINT is blocked (before any thread is created,
// so they all inherit the mask) and waited for in the main thread
sls::blockSignal(SIGINT);
// handle locally on socket crash
sls::setupSignalHandler(SIGPIPE, SIG_IGN);
semaphores.resize(f.numReceivers);
for (auto &s : semaphores) {
sem_init(&s, 0, 0);
}
FrameStatus stat{true, false, f.numReceivers};
// store pointer for signal handler
global_frame_status = &stat;
@@ -540,9 +524,7 @@ int main(int argc, char *argv[]) {
std::exception_ptr threadException = nullptr;
for (int i = 0; i != f.numReceivers; ++i) {
uint16_t port = f.port + i;
sem_t *semaphore = &semaphores[i];
threads.emplace_back([i, semaphore, port, user_data,
&threadException]() {
threads.emplace_back([i, port, user_data, &threadException]() {
LOG(sls::logINFOBLUE)
<< "Thread " << i << " [ Tid: " << sls::getThreadId() << ']';
try {
@@ -555,24 +537,27 @@ int main(int argc, char *argv[]) {
user_data);
/** - as long as no Ctrl+C */
// each child shares the common semaphore
sem_wait(semaphore);
stopSemaphore.acquire();
} catch (...) {
// capture exception and raise SIGINT to exit gracefully
// capture exception and send SIGINT to the process (not
// raise(), which would target this thread where it is
// blocked) so the main thread shuts everything down
threadException = std::current_exception();
raise(SIGINT);
kill(getpid(), SIGINT);
}
LOG(sls::logINFOBLUE) << "Exiting Thread " << i
<< " [ Tid: " << sls::getThreadId() << " ]";
});
}
LOG(sls::logINFO) << "[ Press \'Ctrl+c\' to exit ]";
sls::waitForSignal(SIGINT);
stopSemaphore.release(f.numReceivers);
for (auto &t : threads) {
t.join();
}
for (auto &s : semaphores)
sem_destroy(&s);
cleanup();
{
+4 -21
View File
@@ -13,7 +13,6 @@
#include <csignal> //SIGINT
#include <cstring>
#include <semaphore.h>
#include <sys/wait.h> //wait
#include <unistd.h>
@@ -125,18 +124,6 @@ void GetData(slsDetectorDefs::sls_receiver_header &header,
// imageSize = 26000;
}
sem_t semaphore;
/**
* Control+C Interrupt Handler
* to let all the processes know to exit properly
* All child processes will call the handler (parent process set to ignore)
*/
void sigInterruptHandler(int signal) {
(void)signal; // suppress unused warning if needed
sem_post(&semaphore);
}
int main(int argc, char *argv[]) {
CommandLineOptions cli(AppType::MultiReceiver);
@@ -154,13 +141,12 @@ int main(int argc, char *argv[]) {
LOG(sls::logINFOBLUE) << "Current Process [ Tid: " << sls::getThreadId()
<< ']';
// close files on ctrl+c
sls::setupSignalHandler(SIGINT, sigInterruptHandler);
// close files on ctrl+c: SIGINT is blocked before forking so that every
// child process inherits the mask and can wait for it synchronously
sls::blockSignal(SIGINT);
// handle locally on socket crash
sls::setupSignalHandler(SIGPIPE, SIG_IGN);
sem_init(&semaphore, 1, 0);
/** - loop over receivers */
for (int i = 0; i < m.numReceivers; ++i) {
@@ -206,15 +192,12 @@ int main(int argc, char *argv[]) {
}
/** - as long as no Ctrl+C */
// each child process gets a copy of the semaphore
sem_wait(&semaphore);
sem_destroy(&semaphore);
sls::waitForSignal(SIGINT);
LOG(sls::logINFOBLUE)
<< "Exiting Child Process [ Tid: " << sls::getThreadId()
<< ']';
exit(EXIT_SUCCESS);
} catch (...) {
sem_destroy(&semaphore);
LOG(sls::logINFOBLUE)
<< "Exiting Child Process [ Tid: " << sls::getThreadId()
<< " ]";
+4 -19
View File
@@ -11,20 +11,8 @@
#include "sls/thread_utils.h"
#include <csignal> //SIGINT
#include <semaphore.h>
#include <unistd.h>
sem_t semaphore;
/**
* Control+C Interrupt Handler
* to let all the other process know to exit properly
*/
void sigInterruptHandler(int signal) {
(void)signal; // suppress unused warning if needed
sem_post(&semaphore);
}
int main(int argc, char *argv[]) {
CommandLineOptions cli(AppType::SingleReceiver);
@@ -42,20 +30,17 @@ int main(int argc, char *argv[]) {
LOG(sls::logINFOBLUE) << "Current Process [ Tid: " << sls::getThreadId()
<< " ]";
// close files on ctrl+c
sls::setupSignalHandler(SIGINT, sigInterruptHandler);
// close files on ctrl+c: SIGINT is blocked (before the receiver threads
// are created, so they inherit the mask) and waited for synchronously
sls::blockSignal(SIGINT);
// handle locally on socket crash
sls::setupSignalHandler(SIGPIPE, SIG_IGN);
sem_init(&semaphore, 1, 0);
try {
sls::Receiver r(o.port);
LOG(sls::logINFO) << "[ Press \'Ctrl+c\' to exit ]";
sem_wait(&semaphore);
sem_destroy(&semaphore);
sls::waitForSignal(SIGINT);
} catch (...) {
sem_destroy(&semaphore);
LOG(sls::logINFOBLUE)
<< "Exiting [ Tid: " << sls::getThreadId() << " ]";
throw;
@@ -91,4 +91,13 @@ IpAddr InterfaceNameToIp(const std::string &ifn);
void validatePortNumber(uint16_t port);
void validatePortRange(uint16_t startPort, int numPorts);
void setupSignalHandler(int signal, void (*handler)(int));
/** Block a signal in the calling thread. Threads created afterwards (and
* child processes) inherit the mask, so call this before spawning them. */
void blockSignal(int signal);
/** Wait synchronously until the (previously blocked) signal is delivered to
* the process. Replaces the signal handler + unnamed POSIX semaphore
* (sem_init) pattern, which is not implemented on macOS. */
void waitForSignal(int signal);
} // namespace sls
+31
View File
@@ -9,6 +9,7 @@
#include <iostream>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
@@ -91,5 +92,35 @@ void UdpRxSocket::setBufferSize(int size) {
void UdpRxSocket::Shutdown() {
// not closing yet on purpose, but read gives -1
shutdown(sockfd_, SHUT_RDWR);
#ifdef __APPLE__
// On macOS shutdown() on an unconnected UDP socket fails with ENOTCONN
// and does not wake up a blocking recvfrom(). Send an empty datagram to
// our own port instead: it is read as 0 bytes, so ReceivePacket returns
// false and the listener thread sees that the socket is no longer alive.
sockaddr_storage addr{};
socklen_t addrlen = sizeof(addr);
if (getsockname(sockfd_, reinterpret_cast<sockaddr *>(&addr), &addrlen) ==
-1) {
return;
}
if (addr.ss_family == AF_INET) {
auto *a = reinterpret_cast<sockaddr_in *>(&addr);
if (a->sin_addr.s_addr == htonl(INADDR_ANY))
a->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
} else if (addr.ss_family == AF_INET6) {
auto *a = reinterpret_cast<sockaddr_in6 *>(&addr);
if (IN6_IS_ADDR_UNSPECIFIED(&a->sin6_addr))
a->sin6_addr = in6addr_loopback;
} else {
return;
}
int wakefd = socket(addr.ss_family, SOCK_DGRAM, 0);
if (wakefd == -1) {
return;
}
sendto(wakefd, nullptr, 0, 0, reinterpret_cast<sockaddr *>(&addr),
addrlen);
close(wakefd);
#endif
}
} // namespace sls
+57 -1
View File
@@ -6,6 +6,7 @@
#include <algorithm>
#include <arpa/inet.h>
#include <cassert>
#include <cerrno>
#include <csignal>
#include <cstdlib>
#include <cstring>
@@ -14,6 +15,10 @@
#include <limits>
#include <net/if.h>
#include <netdb.h>
#ifdef __APPLE__
#include <net/if_dl.h>
#endif
#include <pthread.h>
#include <sstream>
#include <sys/ioctl.h>
#include <sys/socket.h>
@@ -179,7 +184,33 @@ IpAddr InterfaceNameToIp(const std::string &ifn) {
MacAddr InterfaceNameToMac(const std::string &inf) {
#ifdef __APPLE__
throw RuntimeError("InterfaceNameToMac not implemented on macOS yet");
// No SIOCGIFHWADDR on macOS: the hardware address is the AF_LINK entry
// that getifaddrs() returns for the interface
struct ifaddrs *ifaddr = nullptr;
if (getifaddrs(&ifaddr) == -1) {
throw RuntimeError("getifaddrs failed [" +
std::string(strerror(errno)) + ']');
}
MacAddr result{};
for (auto *ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
if (ifa->ifa_addr == nullptr || ifa->ifa_addr->sa_family != AF_LINK ||
inf != ifa->ifa_name) {
continue;
}
auto *sdl = reinterpret_cast<struct sockaddr_dl *>(ifa->ifa_addr);
if (sdl->sdl_alen != 6) {
continue; // not an ethernet style address (e.g. utun, lo0)
}
auto *hw = reinterpret_cast<const unsigned char *>(LLADDR(sdl));
uint64_t addr = 0;
for (int j = 0; j < 6; ++j) {
addr = (addr << 8) | hw[j];
}
result = MacAddr(addr);
break;
}
freeifaddrs(ifaddr);
return result;
#else
// TODO! Copied from genericSocket needs to be refactored!
@@ -234,4 +265,29 @@ void setupSignalHandler(int signal, void (*handler)(int)) {
std::string(strsignal(signal)));
}
}
void blockSignal(int signal) {
sigset_t set;
sigemptyset(&set);
sigaddset(&set, signal);
if (pthread_sigmask(SIG_BLOCK, &set, nullptr) != 0) {
throw RuntimeError("Could not block " + std::string(strsignal(signal)));
}
}
void waitForSignal(int signal) {
sigset_t set;
sigemptyset(&set);
sigaddset(&set, signal);
int received = 0;
int ret = 0;
do {
ret = sigwait(&set, &received);
} while (ret == EINTR);
if (ret != 0) {
throw RuntimeError("Could not wait for " +
std::string(strsignal(signal)) + ": " +
strerror(ret));
}
}
} // namespace sls