Experimental support for using the client on macOS (Darwin) (#1321)
Some checks failed
Build on RHEL9 / build (push) Failing after 32s
Build on RHEL8 / build (push) Failing after 4m40s

* shorter SHM names on macOS
* fix segfault on macOS when string is empty
* apple version of read exe path
* ifdef for linux specific API
* fixed test for shm and udp socket
* updated release notes
This commit is contained in:
Erik Fröjdh
2025-10-22 15:19:36 +02:00
committed by GitHub
parent 9d40220274
commit 1d66f1d26d
10 changed files with 88 additions and 26 deletions

View File

@@ -33,7 +33,7 @@ against any of the libSls*.so libraries, you can enable this by passing
Added SLS_USE_SYSTEM_ZMQ option (default OFF) to use the libzmq of the host Added SLS_USE_SYSTEM_ZMQ option (default OFF) to use the libzmq of the host
instead of the one included in our repo. instead of the one included in our repo.
Experimental support for building the detector client (including python bindings) on macOS
2 On-board Detector Server Compatibility 2 On-board Detector Server Compatibility
========================================== ==========================================

View File

@@ -29,7 +29,6 @@ target_link_libraries(slsDetectorObject
slsProjectOptions slsProjectOptions
slsSupportStatic slsSupportStatic
pthread pthread
rt
PRIVATE PRIVATE
slsProjectWarnings slsProjectWarnings
) )

View File

@@ -27,6 +27,10 @@ void CmdParser::Parse(std::string s) {
// taking s by value we can modify it. // taking s by value we can modify it.
Reset(); Reset();
// If the string is empty there is nothing to parse
if (s.empty())
return;
// Are we looking at -h --help? avoid removing h from command starting // Are we looking at -h --help? avoid removing h from command starting
// with h when combined with detector id (ex, 1-hostname) // with h when combined with detector id (ex, 1-hostname)
bool h = replace_first(&s, "--help", " "); bool h = replace_first(&s, "--help", " ");

View File

@@ -1202,7 +1202,7 @@ void Module::setDestinationUDPIP(const IpAddr ip) {
} }
sendToDetector(F_SET_DEST_UDP_IP, ip, nullptr); sendToDetector(F_SET_DEST_UDP_IP, ip, nullptr);
if (shm()->useReceiverFlag) { if (shm()->useReceiverFlag) {
MacAddr retval(0LU); MacAddr retval;
sendToReceiver(F_SET_RECEIVER_UDP_IP, ip, retval); sendToReceiver(F_SET_RECEIVER_UDP_IP, ip, retval);
LOG(logINFO) << "Setting destination udp mac of Module " << moduleIndex LOG(logINFO) << "Setting destination udp mac of Module " << moduleIndex
<< " to " << retval; << " to " << retval;
@@ -1225,7 +1225,7 @@ void Module::setDestinationUDPIP2(const IpAddr ip) {
} }
sendToDetector(F_SET_DEST_UDP_IP2, ip, nullptr); sendToDetector(F_SET_DEST_UDP_IP2, ip, nullptr);
if (shm()->useReceiverFlag) { if (shm()->useReceiverFlag) {
MacAddr retval(0LU); MacAddr retval;
sendToReceiver(F_SET_RECEIVER_UDP_IP2, ip, retval); sendToReceiver(F_SET_RECEIVER_UDP_IP2, ip, retval);
LOG(logINFO) << "Setting destination udp mac2 of Module " << moduleIndex LOG(logINFO) << "Setting destination udp mac2 of Module " << moduleIndex
<< " to " << retval; << " to " << retval;

View File

@@ -25,15 +25,27 @@
#include <sys/stat.h> // fstat #include <sys/stat.h> // fstat
#include <unistd.h> #include <unistd.h>
namespace sls { // ********************** Defines for shared memory. **********************
// WARNING! before chaning these search the codebase for their usage!
struct CtbConfig;
// struct sharedDetector;
#define SHM_IS_VALID_CHECK_VERSION 0x250820 #define SHM_IS_VALID_CHECK_VERSION 0x250820
//Max shared memory name length in macOS is 31 characters
#ifdef __APPLE__
#define SHM_DETECTOR_PREFIX "/sls_"
#define SHM_MODULE_PREFIX "_mod_"
#else
#define SHM_DETECTOR_PREFIX "/slsDetectorPackage_detector_" #define SHM_DETECTOR_PREFIX "/slsDetectorPackage_detector_"
#define SHM_MODULE_PREFIX "_module_" #define SHM_MODULE_PREFIX "_module_"
#endif
#define SHM_ENV_NAME "SLSDETNAME" #define SHM_ENV_NAME "SLSDETNAME"
// ************************************************************************
namespace sls {
class CtbConfig;
template <typename T, typename U> constexpr bool is_type() { template <typename T, typename U> constexpr bool is_type() {
return std::is_same_v<std::decay_t<U>, T>; return std::is_same_v<std::decay_t<U>, T>;
@@ -267,6 +279,11 @@ template <typename T> class SharedMemory {
throw SharedMemoryError(msg); throw SharedMemoryError(msg);
} }
#ifdef __APPLE__
// On macOS, fstat returns the allocated size and not the requested size.
// This means we can't check for size since we always get for example 16384 bytes.
return;
#endif
auto actual_size = static_cast<size_t>(sb.st_size); auto actual_size = static_cast<size_t>(sb.st_size);
auto expected_size = sizeof(T); auto expected_size = sizeof(T);
if (actual_size != expected_size) { if (actual_size != expected_size) {

View File

@@ -38,10 +38,16 @@ void freeShm(const int dindex, const int mIndex) {
} }
constexpr int shm_id = 10; constexpr int shm_id = 10;
//macOS does not expose shm in the filesystem
#ifndef __APPLE__
const std::string file_path = const std::string file_path =
std::string("/dev/shm/slsDetectorPackage_detector_") + std::string("/dev/shm/slsDetectorPackage_detector_") +
std::to_string(shm_id); std::to_string(shm_id);
TEST_CASE("Free obsolete (without isValid)", "[detector][shm]") { TEST_CASE("Free obsolete (without isValid)", "[detector][shm]") {
// ensure its clean to start // ensure its clean to start
@@ -89,6 +95,8 @@ TEST_CASE("Free obsolete (without isValid)", "[detector][shm]") {
} }
} }
#endif
TEST_CASE("Create SharedMemory read and write", "[detector][shm]") { TEST_CASE("Create SharedMemory read and write", "[detector][shm]") {
SharedMemory<Data> shm(shm_id, -1); SharedMemory<Data> shm(shm_id, -1);
if (shm.exists()) { if (shm.exists()) {
@@ -96,9 +104,9 @@ TEST_CASE("Create SharedMemory read and write", "[detector][shm]") {
} }
shm.createSharedMemory(); shm.createSharedMemory();
const char *env_p = std::getenv("SLSDETNAME"); const char *env_p = std::getenv(SHM_ENV_NAME);
std::string env_name = env_p ? ("_" + std::string(env_p)) : ""; std::string env_name = env_p ? ("_" + std::string(env_p)) : "";
CHECK(shm.getName() == std::string("/slsDetectorPackage_detector_") + CHECK(shm.getName() == std::string(SHM_DETECTOR_PREFIX) +
std::to_string(shm_id) + env_name); std::to_string(shm_id) + env_name);
shm()->x = 3; shm()->x = 3;
shm()->y = 5.7; shm()->y = 5.7;
@@ -168,11 +176,11 @@ TEST_CASE("Open two shared memories to the same place", "[detector][shm]") {
} }
TEST_CASE("Move SharedMemory", "[detector][shm]") { TEST_CASE("Move SharedMemory", "[detector][shm]") {
const char *env_p = std::getenv("SLSDETNAME"); const char *env_p = std::getenv(SHM_ENV_NAME);
std::string env_name = env_p ? ("_" + std::string(env_p)) : ""; std::string env_name = env_p ? ("_" + std::string(env_p)) : "";
SharedMemory<Data> shm(shm_id, -1); SharedMemory<Data> shm(shm_id, -1);
CHECK(shm.getName() == std::string("/slsDetectorPackage_detector_") + CHECK(shm.getName() == std::string(SHM_DETECTOR_PREFIX) +
std::to_string(shm_id) + env_name); std::to_string(shm_id) + env_name);
shm.createSharedMemory(); shm.createSharedMemory();
shm()->x = 9; shm()->x = 9;
@@ -183,13 +191,13 @@ TEST_CASE("Move SharedMemory", "[detector][shm]") {
CHECK(shm2()->x == 9); CHECK(shm2()->x == 9);
REQUIRE_THROWS( REQUIRE_THROWS(
shm()); // trying to access should throw instead of returning a nullptr shm()); // trying to access should throw instead of returning a nullptr
CHECK(shm2.getName() == std::string("/slsDetectorPackage_detector_") + CHECK(shm2.getName() == std::string(SHM_DETECTOR_PREFIX) +
std::to_string(shm_id) + env_name); std::to_string(shm_id) + env_name);
shm2.removeSharedMemory(); shm2.removeSharedMemory();
} }
TEST_CASE("Create several shared memories", "[detector][shm]") { TEST_CASE("Create several shared memories", "[detector][shm]") {
const char *env_p = std::getenv("SLSDETNAME"); const char *env_p = std::getenv(SHM_ENV_NAME);
std::string env_name = env_p ? ("_" + std::string(env_p)) : ""; std::string env_name = env_p ? ("_" + std::string(env_p)) : "";
constexpr int N = 5; constexpr int N = 5;
@@ -205,7 +213,7 @@ TEST_CASE("Create several shared memories", "[detector][shm]") {
for (int i = 0; i != N; ++i) { for (int i = 0; i != N; ++i) {
CHECK(v[i]()->x == i); CHECK(v[i]()->x == i);
CHECK(v[i].getName() == std::string("/slsDetectorPackage_detector_") + CHECK(v[i].getName() == std::string(SHM_DETECTOR_PREFIX) +
std::to_string(i + shm_id) + env_name); std::to_string(i + shm_id) + env_name);
} }
@@ -216,12 +224,12 @@ TEST_CASE("Create several shared memories", "[detector][shm]") {
} }
TEST_CASE("Create create a shared memory with a tag") { TEST_CASE("Create create a shared memory with a tag") {
const char *env_p = std::getenv("SLSDETNAME"); const char *env_p = std::getenv(SHM_ENV_NAME);
std::string env_name = env_p ? ("_" + std::string(env_p)) : ""; std::string env_name = env_p ? ("_" + std::string(env_p)) : "";
SharedMemory<Data> shm(0, -1, "ctbdacs"); SharedMemory<Data> shm(0, -1, "ctbdacs");
REQUIRE(shm.getName() == REQUIRE(shm.getName() ==
"/slsDetectorPackage_detector_0" + env_name + "_ctbdacs"); std::string(SHM_DETECTOR_PREFIX) + "0" + env_name + "_ctbdacs");
} }
TEST_CASE("Create create a shared memory with a tag when SLSDETNAME is set") { TEST_CASE("Create create a shared memory with a tag when SLSDETNAME is set") {
@@ -235,7 +243,7 @@ TEST_CASE("Create create a shared memory with a tag when SLSDETNAME is set") {
setenv(SHM_ENV_NAME, "myprefix", 1); setenv(SHM_ENV_NAME, "myprefix", 1);
SharedMemory<Data> shm(0, -1, "ctbdacs"); SharedMemory<Data> shm(0, -1, "ctbdacs");
REQUIRE(shm.getName() == "/slsDetectorPackage_detector_0_myprefix_ctbdacs"); REQUIRE(shm.getName() == std::string(SHM_DETECTOR_PREFIX) + "0_myprefix_ctbdacs");
// Clean up after us // Clean up after us
if (old_slsdetname.empty()) if (old_slsdetname.empty())

View File

@@ -10,11 +10,16 @@
#include <ios> #include <ios>
#include <iostream> #include <iostream>
#include <libgen.h> // dirname #include <libgen.h> // dirname
#include <limits.h>
#include <sstream> #include <sstream>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
#include <unistd.h> //readlink #include <unistd.h> //readlink
#if defined(__APPLE__)
#include <mach-o/dyld.h>
#endif
namespace sls { namespace sls {
int readDataFile(std::ifstream &infile, short int *data, int nch, int offset) { int readDataFile(std::ifstream &infile, short int *data, int nch, int offset) {
@@ -246,21 +251,40 @@ std::vector<int> getChannelsFromFile(const std::string &fname) {
} }
std::string getAbsolutePathFromCurrentProcess(const std::string &fname) { std::string getAbsolutePathFromCurrentProcess(const std::string &fname) {
if (fname[0] == '/') { if (fname[0] == '/') {
return fname; return fname;
} }
// get path of current binary //in case PATH_MAX defines the longest possible path on linux and macOS
char path[MAX_STR_LENGTH]; //use string instead of char array to avoid overflow
memset(path, 0, MAX_STR_LENGTH); std::string path(PATH_MAX, '\0');
ssize_t len = readlink("/proc/self/exe", path, MAX_STR_LENGTH - 1);
#if defined(__APPLE__)
uint32_t size = PATH_MAX;
if (_NSGetExecutablePath(path.data(), &size) != 0) {
throw std::runtime_error("Failed to get executable path");
}
// Resolve any symlinks and .. components
std::string resolved(PATH_MAX, '\0');
if (!realpath(path.data(), resolved.data())) {
throw std::runtime_error("realpath failed for executable");
}
path = resolved;
#else
ssize_t len = readlink("/proc/self/exe", path.data(), PATH_MAX - 1);
if (len < 0) { if (len < 0) {
throw RuntimeError("Could not get absolute path for " + fname); throw RuntimeError("Could not get absolute path for " + fname);
} }
path[len] = '\0'; path[len] = '\0';
#endif
// get dir path and attach file name // get dir path and attach file name
std::string absPath = (std::string(dirname(path)) + '/' + fname); std::string absPath = (std::string(dirname(path.data())) + '/' + fname);
return absPath; return absPath;
} }

View File

@@ -16,7 +16,6 @@
#include <netdb.h> #include <netdb.h>
#include <sstream> #include <sstream>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/prctl.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/types.h> #include <sys/types.h>
#include <unistd.h> #include <unistd.h>
@@ -178,6 +177,12 @@ IpAddr InterfaceNameToIp(const std::string &ifn) {
} }
MacAddr InterfaceNameToMac(const std::string &inf) { MacAddr InterfaceNameToMac(const std::string &inf) {
#ifdef __APPLE__
throw RuntimeError(
"InterfaceNameToMac not implemented on macOS yet");
#else
// TODO! Copied from genericSocket needs to be refactored! // TODO! Copied from genericSocket needs to be refactored!
struct ifreq ifr; struct ifreq ifr;
char mac[32]; char mac[32];
@@ -203,6 +208,7 @@ MacAddr InterfaceNameToMac(const std::string &inf) {
close(sock); close(sock);
} }
return MacAddr(mac); return MacAddr(mac);
#endif
} }
void validatePortNumber(uint16_t port) { void validatePortNumber(uint16_t port) {

View File

@@ -72,6 +72,10 @@ TEST_CASE("Receive data from a vector") {
CHECK(data_to_send == data_received); CHECK(data_to_send == data_received);
} }
// TODO! Test blocking on apple, investigate when implementing
// receiver support in macOS
#ifndef __APPLE__
TEST_CASE("Shutdown socket without hanging when waiting for data") { TEST_CASE("Shutdown socket without hanging when waiting for data") {
constexpr int port = 50001; constexpr int port = 50001;
constexpr ssize_t packet_size = 8000; constexpr ssize_t packet_size = 8000;
@@ -81,13 +85,14 @@ TEST_CASE("Shutdown socket without hanging when waiting for data") {
// Start a thread and wait for package // Start a thread and wait for package
// if the socket is left open we would block // if the socket is left open we would block
std::future<bool> ret = std::future<bool> ret =
std::async(&UdpRxSocket::ReceivePacket, &s, (char *)&buff); std::async(std::launch::async, &UdpRxSocket::ReceivePacket, &s, (char *)&buff);
s.Shutdown(); s.Shutdown();
auto r = ret.get(); auto r = ret.get();
CHECK(r == false); // since we didn't get the packet CHECK(r == false); // since we didn't get the packet
} }
#endif
TEST_CASE("Too small packet") { TEST_CASE("Too small packet") {
constexpr int port = 50001; constexpr int port = 50001;

View File

@@ -25,7 +25,6 @@ target_link_libraries(tests
slsProjectOptions slsProjectOptions
slsSupportStatic slsSupportStatic
pthread pthread
rt
PRIVATE PRIVATE
slsProjectWarnings slsProjectWarnings
) )