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
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
==========================================

View File

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

View File

@@ -27,6 +27,10 @@ void CmdParser::Parse(std::string s) {
// taking s by value we can modify it.
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
// with h when combined with detector id (ex, 1-hostname)
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);
if (shm()->useReceiverFlag) {
MacAddr retval(0LU);
MacAddr retval;
sendToReceiver(F_SET_RECEIVER_UDP_IP, ip, retval);
LOG(logINFO) << "Setting destination udp mac of Module " << moduleIndex
<< " to " << retval;
@@ -1225,7 +1225,7 @@ void Module::setDestinationUDPIP2(const IpAddr ip) {
}
sendToDetector(F_SET_DEST_UDP_IP2, ip, nullptr);
if (shm()->useReceiverFlag) {
MacAddr retval(0LU);
MacAddr retval;
sendToReceiver(F_SET_RECEIVER_UDP_IP2, ip, retval);
LOG(logINFO) << "Setting destination udp mac2 of Module " << moduleIndex
<< " to " << retval;

View File

@@ -25,15 +25,27 @@
#include <sys/stat.h> // fstat
#include <unistd.h>
namespace sls {
struct CtbConfig;
// struct sharedDetector;
// ********************** Defines for shared memory. **********************
// WARNING! before chaning these search the codebase for their usage!
#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_MODULE_PREFIX "_module_"
#endif
#define SHM_ENV_NAME "SLSDETNAME"
// ************************************************************************
namespace sls {
class CtbConfig;
template <typename T, typename U> constexpr bool is_type() {
return std::is_same_v<std::decay_t<U>, T>;
@@ -267,6 +279,11 @@ template <typename T> class SharedMemory {
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 expected_size = sizeof(T);
if (actual_size != expected_size) {

View File

@@ -38,10 +38,16 @@ void freeShm(const int dindex, const int mIndex) {
}
constexpr int shm_id = 10;
//macOS does not expose shm in the filesystem
#ifndef __APPLE__
const std::string file_path =
std::string("/dev/shm/slsDetectorPackage_detector_") +
std::to_string(shm_id);
TEST_CASE("Free obsolete (without isValid)", "[detector][shm]") {
// 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]") {
SharedMemory<Data> shm(shm_id, -1);
if (shm.exists()) {
@@ -96,9 +104,9 @@ TEST_CASE("Create SharedMemory read and write", "[detector][shm]") {
}
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)) : "";
CHECK(shm.getName() == std::string("/slsDetectorPackage_detector_") +
CHECK(shm.getName() == std::string(SHM_DETECTOR_PREFIX) +
std::to_string(shm_id) + env_name);
shm()->x = 3;
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]") {
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)) : "";
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);
shm.createSharedMemory();
shm()->x = 9;
@@ -183,13 +191,13 @@ TEST_CASE("Move SharedMemory", "[detector][shm]") {
CHECK(shm2()->x == 9);
REQUIRE_THROWS(
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);
shm2.removeSharedMemory();
}
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)) : "";
constexpr int N = 5;
@@ -205,7 +213,7 @@ TEST_CASE("Create several shared memories", "[detector][shm]") {
for (int i = 0; i != N; ++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);
}
@@ -216,12 +224,12 @@ TEST_CASE("Create several shared memories", "[detector][shm]") {
}
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)) : "";
SharedMemory<Data> shm(0, -1, "ctbdacs");
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") {
@@ -235,7 +243,7 @@ TEST_CASE("Create create a shared memory with a tag when SLSDETNAME is set") {
setenv(SHM_ENV_NAME, "myprefix", 1);
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
if (old_slsdetname.empty())

View File

@@ -10,11 +10,16 @@
#include <ios>
#include <iostream>
#include <libgen.h> // dirname
#include <limits.h>
#include <sstream>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h> //readlink
#if defined(__APPLE__)
#include <mach-o/dyld.h>
#endif
namespace sls {
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) {
if (fname[0] == '/') {
return fname;
}
// get path of current binary
char path[MAX_STR_LENGTH];
memset(path, 0, MAX_STR_LENGTH);
ssize_t len = readlink("/proc/self/exe", path, MAX_STR_LENGTH - 1);
//in case PATH_MAX defines the longest possible path on linux and macOS
//use string instead of char array to avoid overflow
std::string path(PATH_MAX, '\0');
#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) {
throw RuntimeError("Could not get absolute path for " + fname);
}
path[len] = '\0';
#endif
// 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;
}

View File

@@ -16,7 +16,6 @@
#include <netdb.h>
#include <sstream>
#include <sys/ioctl.h>
#include <sys/prctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
@@ -178,6 +177,12 @@ IpAddr InterfaceNameToIp(const std::string &ifn) {
}
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!
struct ifreq ifr;
char mac[32];
@@ -203,6 +208,7 @@ MacAddr InterfaceNameToMac(const std::string &inf) {
close(sock);
}
return MacAddr(mac);
#endif
}
void validatePortNumber(uint16_t port) {

View File

@@ -72,6 +72,10 @@ TEST_CASE("Receive data from a vector") {
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") {
constexpr int port = 50001;
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
// if the socket is left open we would block
std::future<bool> ret =
std::async(&UdpRxSocket::ReceivePacket, &s, (char *)&buff);
std::async(std::launch::async, &UdpRxSocket::ReceivePacket, &s, (char *)&buff);
s.Shutdown();
auto r = ret.get();
CHECK(r == false); // since we didn't get the packet
}
#endif
TEST_CASE("Too small packet") {
constexpr int port = 50001;

View File

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