fix: replace deprecated POSIX semaphores with pthread wrapper (#517)

## Summary

Fixes #516. Replaces deprecated unnamed POSIX semaphores with a portable
pthread-based wrapper. Eliminates all 9 remaining build warnings.

Depends on #514 (squashed single commit, rebased on latest develop).

## Approach

Spine uses semaphores as atomic counters (`sem_post` + `sem_getvalue` +
`sem_trywait`, no blocking `sem_wait`). A pthread mutex + counter
wrapper provides identical semantics on all platforms with zero
`#ifdef`s.

Alternatives considered:
- `dispatch_semaphore_t` (macOS GCD): no `getvalue` equivalent, requires
platform ifdefs
- C11 `_Atomic`: no wait/signal capability
- autotools detection: still requires fallback code

## Changes (1 commit)

- New `spine_sem.h`: inline `spine_sem_init`, `spine_sem_post`,
`spine_sem_getvalue`, `spine_sem_wait`, `spine_sem_trywait`,
`spine_sem_destroy`
- Replace `#include <semaphore.h>` with `#include "spine_sem.h"` in
`common.h`
- Update all `sem_t`/`sem_*` references in `spine.c`, `poller.c`,
`spine.h`
- Add `spine_sem.h` to `EXTRA_DIST`

## Warnings eliminated (9)

| File | Function | Count |
|------|----------|-------|
| `spine.c` | `sem_init` | 3 |
| `spine.c` | `sem_getvalue` | 4 |
| `poller.c` | `sem_getvalue` | 2 |

## Build result

Zero errors, zero warnings.

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
This commit is contained in:
Thomas Vincent authored and GitHub committed 2026-03-26 08:15:28 -04:00
1 parent 926d5b66af
commit c72b91b1be
6 files changed
+113 -25

No files matched your search

+1 -1
View File
@@ -31,7 +31,7 @@ bin_PROGRAMS = spine
man_MANS = spine.1
EXTRA_DIST = spine.1 uthash.h
EXTRA_DIST = spine.1 uthash.h spine_sem.h
# Docker targets — require Dockerfile and Dockerfile.dev (from PR #401)
.PHONY: docker docker-dev verify cppcheck
+1 -1
View File
@@ -87,7 +87,7 @@
#include <math.h>
#include <mysql.h>
#include <netdb.h>
#include <semaphore.h>
#include "spine_sem.h"
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
+6 -6
View File
@@ -44,20 +44,20 @@ void child_cleanup(void *arg) {
void child_cleanup_thread(void *arg) {
UNUSED_PARAMETER(arg);
sem_post(&available_threads);
spine_sem_post(&available_threads);
int a_threads_value;
sem_getvalue(&available_threads, &a_threads_value);
spine_sem_getvalue(&available_threads, &a_threads_value);
SPINE_LOG_DEVDBG(("DEBUG: Available Threads is %i (%i outstanding)", a_threads_value, set.threads - a_threads_value));
}
void child_cleanup_script(void *arg) {
UNUSED_PARAMETER(arg);
sem_post(&available_scripts);
spine_sem_post(&available_scripts);
int a_scripts_value;
sem_getvalue(&available_scripts, &a_scripts_value);
spine_sem_getvalue(&available_scripts, &a_scripts_value);
SPINE_LOG_DEVDBG(("DEBUG: Available Scripts is %i (%i outstanding)", a_scripts_value, MAX_SIMULTANEOUS_SCRIPTS - a_scripts_value));
}
@@ -99,7 +99,7 @@ void *child(void *arg) {
thread_mutex_unlock(LOCK_HOST_TIME);
/* Allows main thread to proceed with creation of other threads */
sem_post(poller_details.thread_init_sem);
spine_sem_post(poller_details.thread_init_sem);
if (is_debug_device(host_id)) {
SPINE_LOG(("DEBUG: Device[%i] HT[%i] In Poller, About to Start Polling", host_id, host_thread));
@@ -2330,7 +2330,7 @@ char *exec_poll(host_t *current_host, char *command, int id, const char *type) {
// use the script server timeout value, allow for 50% leeway
while (++retries < (set.script_timeout * 15)) {
sem_err = sem_trywait(&available_scripts);
sem_err = spine_sem_trywait(&available_scripts);
if (sem_err == 0) {
break;
} else if (sem_err == EAGAIN || sem_err == EWOULDBLOCK) {
+14 -14
View File
@@ -101,8 +101,8 @@
/* Global Variables */
int entries = 0;
int num_hosts = 0;
sem_t available_threads;
sem_t available_scripts;
spine_sem_t available_threads;
spine_sem_t available_scripts;
double start_time;
double total_time;
@@ -202,7 +202,7 @@ int main(int argc, char *argv[]) {
double host_time_double = 0;
int items_per_thread = 0;
int device_threads;
sem_t thread_init_sem;
spine_sem_t thread_init_sem;
int a_threads_value;
//struct timespec until_spec;
@@ -719,19 +719,19 @@ int main(int argc, char *argv[]) {
init_mutexes();
/* initialize available_threads semaphore */
sem_init(&available_threads, 0, set.threads);
spine_sem_init(&available_threads, set.threads);
/* initialize available_scripts semaphore */
sem_init(&available_scripts, 0, MAX_SIMULTANEOUS_SCRIPTS);
spine_sem_init(&available_scripts, MAX_SIMULTANEOUS_SCRIPTS);
/* initialize thread initialization semaphore */
sem_init(&thread_init_sem, 0, 1);
spine_sem_init(&thread_init_sem, 1);
/* specify the point of timeout for timedwait semaphores */
//until_spec.tv_sec = (time_t)(set.poller_interval + begin_time - 0.2);
//until_spec.tv_nsec = 0;
sem_getvalue(&available_threads, &a_threads_value);
spine_sem_getvalue(&available_threads, &a_threads_value);
SPINE_LOG_HIGH(("DEBUG: Initial Value of Available Threads is %i (%i outstanding)", a_threads_value, set.threads - a_threads_value));
/* tell fork processes that they are now active */
@@ -864,7 +864,7 @@ int main(int argc, char *argv[]) {
spine_timeout = FALSE;
while (TRUE) {
sem_err = sem_trywait(&available_threads);
sem_err = spine_sem_trywait(&available_threads);
if (sem_err == 0) {
/* acquired a thread */
@@ -906,7 +906,7 @@ int main(int argc, char *argv[]) {
loop_count = 0;
while (!spine_timeout) {
sem_err = sem_trywait(&thread_init_sem);
sem_err = spine_sem_trywait(&thread_init_sem);
if (sem_err == 0) {
// Acquired a thread
@@ -960,10 +960,10 @@ int main(int argc, char *argv[]) {
device_counter++;
}
sem_getvalue(&available_threads, &a_threads_value);
spine_sem_getvalue(&available_threads, &a_threads_value);
SPINE_LOG_HIGH(("DEBUG: Device[%i] Available Threads is %i (%i outstanding)", poller_details->host_id, a_threads_value, set.threads - a_threads_value));
sem_post(&thread_init_sem);
spine_sem_post(&thread_init_sem);
SPINE_LOG_DEVDBG(("DEBUG: DTS: device = %d, host_id = %d, host_thread = %d,"
" host_threads = %d, host_data_ids = %d, complete = %d",
@@ -984,12 +984,12 @@ int main(int argc, char *argv[]) {
/* Restore thread initialization semaphore if thread creation failed */
if (thread_status) {
thread_mutex_unlock(LOCK_HOST_TIME);
sem_post(&thread_init_sem);
spine_sem_post(&thread_init_sem);
}
}
}
sem_getvalue(&available_threads, &a_threads_value);
spine_sem_getvalue(&available_threads, &a_threads_value);
/* wait for all threads to 'complete'
* using the mutex here as the semaphore will
@@ -1004,7 +1004,7 @@ int main(int argc, char *argv[]) {
SPINE_LOG_HIGH(("NOTE: Polling sleeping while waiting for %d Threads to End", set.threads - a_threads_value));
usleep(500000);
sem_getvalue(&available_threads, &a_threads_value);
spine_sem_getvalue(&available_threads, &a_threads_value);
}
threads_final = set.threads - a_threads_value;
+3 -3
View File
@@ -499,7 +499,7 @@ typedef struct poller_thread {
int complete;
char host_time[40];
double host_time_double;
sem_t *thread_init_sem;
spine_sem_t *thread_init_sem;
} poller_thread_t;
/*! PHP Script Server Structure
@@ -630,8 +630,8 @@ extern config_t set;
extern php_t *php_processes;
extern char start_datetime[20];
extern char config_paths[CONFIG_PATHS][BUFSIZE];
extern sem_t available_threads;
extern sem_t available_scripts;
extern spine_sem_t available_threads;
extern spine_sem_t available_scripts;
extern pool_t *db_pool_remote;
extern pool_t *db_pool_local;
+88
View File
@@ -0,0 +1,88 @@
/*
+-------------------------------------------------------------------------+
| Copyright (C) 2004-2026 The Cacti Group |
| |
| This program is free software; you can redistribute it and/or |
| modify it under the terms of the GNU General Public License |
| as published by the Free Software Foundation; either version 2 |
| of the License, or (at your option) any later version. |
+-------------------------------------------------------------------------+
| Cacti: The Complete RRDtool-based Graphing Solution |
+-------------------------------------------------------------------------+
*/
#ifndef SPINE_SEM_H
#define SPINE_SEM_H
/*
* Portable counting semaphore using pthreads.
*
* macOS deprecated unnamed POSIX semaphores (sem_init, sem_getvalue).
* This wrapper provides the same counting semantics using a pthread
* mutex + condition variable, which works on all POSIX platforms.
*
* Spine uses semaphores as atomic counters (sem_post, sem_getvalue, sem_trywait).
*/
#include <pthread.h>
#include <errno.h>
typedef struct {
pthread_mutex_t mutex;
pthread_cond_t cond;
int value;
} spine_sem_t;
static inline int spine_sem_init(spine_sem_t *s, int value) {
if (pthread_mutex_init(&s->mutex, NULL) != 0) return -1;
if (pthread_cond_init(&s->cond, NULL) != 0) {
pthread_mutex_destroy(&s->mutex);
return -1;
}
s->value = value;
return 0;
}
static inline int spine_sem_post(spine_sem_t *s) {
pthread_mutex_lock(&s->mutex);
s->value++;
pthread_cond_signal(&s->cond);
pthread_mutex_unlock(&s->mutex);
return 0;
}
static inline int spine_sem_getvalue(spine_sem_t *s, int *val) {
pthread_mutex_lock(&s->mutex);
*val = s->value;
pthread_mutex_unlock(&s->mutex);
return 0;
}
static inline int spine_sem_wait(spine_sem_t *s) {
pthread_mutex_lock(&s->mutex);
while (s->value <= 0)
pthread_cond_wait(&s->cond, &s->mutex);
s->value--;
pthread_mutex_unlock(&s->mutex);
return 0;
}
static inline int spine_sem_trywait(spine_sem_t *s) {
pthread_mutex_lock(&s->mutex);
if (s->value <= 0) {
pthread_mutex_unlock(&s->mutex);
errno = EAGAIN;
return -1;
}
s->value--;
pthread_mutex_unlock(&s->mutex);
return 0;
}
static inline int spine_sem_destroy(spine_sem_t *s) {
pthread_mutex_destroy(&s->mutex);
pthread_cond_destroy(&s->cond);
return 0;
}
#endif /* SPINE_SEM_H */