diff --git a/CMakeLists.txt b/CMakeLists.txt index f136e5c..93dfab7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,6 +26,7 @@ add_executable(datum_gateway src/datum_jsonrpc.c src/datum_logger.c src/datum_protocol.c + src/portable_mutex.c src/datum_queue.c src/datum_sockets.c src/datum_stratum.c @@ -79,6 +80,16 @@ endif() set(ARGP_LIBS "") check_function_exists(argp_parse HAVE_ARGP_PARSE) +if(APPLE AND NOT HAVE_ARGP_PARSE) + find_library(ARGP_LIBRARY NAMES argp libargp HINTS /opt/homebrew/lib) + if(ARGP_LIBRARY) + list(APPEND ARGP_LIBS ${ARGP_LIBRARY}) + include_directories(SYSTEM /opt/homebrew/include) + else() + message(FATAL_ERROR "libargp not found. Please install it with `brew install argp-standalone`.") + endif() +endif() + if(NOT HAVE_ARGP_PARSE) check_library_exists(argp argp_parse "" ARGP) if(NOT ARGP AND CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") @@ -91,12 +102,39 @@ if(NOT HAVE_ARGP_PARSE) endif() endif() +# Ensure pkg-config can find Homebrew-installed packages on macOS +if(APPLE) + set(ENV{PKG_CONFIG_PATH} "/opt/homebrew/lib/pkgconfig:$ENV{PKG_CONFIG_PATH}") +endif() + check_function_exists(epoll_wait HAVE_EPOLL_WAIT) if(HAVE_EPOLL_WAIT) set(EPOLL_SHIM_INCLUDE_DIRS "") set(EPOLL_SHIM_LIBRARIES "") else() - pkg_check_modules(EPOLL_SHIM REQUIRED epoll-shim) + pkg_check_modules(EPOLL_SHIM REQUIRED epoll-shim) + if(APPLE) + if(NOT EPOLL_SHIM_FOUND) + message(FATAL_ERROR "epoll-shim not found. Install it with `brew install epoll-shim`.") + endif() + # Add include directories + target_include_directories(datum_gateway + PUBLIC + ${EPOLL_SHIM_INCLUDE_DIRS} + ) + + # Add link directories + target_link_directories(datum_gateway + PUBLIC + ${EPOLL_SHIM_LIBRARY_DIRS} + ) + + # Add linker flags (e.g. -L... -lepoll-shim) + target_link_libraries(datum_gateway + PUBLIC + ${EPOLL_SHIM_LDFLAGS} + ) + endif() endif() cmake_pop_check_state() diff --git a/src/datum_protocol.c b/src/datum_protocol.c index 76dbc42..70a9ee8 100644 --- a/src/datum_protocol.c +++ b/src/datum_protocol.c @@ -75,6 +75,10 @@ #include "datum_queue.h" #include "git_version.h" + +#ifdef __APPLE__ +#include "portable_mutex.h" +#endif atomic_int datum_protocol_client_active = 0; DATUM_ENC_KEYS local_datum_keys; @@ -265,6 +269,11 @@ int datum_protocol_mining_cmd(void *data, int len) { } pthread_mutex_t datum_protocol_coinbaser_fetch_mutex = PTHREAD_MUTEX_INITIALIZER; +#ifdef __APPLE__ +// Declare the state for the timed mutex only for Apple +static timed_mutex_state_t datum_protocol_coinbaser_fetch_state; +#endif + pthread_cond_t datum_protocol_coinbaser_fetch_cond = PTHREAD_COND_INITIALIZER; unsigned char datum_coinbaser_v2_response_buf[2][32768] = { 0 }; unsigned char *datum_coinbaser_v2_response = NULL; @@ -293,8 +302,11 @@ int datum_protocol_coinbaser_fetch_response(int len, unsigned char *data) { DLOG_DEBUG("Invalid coinbaser received! %lu %lu", (unsigned long)x, (unsigned long)(len-12)); return 0; } - - rc = pthread_mutex_timedlock(&datum_protocol_coinbaser_fetch_mutex, &ts); + #ifdef __APPLE__ + rc = apple_mutex_timedlock(&datum_protocol_coinbaser_fetch_mutex, &datum_protocol_coinbaser_fetch_state, &ts); + #else + rc = pthread_mutex_timedlock(&datum_protocol_coinbaser_fetch_mutex, &ts); + #endif if (rc != 0) { DLOG_DEBUG("Could not get a lock on the coinbaser reception mutex after 5 seconds... bug?"); return 0; @@ -312,8 +324,12 @@ int datum_protocol_coinbaser_fetch_response(int len, unsigned char *data) { datum_coinbaser_v2_response_len[datum_coinbaser_v2_response_buf_idx] = x; pthread_cond_signal(&datum_protocol_coinbaser_fetch_cond); // Signal the condition variable - pthread_mutex_unlock(&datum_protocol_coinbaser_fetch_mutex); - + + #ifdef __APPLE__ + apple_mutex_unlock(&datum_protocol_coinbaser_fetch_mutex, &datum_protocol_coinbaser_fetch_state); + #else + pthread_mutex_unlock(&datum_protocol_coinbaser_fetch_mutex); + #endif return 1; } @@ -356,19 +372,29 @@ int datum_protocol_coinbaser_fetch(void *sptr) { // spin here for up to 5 seconds while awaiting a coinbaser response from DATUM Prime clock_gettime(CLOCK_REALTIME, &ts); ts.tv_sec += 5; // Set timeout to 5 seconds - - pthread_mutex_lock(&datum_protocol_coinbaser_fetch_mutex); - + #ifdef __APPLE__ + apple_mutex_lock(&datum_protocol_coinbaser_fetch_mutex, &datum_protocol_coinbaser_fetch_state); + #else + pthread_mutex_lock(&datum_protocol_coinbaser_fetch_mutex); + #endif rc = pthread_cond_timedwait(&datum_protocol_coinbaser_fetch_cond, &datum_protocol_coinbaser_fetch_mutex, &ts); if (rc == ETIMEDOUT) { - pthread_mutex_unlock(&datum_protocol_coinbaser_fetch_mutex); + #ifdef __APPLE__ + apple_mutex_unlock(&datum_protocol_coinbaser_fetch_mutex, &datum_protocol_coinbaser_fetch_state); + #else + pthread_mutex_unlock(&datum_protocol_coinbaser_fetch_mutex); + #endif DLOG_DEBUG("Timeout waiting for coinbaser response from DATUM Prime"); return 0; } if (rc != 0) { DLOG_DEBUG("Error waiting for coinbaser response from DATUM Prime"); - pthread_mutex_unlock(&datum_protocol_coinbaser_fetch_mutex); + #ifdef __APPLE__ + apple_mutex_unlock(&datum_protocol_coinbaser_fetch_mutex, &datum_protocol_coinbaser_fetch_state); + #else + pthread_mutex_unlock(&datum_protocol_coinbaser_fetch_mutex); + #endif return 0; } i = 0; @@ -377,8 +403,11 @@ int datum_protocol_coinbaser_fetch(void *sptr) { if ((datum_coinbaser_v2_response) && (datum_coinbaser_v2_response_value[datum_coinbaser_v2_response_buf_idx] == value)) { i = datum_coinbaser_v2_parse(s, datum_coinbaser_v2_response, datum_coinbaser_v2_response_len[datum_coinbaser_v2_response_buf_idx], false); } - - pthread_mutex_unlock(&datum_protocol_coinbaser_fetch_mutex); + #ifdef __APPLE__ + apple_mutex_unlock(&datum_protocol_coinbaser_fetch_mutex, &datum_protocol_coinbaser_fetch_state); + #else + pthread_mutex_unlock(&datum_protocol_coinbaser_fetch_mutex); + #endif return i; } @@ -1913,7 +1942,9 @@ int datum_protocol_init(void) { DLOG_WARN("****************************************************"); return 0; } - + #ifdef __APPLE__ + apple_timed_mutex_init(&datum_protocol_coinbaser_fetch_state); + #endif if (sodium_init() < 0) { DLOG_FATAL("libsodium initialization failed"); return -1; diff --git a/src/portable_mutex.c b/src/portable_mutex.c new file mode 100644 index 0000000..530b923 --- /dev/null +++ b/src/portable_mutex.c @@ -0,0 +1,48 @@ +#include "portable_mutex.h" + +#ifdef __APPLE__ +void apple_timed_mutex_init(timed_mutex_state_t *state) { + pthread_cond_init(&state->cond, NULL); + state->locked = 0; +} + +int apple_mutex_timedlock(pthread_mutex_t *mutex, timed_mutex_state_t *state, const struct timespec *abstime) { + int rc; + pthread_mutex_lock(mutex); + while (state->locked) { + rc = pthread_cond_timedwait(&state->cond, mutex, abstime); + if (rc == ETIMEDOUT) { + pthread_mutex_unlock(mutex); + return ETIMEDOUT; + } + if (rc != 0) { + pthread_mutex_unlock(mutex); + return rc; + } + } + state->locked = 1; + pthread_mutex_unlock(mutex); + return 0; +} + +void apple_mutex_lock(pthread_mutex_t *mutex, timed_mutex_state_t *state) { + pthread_mutex_lock(mutex); + while (state->locked) { + pthread_cond_wait(&state->cond, mutex); + } + state->locked = 1; + pthread_mutex_unlock(mutex); +} + +void apple_mutex_unlock(pthread_mutex_t *mutex, timed_mutex_state_t *state) { + pthread_mutex_lock(mutex); + state->locked = 0; + pthread_cond_signal(&state->cond); + pthread_mutex_unlock(mutex); +} + +void apple_timed_mutex_destroy(timed_mutex_state_t *state) { + pthread_cond_destroy(&state->cond); +} +#endif // __APPLE__ + diff --git a/src/portable_mutex.h b/src/portable_mutex.h new file mode 100644 index 0000000..a229c3b --- /dev/null +++ b/src/portable_mutex.h @@ -0,0 +1,24 @@ +#ifndef PORTABLE_MUTEX_H +#define PORTABLE_MUTEX_H + +#ifdef __APPLE__ +#include +#include +#include + +// A struct to manage the state for a timed mutex on Apple +typedef struct { + pthread_cond_t cond; + int locked; +} timed_mutex_state_t; + +// State management functions for timed mutexes on Apple +void apple_timed_mutex_init(timed_mutex_state_t *state); +int apple_mutex_timedlock(pthread_mutex_t *mutex, timed_mutex_state_t *state, const struct timespec *abstime); +void apple_mutex_lock(pthread_mutex_t *mutex, timed_mutex_state_t *state); +void apple_mutex_unlock(pthread_mutex_t *mutex, timed_mutex_state_t *state); +void apple_timed_mutex_destroy(timed_mutex_state_t *state); +#endif // __APPLE__ + +#endif // PORTABLE_MUTEX_H +