Add a new exit handler to enable Shairport Sync to exit cleanly — that is, on exit, to clean up everything, e.g. open files, ports, sockets, devices, threads, it has been using.

If cleanup isn’t finished after one second, exit will occur unconditionally.
Use the new exit handler for normal exit, for SIGINT and SIGTERM handling, for abnormal exit after fatal errors, and for handling quit requests from the D-Bus and MPRIS interfaces.

When an unfixable error occurs without an installed unfixable error handler, unlock the mutex protecting Shairport Sync’s alsa backend to permit a clean exit.

Fix a very low-level mutex-locking error that interfered with debugging.

Simplify much mutex lock handling.
Remove lots of old and unused code, variables and definitions.

Update audio_sndio.c to remove some harmless compiler warnings.

Undo adding two seconds for messages to be output before _Exit() on die() to allow messages to be output...
Undo making die() exit unconditionally.
This commit is contained in:
Mike Brady
2026-07-16 14:51:06 +01:00
parent 0adbe5fc72
commit 83ae1d286b
27 changed files with 317 additions and 329 deletions
+2 -1
View File
@@ -31,7 +31,8 @@ shairport_sync_SOURCES = shairport.c bonjour_strings.c mdns.c common.c rtp.c \
player.c audio.c loudness.c activity_monitor.c \ player.c audio.c loudness.c activity_monitor.c \
rtsp.c \ rtsp.c \
utilities/debug.c utilities/network_utilities.c utilities/rtsp_message_utilities.c \ utilities/debug.c utilities/network_utilities.c utilities/rtsp_message_utilities.c \
utilities/string_utilities.c utilities/string_utilities.c \
utilities/exit.c
if BUILD_FOR_DARWIN if BUILD_FOR_DARWIN
AM_CXXFLAGS = -I/usr/local/include -Wno-multichar -Wall -Wextra -Wno-deprecated-declarations -pthread -DSYSCONFDIR=\"$(sysconfdir)\" AM_CXXFLAGS = -I/usr/local/include -Wno-multichar -Wall -Wextra -Wno-deprecated-declarations -pthread -DSYSCONFDIR=\"$(sysconfdir)\"
+1 -1
View File
@@ -131,7 +131,7 @@ void activity_monitor_signify_activity(int active) {
pthread_mutex_unlock(&activity_monitor_mutex); pthread_mutex_unlock(&activity_monitor_mutex);
} }
// lock the mutex again to send a signal // lock the mutex again to send a signal
pthread_cleanup_debug_mutex_lock(&activity_monitor_mutex, 10000, 4); pthread_mutex_lock_and_cleanup_push(&activity_monitor_mutex);
pthread_cond_signal(&activity_monitor_cv); pthread_cond_signal(&activity_monitor_cv);
pthread_cleanup_pop(1); // release the mutex pthread_cleanup_pop(1); // release the mutex
} }
+1 -2
View File
@@ -344,8 +344,7 @@ void *rtp_buffered_audio_processor(void *arg) {
} }
if (finished == 0) { if (finished == 0) {
pthread_cleanup_debug_mutex_lock(&conn->flush_mutex, 25000, pthread_mutex_lock_and_cleanup_push(&conn->flush_mutex);
4); // 25 ms is a long time to wait!
if (blocks_read != 0) { if (blocks_read != 0) {
if (conn->ap2_immediate_flush_requested != 0) { if (conn->ap2_immediate_flush_requested != 0) {
if (ap2_immediate_flush_requested == 0) { if (ap2_immediate_flush_requested == 0) {
+1 -1
View File
@@ -44,7 +44,7 @@ void decodeAndLogPlist(plist_t plist_to_log) {
// number if okay // number if okay
ssize_t ap2_event_port_send_message(rtsp_conn_info *conn, char *data, size_t data_length) { ssize_t ap2_event_port_send_message(rtsp_conn_info *conn, char *data, size_t data_length) {
ssize_t result = -1; // assume a problem ssize_t result = -1; // assume a problem
debug_mutex_lock(&conn->event_sender_mutex, 1000000, 4); pthread_mutex_lock(&conn->event_sender_mutex);
pthread_cleanup_push(mutex_unlock, &conn->event_sender_mutex); pthread_cleanup_push(mutex_unlock, &conn->event_sender_mutex);
if (conn->event_channel_fd != 0) { if (conn->event_channel_fd != 0) {
result = write_encrypted(conn->event_channel_fd, &conn->ap2_pairing_context.event_cipher_bundle, result = write_encrypted(conn->event_channel_fd, &conn->ap2_pairing_context.event_cipher_bundle,
+2 -2
View File
@@ -41,7 +41,7 @@ void ap2_event_receiver_cleanup_handler(void *arg) {
// this is here to ensure it's only performed once during a teardown of a ptp stream // this is here to ensure it's only performed once during a teardown of a ptp stream
send_ssnc_metadata('disc', conn->client_ip_string, strlen(conn->client_ip_string), 1); send_ssnc_metadata('disc', conn->client_ip_string, strlen(conn->client_ip_string), 1);
#endif #endif
debug_mutex_lock(&conn->event_sender_mutex, 1000000, 4); pthread_mutex_lock(&conn->event_sender_mutex);
pthread_cleanup_push(mutex_unlock, &conn->event_sender_mutex); pthread_cleanup_push(mutex_unlock, &conn->event_sender_mutex);
safe_socket_close(&conn->event_channel_fd); safe_socket_close(&conn->event_channel_fd);
pthread_cleanup_pop(1); // unlock the mutex pthread_cleanup_pop(1); // unlock the mutex
@@ -65,7 +65,7 @@ void *ap2_event_receiver(void *arg) {
memset(&remote_addr, 0, sizeof(remote_addr)); memset(&remote_addr, 0, sizeof(remote_addr));
socklen_t addr_size = sizeof(remote_addr); socklen_t addr_size = sizeof(remote_addr);
debug_mutex_lock(&conn->event_sender_mutex, 1000000, 4); pthread_mutex_lock(&conn->event_sender_mutex);
pthread_cleanup_push(mutex_unlock, &conn->event_sender_mutex); pthread_cleanup_push(mutex_unlock, &conn->event_sender_mutex);
conn->event_channel_fd = conn->event_channel_fd =
eintr_checked_accept(conn->event_socket, (struct sockaddr *)&remote_addr, &addr_size); eintr_checked_accept(conn->event_socket, (struct sockaddr *)&remote_addr, &addr_size);
+29 -30
View File
@@ -1,7 +1,7 @@
/* /*
* libalsa output driver. This file is part of Shairport. * libalsa output driver. This file is part of Shairport.
* Copyright (c) Muffinman, Skaman 2013 * Copyright (c) Muffinman, Skaman 2013
* Copyright (c) Mike Brady 2014--2025 * Copyright (c) Mike Brady 2014--2026
* All rights reserved. * All rights reserved.
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
@@ -212,9 +212,10 @@ void handle_unfixable_error(int errorCode) {
if (config.cmd_unfixable) { if (config.cmd_unfixable) {
command_execute(config.cmd_unfixable, messageString, 1); command_execute(config.cmd_unfixable, messageString, 1);
} else { } else {
die("An unrecoverable error, \"output_device_error_%d\", has been " pthread_mutex_unlock(&alsa_mutex); // release the alsa mutex to allow a clean exit
"detected. Doing an emergency exit, as no run_this_if_an_unfixable_error_is_detected " die("an unrecoverable error, \"output_device_error_%d\", has been "
"program.", "detected. Doing an emergency exit, as no \"run_this_if_an_unfixable_error_is_detected\" "
"handler has been provided.",
errorCode); errorCode);
} }
} }
@@ -246,7 +247,7 @@ static int get_permissible_configuration_settings() {
snd_pcm_hw_params_alloca(&local_alsa_params); snd_pcm_hw_params_alloca(&local_alsa_params);
snd_pcm_info_t *local_alsa_info; snd_pcm_info_t *local_alsa_info;
snd_pcm_info_alloca(&local_alsa_info); snd_pcm_info_alloca(&local_alsa_info);
pthread_cleanup_debug_mutex_lock(&alsa_mutex, 50000, 0); pthread_mutex_lock_and_cleanup_push(&alsa_mutex);
snd_pcm_t *temporary_alsa_handle = NULL; snd_pcm_t *temporary_alsa_handle = NULL;
ret = snd_pcm_open(&temporary_alsa_handle, alsa_out_dev, SND_PCM_STREAM_PLAYBACK, 0); ret = snd_pcm_open(&temporary_alsa_handle, alsa_out_dev, SND_PCM_STREAM_PLAYBACK, 0);
if (ret == 0) { if (ret == 0) {
@@ -1061,7 +1062,7 @@ static int prepare_mixer() {
// Now, start trying to initialise the alsa device with the settings // Now, start trying to initialise the alsa device with the settings
// obtained // obtained
pthread_cleanup_debug_mutex_lock(&alsa_mixer_mutex, 1000, 1); pthread_mutex_lock_and_cleanup_push(&alsa_mixer_mutex);
if (open_mixer() == 0) { if (open_mixer() == 0) {
if (snd_mixer_selem_get_playback_volume_range(alsa_mix_elem, &alsa_mix_minv, &alsa_mix_maxv) < if (snd_mixer_selem_get_playback_volume_range(alsa_mix_elem, &alsa_mix_minv, &alsa_mix_maxv) <
0) { 0) {
@@ -1147,7 +1148,7 @@ static int prepare_mixer() {
if (response == 0) if (response == 0)
response = close_mixer(); response = close_mixer();
} }
debug_mutex_unlock(&alsa_mixer_mutex, 3); // release the mutex pthread_mutex_unlock(&alsa_mixer_mutex); // release the mutex
pthread_cleanup_pop(0); pthread_cleanup_pop(0);
pthread_setcancelstate(oldState, NULL); pthread_setcancelstate(oldState, NULL);
} }
@@ -1593,7 +1594,7 @@ static int configure(int32_t requested_encoded_format, char **channel_map) {
int response = 0; int response = 0;
int oldState; int oldState;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable
pthread_cleanup_debug_mutex_lock(&alsa_mutex, 200000, 0); pthread_mutex_lock_and_cleanup_push(&alsa_mutex);
if (current_encoded_output_format != requested_encoded_format) { if (current_encoded_output_format != requested_encoded_format) {
if (current_encoded_output_format == 0) if (current_encoded_output_format == 0)
debug(2, "alsa: setting output configuration to %s.", debug(2, "alsa: setting output configuration to %s.",
@@ -1610,7 +1611,7 @@ static int configure(int32_t requested_encoded_format, char **channel_map) {
if ((response == 0) && (channel_map != NULL)) { if ((response == 0) && (channel_map != NULL)) {
*channel_map = get_channel_map_str(); *channel_map = get_channel_map_str();
} }
debug_mutex_unlock(&alsa_mutex, 0); pthread_mutex_unlock(&alsa_mutex);
pthread_cleanup_pop(0); pthread_cleanup_pop(0);
pthread_setcancelstate(oldState, NULL); pthread_setcancelstate(oldState, NULL);
if (response != 0) if (response != 0)
@@ -1623,7 +1624,7 @@ static void deinit(void) {
int oldState; int oldState;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable
debug(2, "audio_alsa deinit called."); debug(2, "audio_alsa deinit called.");
pthread_cleanup_debug_mutex_lock(&alsa_mutex, 10000, 1); pthread_mutex_lock_and_cleanup_push(&alsa_mutex);
if (alsa_handle != NULL) { if (alsa_handle != NULL) {
debug(3, "alsa: closing the output device."); debug(3, "alsa: closing the output device.");
do_close(); do_close();
@@ -1647,7 +1648,7 @@ static int set_mute_state() {
int response = 1; // some problem expected, e.g. no mixer or not allowed to use it or disconnected int response = 1; // some problem expected, e.g. no mixer or not allowed to use it or disconnected
int oldState; int oldState;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable
pthread_cleanup_debug_mutex_lock(&alsa_mixer_mutex, 10000, 0); pthread_mutex_lock_and_cleanup_push(&alsa_mixer_mutex);
if ((alsa_backend_state != abm_disconnected) && (config.alsa_use_hardware_mute == 1) && if ((alsa_backend_state != abm_disconnected) && (config.alsa_use_hardware_mute == 1) &&
(open_mixer() == 0)) { (open_mixer() == 0)) {
response = 0; // okay if actually using the mute facility response = 0; // okay if actually using the mute facility
@@ -1674,7 +1675,7 @@ static int set_mute_state() {
} }
close_mixer(); close_mixer();
} }
debug_mutex_unlock(&alsa_mixer_mutex, 4); // release the mutex pthread_mutex_unlock(&alsa_mixer_mutex); // release the mutex
pthread_cleanup_pop(0); // release the mutex pthread_cleanup_pop(0); // release the mutex
pthread_setcancelstate(oldState, NULL); pthread_setcancelstate(oldState, NULL);
return response; return response;
@@ -1910,11 +1911,11 @@ static int delay(long *the_delay) {
snd_pcm_state_t state; snd_pcm_state_t state;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable
pthread_cleanup_debug_mutex_lock(&alsa_mutex, 10000, 0); pthread_mutex_lock_and_cleanup_push(&alsa_mutex);
ret = delay_and_status(&state, &my_delay, NULL); ret = delay_and_status(&state, &my_delay, NULL);
debug_mutex_unlock(&alsa_mutex, 0); pthread_mutex_unlock(&alsa_mutex);
pthread_cleanup_pop(0); pthread_cleanup_pop(0);
pthread_setcancelstate(oldState, NULL); pthread_setcancelstate(oldState, NULL);
@@ -1941,7 +1942,7 @@ static int stats(uint64_t *raw_measurement_time, uint64_t *corrected_measurement
snd_pcm_sframes_t my_delay = 0; // this initialisation is to silence a clang warning snd_pcm_sframes_t my_delay = 0; // this initialisation is to silence a clang warning
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable
pthread_cleanup_debug_mutex_lock(&alsa_mutex, 10000, 0); pthread_mutex_lock_and_cleanup_push(&alsa_mutex);
if (alsa_handle == NULL) { if (alsa_handle == NULL) {
ret = alsa_handle_status; ret = alsa_handle_status;
@@ -1958,7 +1959,7 @@ static int stats(uint64_t *raw_measurement_time, uint64_t *corrected_measurement
frames_sent_break_occurred = 0; // reset it. frames_sent_break_occurred = 0; // reset it.
if (frames_sent_to_dac != NULL) if (frames_sent_to_dac != NULL)
*frames_sent_to_dac = frames_sent_for_playing; *frames_sent_to_dac = frames_sent_for_playing;
debug_mutex_unlock(&alsa_mutex, 0); pthread_mutex_unlock(&alsa_mutex);
pthread_cleanup_pop(0); pthread_cleanup_pop(0);
pthread_setcancelstate(oldState, NULL); pthread_setcancelstate(oldState, NULL);
uint64_t hd = my_delay; // note: snd_pcm_sframes_t is a long uint64_t hd = my_delay; // note: snd_pcm_sframes_t is a long
@@ -1983,12 +1984,10 @@ static int do_play(void *buf, int samples) {
if (ret == 0) { // will be non-zero if an error or a stall if (ret == 0) { // will be non-zero if an error or a stall
// just check the state of the DAC // just check the state of the DAC
if ((state != SND_PCM_STATE_PREPARED) && (state != SND_PCM_STATE_RUNNING) && if ((state != SND_PCM_STATE_PREPARED) && (state != SND_PCM_STATE_RUNNING)) {
(state != SND_PCM_STATE_XRUN)) { debug(1, "alsa: DAC in unexpected state %s prior to writing.", snd_pcm_state_name(state));
debug(1, "alsa: DAC in odd SND_PCM_STATE_* %d prior to writing.", state);
} }
if (state == SND_PCM_STATE_XRUN) { if (state == SND_PCM_STATE_XRUN) {
debug(1, "alsa: DAC in SND_PCM_STATE_XRUN prior to writing.");
ret = snd_pcm_recover(alsa_handle, ret, 1); ret = snd_pcm_recover(alsa_handle, ret, 1);
} }
@@ -2141,7 +2140,7 @@ static int play(void *buf, int samples, __attribute__((unused)) int sample_type,
int ret = 0; int ret = 0;
pthread_cleanup_debug_mutex_lock(&alsa_mutex, 50000, 0); pthread_mutex_lock_and_cleanup_push(&alsa_mutex);
if (alsa_backend_state == abm_disconnected) { if (alsa_backend_state == abm_disconnected) {
ret = do_open(); ret = do_open();
@@ -2163,13 +2162,13 @@ static int play(void *buf, int samples, __attribute__((unused)) int sample_type,
ret = do_play(buf, samples); ret = do_play(buf, samples);
} }
debug_mutex_unlock(&alsa_mutex, 0); pthread_mutex_unlock(&alsa_mutex);
pthread_cleanup_pop(0); // release the mutex pthread_cleanup_pop(0); // release the mutex
return ret; return ret;
} }
static void flush(void) { static void flush(void) {
pthread_cleanup_debug_mutex_lock(&alsa_mutex, 10000, 4); pthread_mutex_lock_and_cleanup_push(&alsa_mutex);
if (alsa_backend_state != abm_disconnected) { // must be playing or connected... if (alsa_backend_state != abm_disconnected) { // must be playing or connected...
// do nothing for a flush if config.keep_dac_busy is true // do nothing for a flush if config.keep_dac_busy is true
if (config.keep_dac_busy == 0) { if (config.keep_dac_busy == 0) {
@@ -2178,19 +2177,19 @@ static void flush(void) {
} else { } else {
debug(3, "alsa: flush() -- called on a disconnected alsa backend"); debug(3, "alsa: flush() -- called on a disconnected alsa backend");
} }
debug_mutex_unlock(&alsa_mutex, 4); pthread_mutex_unlock(&alsa_mutex);
pthread_cleanup_pop(0); // release the mutex pthread_cleanup_pop(0); // release the mutex
} }
static void stop(void) { static void stop(void) {
pthread_cleanup_debug_mutex_lock(&alsa_mutex, 10000, 4); pthread_mutex_lock_and_cleanup_push(&alsa_mutex);
if (alsa_backend_state != abm_disconnected) { // must be playing or connected... if (alsa_backend_state != abm_disconnected) { // must be playing or connected...
if (config.keep_dac_busy == 0) { if (config.keep_dac_busy == 0) {
do_close(); do_close();
} }
} else } else
debug(3, "alsa: stop() -- called on a disconnected alsa backend"); debug(3, "alsa: stop() -- called on a disconnected alsa backend");
debug_mutex_unlock(&alsa_mutex, 4); pthread_mutex_unlock(&alsa_mutex);
pthread_cleanup_pop(0); // release the mutex pthread_cleanup_pop(0); // release the mutex
} }
@@ -2200,7 +2199,7 @@ static void do_volume(double vol) { // caller is assumed to have the alsa_mutex
int oldState; int oldState;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable
set_volume = vol; set_volume = vol;
pthread_cleanup_debug_mutex_lock(&alsa_mixer_mutex, 1000, 4); pthread_mutex_lock_and_cleanup_push(&alsa_mixer_mutex);
if (volume_set_request && (open_mixer() == 0)) { if (volume_set_request && (open_mixer() == 0)) {
if (has_softvol) { if (has_softvol) {
if (ctl && elem_id) { if (ctl && elem_id) {
@@ -2230,7 +2229,7 @@ static void do_volume(double vol) { // caller is assumed to have the alsa_mutex
volume_set_request = 0; // any external request that has been made is now satisfied volume_set_request = 0; // any external request that has been made is now satisfied
close_mixer(); close_mixer();
} }
debug_mutex_unlock(&alsa_mixer_mutex, 4); pthread_mutex_unlock(&alsa_mixer_mutex);
pthread_cleanup_pop(0); // release the mutex pthread_cleanup_pop(0); // release the mutex
pthread_setcancelstate(oldState, NULL); pthread_setcancelstate(oldState, NULL);
} }
@@ -2303,7 +2302,7 @@ static void *alsa_buffer_monitor_thread_code(__attribute__((unused)) void *arg)
"alsa_buffer_monitor_thread_code sleep was %.6f sec but request was for %.6f sec. " "alsa_buffer_monitor_thread_code sleep was %.6f sec but request was for %.6f sec. "
"Disabling standby may not work properly!", "Disabling standby may not work properly!",
sleep_time_actual_ns * 0.000000001, config.disable_standby_mode_silence_scan_interval); sleep_time_actual_ns * 0.000000001, config.disable_standby_mode_silence_scan_interval);
pthread_cleanup_debug_mutex_lock(&alsa_mutex, 200000, 0); pthread_mutex_lock_and_cleanup_push(&alsa_mutex);
// check possible state transitions here // check possible state transitions here
if ((alsa_backend_state == abm_disconnected) && (config.keep_dac_busy != 0)) { if ((alsa_backend_state == abm_disconnected) && (config.keep_dac_busy != 0)) {
// open the dac and move to abm_connected mode // open the dac and move to abm_connected mode
@@ -2409,7 +2408,7 @@ static void *alsa_buffer_monitor_thread_code(__attribute__((unused)) void *arg)
} }
} }
} }
debug_mutex_unlock(&alsa_mutex, 0); pthread_mutex_unlock(&alsa_mutex);
pthread_cleanup_pop(0); // release the mutex pthread_cleanup_pop(0); // release the mutex
uint64_t tsb = get_absolute_time_in_ns(); uint64_t tsb = get_absolute_time_in_ns();
usleep(sleep_time_us); // has a cancellation point in it usleep(sleep_time_us); // has a cancellation point in it
+28 -28
View File
@@ -39,7 +39,7 @@ static char channel_map_6[] = "FL FR BL BR FC LFE";
static pthread_mutex_t sndio_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t sndio_mutex = PTHREAD_MUTEX_INITIALIZER;
static int current_encoded_output_format; static int current_encoded_output_format;
static struct sio_hdl *hdl; static struct sio_hdl *ohdl;
static const char *output_device_name; static const char *output_device_name;
static unsigned int output_device_driver_bufsiz; // parameters for opening the output device static unsigned int output_device_driver_bufsiz; // parameters for opening the output device
static unsigned int output_device_driver_round; // parameters for opening the output device static unsigned int output_device_driver_round; // parameters for opening the output device
@@ -51,7 +51,7 @@ static size_t written;
uint64_t time_of_last_onmove_cb; uint64_t time_of_last_onmove_cb;
int at_least_one_onmove_cb_seen; int at_least_one_onmove_cb_seen;
struct sio_par par; struct sio_par opar;
struct sndio_formats { struct sndio_formats {
sps_format_t sps_format; sps_format_t sps_format;
@@ -84,7 +84,7 @@ static uint16_t permissible_configurations[SPS_RATE_HIGHEST + 1][SPS_FORMAT_HIGH
static int get_permissible_configuration_settings() { static int get_permissible_configuration_settings() {
int ret = 0; int ret = 0;
uint64_t hto = get_absolute_time_in_ns(); uint64_t hto = get_absolute_time_in_ns();
pthread_cleanup_debug_mutex_lock(&sndio_mutex, 1000, 1); pthread_mutex_lock_and_cleanup_push(&sndio_mutex);
struct sio_hdl *hdl = sio_open(output_device_name, SIO_PLAY, 0); struct sio_hdl *hdl = sio_open(output_device_name, SIO_PLAY, 0);
if (hdl != NULL) { if (hdl != NULL) {
struct sio_cap cap; struct sio_cap cap;
@@ -198,7 +198,7 @@ static int get_permissible_configuration_settings() {
proposed_par.rate = sps_rate_actual_rate(r); proposed_par.rate = sps_rate_actual_rate(r);
proposed_par.pchan = c; proposed_par.pchan = c;
proposed_par.bits = format_info->bits; proposed_par.bits = format_info->bits;
proposed_par.bps = SIO_BPS(par.bits); proposed_par.bps = SIO_BPS(opar.bits);
proposed_par.le = format_info->le; proposed_par.le = format_info->le;
proposed_par.sig = format_info->sig; proposed_par.sig = format_info->sig;
if (sio_setpar(hdl, &proposed_par) == 1) { if (sio_setpar(hdl, &proposed_par) == 1) {
@@ -283,7 +283,7 @@ static int configure(int32_t requested_encoded_format, char **channel_map) {
if (is_running != 0) { if (is_running != 0) {
debug(1, "sndio: the output device is running while changing configuration"); debug(1, "sndio: the output device is running while changing configuration");
if (sio_flush(hdl) != 1) if (sio_flush(ohdl) != 1)
debug(1, "sndio: unable to flush"); debug(1, "sndio: unable to flush");
written = played = is_running = 0; written = played = is_running = 0;
time_of_last_onmove_cb = 0; time_of_last_onmove_cb = 0;
@@ -301,9 +301,9 @@ static int configure(int32_t requested_encoded_format, char **channel_map) {
par.sig = format_info->sig; par.sig = format_info->sig;
debug(3, "Requested %u/%u/%u/%u/%u (rate/bits/signed/le/channels)", par.rate, par.bits, par.sig, debug(3, "Requested %u/%u/%u/%u/%u (rate/bits/signed/le/channels)", par.rate, par.bits, par.sig,
par.le, par.pchan); par.le, par.pchan);
if (sio_setpar(hdl, &par) == 1) { if (sio_setpar(ohdl, &par) == 1) {
struct sio_par apar; struct sio_par apar;
if (sio_getpar(hdl, &apar) == 1) { if (sio_getpar(ohdl, &apar) == 1) {
debug(3, "Got %u/%u/%u/%u/%u (rate/bits/signed/le/channels)", apar.rate, apar.bits, debug(3, "Got %u/%u/%u/%u/%u (rate/bits/signed/le/channels)", apar.rate, apar.bits,
apar.sig, apar.le, apar.pchan); apar.sig, apar.le, apar.pchan);
if ((apar.rate == par.rate) && (apar.pchan == par.pchan) && (apar.bits == par.bits) && if ((apar.rate == par.rate) && (apar.pchan == par.pchan) && (apar.bits == par.bits) &&
@@ -439,23 +439,23 @@ static int init(int argc, char **argv) {
*/ */
get_permissible_configuration_settings(); get_permissible_configuration_settings();
debug(2, "sndio: output device name is \"%s\".", output_device_name); debug(2, "sndio: output device name is \"%s\".", output_device_name);
hdl = sio_open(output_device_name, SIO_PLAY, 0); ohdl = sio_open(output_device_name, SIO_PLAY, 0);
if (!hdl) if (!ohdl)
die("sndio: cannot open audio device"); die("sndio: cannot open audio device");
sio_onmove(hdl, onmove_cb, NULL); sio_onmove(ohdl, onmove_cb, NULL);
// debug(1, "sndio: init done"); // debug(1, "sndio: init done");
return 0; return 0;
} }
static void deinit() { static void deinit() {
pthread_cleanup_debug_mutex_lock(&sndio_mutex, 1000, 1); pthread_mutex_lock_and_cleanup_push(&sndio_mutex);
if (hdl != NULL) { if (ohdl != NULL) {
if (is_running != 0) { if (is_running != 0) {
sio_flush(hdl); sio_flush(ohdl);
is_running = 0; is_running = 0;
} }
sio_close(hdl); sio_close(ohdl);
hdl = NULL; ohdl = NULL;
} }
pthread_cleanup_pop(1); // unlock the mutex pthread_cleanup_pop(1); // unlock the mutex
} }
@@ -465,10 +465,10 @@ static int play(void *buf, int frames, __attribute__((unused)) int sample_type,
__attribute__((unused)) uint64_t playtime) { __attribute__((unused)) uint64_t playtime) {
if (frames > 0) { if (frames > 0) {
pthread_cleanup_debug_mutex_lock(&sndio_mutex, 1000, 1); pthread_mutex_lock_and_cleanup_push(&sndio_mutex);
if (is_running == 0) { if (is_running == 0) {
if (hdl != NULL) { if (ohdl != NULL) {
if (sio_start(hdl) != 1) if (sio_start(ohdl) != 1)
debug(1, "sndio: unable to start"); debug(1, "sndio: unable to start");
is_running = 1; is_running = 1;
written = played = 0; written = played = 0;
@@ -478,7 +478,7 @@ static int play(void *buf, int frames, __attribute__((unused)) int sample_type,
debug(1, "sndio: output device is not open for play!"); debug(1, "sndio: output device is not open for play!");
} }
} }
written += sio_write(hdl, buf, frames * framesize); written += sio_write(ohdl, buf, frames * framesize);
pthread_cleanup_pop(1); // unlock the mutex pthread_cleanup_pop(1); // unlock the mutex
} }
return 0; return 0;
@@ -486,10 +486,10 @@ static int play(void *buf, int frames, __attribute__((unused)) int sample_type,
static void stop() { static void stop() {
pthread_cleanup_debug_mutex_lock(&sndio_mutex, 1000, 1); pthread_mutex_lock_and_cleanup_push(&sndio_mutex);
if (hdl != NULL) { if (ohdl != NULL) {
if (is_running != 0) { if (is_running != 0) {
if (sio_flush(hdl) != 1) if (sio_flush(ohdl) != 1)
debug(1, "sndio: unable to stop"); debug(1, "sndio: unable to stop");
written = played = is_running = 0; written = played = is_running = 0;
} else { } else {
@@ -509,7 +509,7 @@ int get_delay(long *delay) {
// calculate the difference in time between now and when the last callback occurred, // calculate the difference in time between now and when the last callback occurred,
// and use it to estimate the frames that would have been output // and use it to estimate the frames that would have been output
uint64_t time_difference = get_absolute_time_in_ns() - time_of_last_onmove_cb; uint64_t time_difference = get_absolute_time_in_ns() - time_of_last_onmove_cb;
uint64_t frame_difference = (time_difference * par.rate) / 1000000000; uint64_t frame_difference = (time_difference * opar.rate) / 1000000000;
estimated_extra_frames_output = frame_difference; estimated_extra_frames_output = frame_difference;
// sanity check -- total estimate can not exceed frames written. // sanity check -- total estimate can not exceed frames written.
if ((estimated_extra_frames_output + played) > written / framesize) { if ((estimated_extra_frames_output + played) > written / framesize) {
@@ -526,8 +526,8 @@ int get_delay(long *delay) {
static int delay(long *delay) { static int delay(long *delay) {
int result = 0; int result = 0;
pthread_cleanup_debug_mutex_lock(&sndio_mutex, 1000, 1); pthread_mutex_lock_and_cleanup_push(&sndio_mutex);
if (hdl != NULL) { if (ohdl != NULL) {
if (is_running != 0) { if (is_running != 0) {
result = get_delay(delay); result = get_delay(delay);
} else { } else {
@@ -543,10 +543,10 @@ static int delay(long *delay) {
} }
static void flush() { static void flush() {
pthread_cleanup_debug_mutex_lock(&sndio_mutex, 1000, 1); pthread_mutex_lock_and_cleanup_push(&sndio_mutex);
if (hdl != NULL) { if (ohdl != NULL) {
if (is_running != 0) { if (is_running != 0) {
if (sio_flush(hdl) != 1) if (sio_flush(ohdl) != 1)
debug(1, "sndio: unable to flush"); debug(1, "sndio: unable to flush");
written = played = is_running = 0; written = played = is_running = 0;
} else { } else {
+7 -106
View File
@@ -133,8 +133,6 @@ void set_alsa_out_dev(char *);
config_t config_file_stuff; config_t config_file_stuff;
uint64_t minimum_dac_queue_size; uint64_t minimum_dac_queue_size;
pthread_mutex_t the_conn_lock = PTHREAD_MUTEX_INITIALIZER;
unsigned int sps_format_sample_size_array[] = { unsigned int sps_format_sample_size_array[] = {
0, // unknown 0, // unknown
1, 1, // S8, U8 1, 1, // S8, U8
@@ -232,8 +230,10 @@ const char *short_format_description(int32_t encoded_format) {
} }
// true if Shairport Sync is supposed to be sending output to the output device, false otherwise // true if Shairport Sync is supposed to be sending output to the output device, false otherwise
static volatile int requested_connection_state_to_output = 1; // static volatile int requested_connection_state_to_output = 1;
/*
// this stuff is to direct logging to syslog via libdaemon or directly // this stuff is to direct logging to syslog via libdaemon or directly
// alternatively you can direct it to stderr using a command line option // alternatively you can direct it to stderr using a command line option
@@ -336,12 +336,12 @@ void log_to_syslog() {
#else #else
sps_log = syslog; sps_log = syslog;
#endif #endif
} }
*/
shairport_cfg config; shairport_cfg config;
sigset_t pselect_sigset;
static uint16_t UDPPortIndex = 0; static uint16_t UDPPortIndex = 0;
void resetFreeUDPPort() { void resetFreeUDPPort() {
@@ -502,9 +502,9 @@ uint16_t bind_UDP_port(int ip_family, const char *self_ip_address, uint32_t scop
return sport; return sport;
} }
int get_requested_connection_state_to_output() { return requested_connection_state_to_output; } // int get_requested_connection_state_to_output() { return requested_connection_state_to_output; }
void set_requested_connection_state_to_output(int v) { requested_connection_state_to_output = v; } // void set_requested_connection_state_to_output(int v) { requested_connection_state_to_output = v; }
void getErrorText(char *destinationString, size_t destinationStringLength) { void getErrorText(char *destinationString, size_t destinationStringLength) {
#pragma GCC diagnostic push #pragma GCC diagnostic push
@@ -1806,13 +1806,6 @@ uint64_t nctoh64(const uint8_t *p) {
return vl; return vl;
} }
pthread_mutex_t barrier_mutex = PTHREAD_MUTEX_INITIALIZER;
void memory_barrier() {
pthread_mutex_lock(&barrier_mutex);
pthread_mutex_unlock(&barrier_mutex);
}
void sps_nanosleep(const time_t sec, const long nanosec) { void sps_nanosleep(const time_t sec, const long nanosec) {
struct timespec req, rem; struct timespec req, rem;
int result; int result;
@@ -1827,98 +1820,6 @@ void sps_nanosleep(const time_t sec, const long nanosec) {
nanosec, errno); nanosec, errno);
} }
// Mac OS X doesn't have pthread_mutex_timedlock
// Also note that timing must be relative to CLOCK_REALTIME
/*
#ifdef COMPILE_FOR_LINUX_AND_FREEBSD_AND_CYGWIN_AND_OPENBSD
int sps_pthread_mutex_timedlock(pthread_mutex_t *mutex, useconds_t dally_time) {
int oldState;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState);
struct timespec timeoutTime;
uint64_t wait_until_time = dally_time * 1000; // to nanoseconds
uint64_t start_time = get_realtime_in_ns(); // this is from CLOCK_REALTIME
wait_until_time = wait_until_time + start_time;
uint64_t wait_until_sec = wait_until_time / 1000000000;
uint64_t wait_until_nsec = wait_until_time % 1000000000;
timeoutTime.tv_sec = wait_until_sec;
timeoutTime.tv_nsec = wait_until_nsec;
int r = pthread_mutex_timedlock(mutex, &timeoutTime);
pthread_setcancelstate(oldState, NULL);
return r;
}
#endif
#ifdef COMPILE_FOR_OSX
*/
int sps_pthread_mutex_timedlock(pthread_mutex_t *mutex, useconds_t dally_time) {
// this would not be not pthread_cancellation safe because is contains a cancellation point
int oldState;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState);
int time_to_wait = dally_time;
int r = pthread_mutex_trylock(mutex);
while ((r == EBUSY) && (time_to_wait > 0)) {
int st = time_to_wait;
if (st > 1000)
st = 1000;
sps_nanosleep(0, st * 1000); // this contains a cancellation point
time_to_wait -= st;
r = pthread_mutex_trylock(mutex);
}
pthread_setcancelstate(oldState, NULL);
return r;
}
// #endif
int _debug_mutex_lock(pthread_mutex_t *mutex, useconds_t dally_time, const char *mutexname,
const char *filename, const int line, int debuglevel) {
if ((debuglevel > debug_level()) || (debuglevel == 0))
return pthread_mutex_lock(mutex);
int oldState;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState);
if (debuglevel != 0)
_debug(filename, line, 3, "mutex_lock \"%s\".", mutexname); // only if you really ask for it!
int result = sps_pthread_mutex_timedlock(mutex, dally_time);
if (result == ETIMEDOUT) {
_debug(
filename, line, debuglevel,
"mutex_lock \"%s\" failed to lock after %f ms -- now waiting unconditionally to lock it.",
mutexname, dally_time * 1E-3);
result = pthread_mutex_lock(mutex);
if (result == 0)
_debug(filename, line, debuglevel, " ...mutex_lock \"%s\" locked successfully.", mutexname);
else
_debug(filename, line, debuglevel, " ...mutex_lock \"%s\" exited with error code: %u",
mutexname, result);
}
pthread_setcancelstate(oldState, NULL);
return result;
}
int _debug_mutex_unlock(pthread_mutex_t *mutex, const char *mutexname, const char *filename,
const int line, int debuglevel) {
if ((debuglevel > debug_level()) || (debuglevel == 0))
return pthread_mutex_unlock(mutex);
int oldState;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState);
char dstring[1000];
char errstr[512];
memset(dstring, 0, sizeof(dstring));
snprintf(dstring, sizeof(dstring), "%s:%d", filename, line);
debug(debuglevel, "mutex_unlock \"%s\" at \"%s\".", mutexname, dstring);
int r = pthread_mutex_unlock(mutex);
if ((debuglevel != 0) && (r != 0)) {
if (strerror_r(r, errstr, sizeof(errstr)) == 0) {
debug(1, "error %d: \"%s\" unlocking mutex \"%s\" at \"%s\".", r, errstr, mutexname, dstring);
} else {
debug(1, "error %d: unlocking mutex \"%s\" at \"%s\".", r, mutexname, dstring);
}
}
pthread_setcancelstate(oldState, NULL);
return r;
}
void malloc_cleanup(void *arg) { void malloc_cleanup(void *arg) {
// the address of the malloc variable is passed in case a realloc is done as some time // the address of the malloc variable is passed in case a realloc is done as some time
// debug(1, "malloc cleanup called."); // debug(1, "malloc cleanup called.");
+13 -50
View File
@@ -433,9 +433,6 @@ typedef struct {
unsigned int output_channel_map_size; // number of output channels unsigned int output_channel_map_size; // number of output channels
#if defined(CONFIG_DBUS_INTERFACE) || defined(CONFIG_MPRIS_INTERFACE) #if defined(CONFIG_DBUS_INTERFACE) || defined(CONFIG_MPRIS_INTERFACE)
GMainLoop *glib_worker_loop;
// for clean quitting from a dbus interface quit request (from the DBus or MPRIS interfaces)
int quit_requested_from_glib_mainloop; // remember that it initialised to zero.
dbus_message_bus_t dbus_default_message_bus; dbus_message_bus_t dbus_default_message_bus;
#if defined(CONFIG_DBUS_INTERFACE) #if defined(CONFIG_DBUS_INTERFACE)
@@ -453,18 +450,20 @@ uint32_t nctohl(const uint8_t *p); // read 4 characters from *p and do ntohl on
uint16_t nctohs(const uint8_t *p); // read 2 characters from *p and do ntohs on them uint16_t nctohs(const uint8_t *p); // read 2 characters from *p and do ntohs on them
uint64_t nctoh64(const uint8_t *p); // read 8 characters from *p to a uint64_t uint64_t nctoh64(const uint8_t *p); // read 8 characters from *p to a uint64_t
void memory_barrier(); // void memory_barrier();
/*
void log_to_stderr(); // call this to direct logging to stderr; void log_to_stderr(); // call this to direct logging to stderr;
void log_to_stdout(); // call this to direct logging to stdout; void log_to_stdout(); // call this to direct logging to stdout;
void log_to_syslog(); // call this to direct logging to the system log; void log_to_syslog(); // call this to direct logging to the system log;
void log_to_file(); // call this to direct logging to a file or (pre-existing) pipe; void log_to_file(); // call this to direct logging to a file or (pre-existing) pipe;
*/
// true if Shairport Sync is supposed to be sending output to the output device, false otherwise // true if Shairport Sync is supposed to be sending output to the output device, false otherwise
int get_requested_connection_state_to_output(); //int get_requested_connection_state_to_output();
void set_requested_connection_state_to_output(int v); // void set_requested_connection_state_to_output(int v);
int try_to_open_pipe_for_writing( int try_to_open_pipe_for_writing(
const char *pathname); // open it without blocking if it's not hooked up const char *pathname); // open it without blocking if it's not hooked up
@@ -500,14 +499,6 @@ extern volatile int debuglev;
MADEID(once_flag_, __LINE__) = 1; \ MADEID(once_flag_, __LINE__) = 1; \
} }
// do X once, and then ignore repeated calls until they stop for more than one second
#define once_per_1_second_burst(X) \
static uint64_t MADEID(time_, __LINE__) = 0; \
int64_t MADEID(interval_, __LINE__) = get_absolute_time_in_ns() - MADEID(time_, __LINE__); \
if ((MADEID(time_, __LINE__) == 0) || (MADEID(interval_, __LINE__) > 1000000000L)) \
X; \
MADEID(time_, __LINE__) = get_absolute_time_in_ns()
void getErrorText(char *destinationString, size_t destinationStringLength); void getErrorText(char *destinationString, size_t destinationStringLength);
uint8_t *base64_dec(char *input, int *outlen); uint8_t *base64_dec(char *input, int *outlen);
@@ -575,42 +566,11 @@ void command_set_volume(double volume);
int mkpath(const char *path, mode_t mode); int mkpath(const char *path, mode_t mode);
extern sigset_t pselect_sigset; #define pthread_mutex_lock_and_cleanup_push(mu) \
if (pthread_mutex_lock(mu) == 0) \
pthread_cleanup_push(mutex_unlock, (void *)mu)
extern pthread_mutex_t the_conn_lock;
#define conn_lock(arg) \
pthread_mutex_lock(&the_conn_lock); \
arg; \
pthread_mutex_unlock(&the_conn_lock);
// wait for the specified time in microseconds -- it checks every 20 milliseconds
// int sps_pthread_mutex_timedlock(pthread_mutex_t *mutex, useconds_t dally_time,
// const char *debugmessage, int debuglevel);
// wait for the specified time, checking every 20 milliseconds, and block if it can't acquire the
// lock
int _debug_mutex_lock(pthread_mutex_t *mutex, useconds_t dally_time, const char *mutexName,
const char *filename, const int line, int debuglevel);
#define debug_mutex_lock(mu, t, d) _debug_mutex_lock(mu, t, #mu, __FILE__, __LINE__, d)
int _debug_mutex_unlock(pthread_mutex_t *mutex, const char *mutexName, const char *filename,
const int line, int debuglevel);
#define debug_mutex_unlock(mu, d) _debug_mutex_unlock(mu, #mu, __FILE__, __LINE__, d)
void pthread_cleanup_debug_mutex_unlock(void *arg);
#define pthread_cleanup_debug_mutex_lock(mu, t, d) \
if (_debug_mutex_lock(mu, t, #mu, __FILE__, __LINE__, d) == 0) \
pthread_cleanup_push(pthread_cleanup_debug_mutex_unlock, (void *)mu)
#define config_lock \
if (pthread_mutex_trylock(&config.lock) != 0) { \
debug(1, "config_lock: cannot acquire config.lock"); \
}
#define config_unlock pthread_mutex_unlock(&config.lock)
int do_pthread_setname(pthread_t *restrict thread, const char *format, ...); int do_pthread_setname(pthread_t *restrict thread, const char *format, ...);
@@ -632,7 +592,6 @@ char *get_version_string(); // mallocs a string space -- remember to free it aft
int64_t generate_zero_frames(char *outp, size_t number_of_frames, int with_dither, int64_t generate_zero_frames(char *outp, size_t number_of_frames, int with_dither,
int64_t random_number_in, uint32_t encoded_output_format); int64_t random_number_in, uint32_t encoded_output_format);
void malloc_cleanup(void *arg);
int string_update_with_size(char **str, int *flag, char *s, size_t len); int string_update_with_size(char **str, int *flag, char *s, size_t len);
@@ -644,10 +603,14 @@ int bind_socket_and_port(int type, int ip_family, const char *self_ip_address, u
uint16_t bind_UDP_port(int ip_family, const char *self_ip_address, uint32_t scope_id, int *sock); uint16_t bind_UDP_port(int ip_family, const char *self_ip_address, uint32_t scope_id, int *sock);
// for pthread_push and pop
// careful with the difference between cleanup and unlock!
void malloc_cleanup(void *arg);
void socket_cleanup(void *arg); void socket_cleanup(void *arg);
void mutex_unlock(void *arg); void mutex_unlock(void *arg);
void rwlock_unlock(void *arg);
void mutex_cleanup(void *arg); void mutex_cleanup(void *arg);
void rwlock_unlock(void *arg);
void cv_cleanup(void *arg); void cv_cleanup(void *arg);
void thread_cleanup(void *arg); void thread_cleanup(void *arg);
#ifdef CONFIG_AIRPLAY_2 #ifdef CONFIG_AIRPLAY_2
+8 -12
View File
@@ -212,11 +212,7 @@ int dacp_send_command(const char *command, char **body, ssize_t *bodysize) {
uint64_t start_time = get_absolute_time_in_ns(); uint64_t start_time = get_absolute_time_in_ns();
pthread_cleanup_push(addrinfo_cleanup, (void *)&res); pthread_cleanup_push(addrinfo_cleanup, (void *)&res);
// only do this one at a time -- not sure it is necessary, but better safe than sorry // only do this one at a time -- not sure it is necessary, but better safe than sorry
int mutex_reply = pthread_mutex_lock(&dacp_conversation_lock);
// int mutex_reply = sps_pthread_mutex_timedlock(&dacp_conversation_lock, 2000000, command,
// 1);
int mutex_reply = debug_mutex_lock(&dacp_conversation_lock, 2000000, 1);
// int mutex_reply = pthread_mutex_lock(&dacp_conversation_lock);
if (mutex_reply == 0) { if (mutex_reply == 0) {
pthread_cleanup_push(mutex_lock_cleanup, (void *)&dacp_conversation_lock); pthread_cleanup_push(mutex_lock_cleanup, (void *)&dacp_conversation_lock);
@@ -390,10 +386,10 @@ void relinquish_dacp_server_information(rtsp_conn_info *conn) {
// as the conn's connection number // as the conn's connection number
// this is to signify that the player has stopped, but only if another thread (with a different // this is to signify that the player has stopped, but only if another thread (with a different
// index) hasn't already taken over the dacp service // index) hasn't already taken over the dacp service
debug_mutex_lock(&dacp_server_information_lock, 500000, 4); pthread_mutex_lock(&dacp_server_information_lock);
if (dacp_server.players_connection_thread_index == conn->connection_number) if (dacp_server.players_connection_thread_index == conn->connection_number)
dacp_server.players_connection_thread_index = 0; dacp_server.players_connection_thread_index = 0;
debug_mutex_unlock(&dacp_server_information_lock, 4); pthread_mutex_unlock(&dacp_server_information_lock);
} }
// this will be running on the thread of its caller, not of the conversation thread... // this will be running on the thread of its caller, not of the conversation thread...
@@ -403,7 +399,7 @@ void relinquish_dacp_server_information(rtsp_conn_info *conn) {
// Thus, we can keep the DACP port that might have previously been discovered // Thus, we can keep the DACP port that might have previously been discovered
void set_dacp_server_information(rtsp_conn_info *conn) { void set_dacp_server_information(rtsp_conn_info *conn) {
// debug(1, "set_dacp_server_information"); // debug(1, "set_dacp_server_information");
debug_mutex_lock(&dacp_server_information_lock, 500000, 2); pthread_mutex_lock(&dacp_server_information_lock);
dacp_server.players_connection_thread_index = conn->connection_number; dacp_server.players_connection_thread_index = conn->connection_number;
if ((conn->dacp_id == NULL) || (strcmp(conn->dacp_id, dacp_server.dacp_id) != 0)) { if ((conn->dacp_id == NULL) || (strcmp(conn->dacp_id, dacp_server.dacp_id) != 0)) {
@@ -464,11 +460,11 @@ void set_dacp_server_information(rtsp_conn_info *conn) {
debug(3, "set_dacp_server_information set active-remote id to %s.", dacp_server.active_remote_id); debug(3, "set_dacp_server_information set active-remote id to %s.", dacp_server.active_remote_id);
pthread_cond_signal(&dacp_server_information_cv); pthread_cond_signal(&dacp_server_information_cv);
debug_mutex_unlock(&dacp_server_information_lock, 3); pthread_mutex_unlock(&dacp_server_information_lock);
} }
void dacp_monitor_port_update_callback(const char *dacp_id, uint16_t port) { void dacp_monitor_port_update_callback(const char *dacp_id, uint16_t port) {
debug_mutex_lock(&dacp_server_information_lock, 500000, 2); pthread_mutex_lock(&dacp_server_information_lock);
debug(3, debug(3,
"dacp_monitor_port_update_callback with Remote ID \"%s\", target ID \"%s\" and port " "dacp_monitor_port_update_callback with Remote ID \"%s\", target ID \"%s\" and port "
"number %d.", "number %d.",
@@ -493,7 +489,7 @@ void dacp_monitor_port_update_callback(const char *dacp_id, uint16_t port) {
} }
} }
pthread_cond_signal(&dacp_server_information_cv); pthread_cond_signal(&dacp_server_information_cv);
debug_mutex_unlock(&dacp_server_information_lock, 3); pthread_mutex_unlock(&dacp_server_information_lock);
} }
void *dacp_monitor_thread_code(__attribute__((unused)) void *na) { void *dacp_monitor_thread_code(__attribute__((unused)) void *na) {
@@ -510,7 +506,7 @@ void *dacp_monitor_thread_code(__attribute__((unused)) void *na) {
while (1) { while (1) {
int result = 0; int result = 0;
int32_t the_volume; int32_t the_volume;
pthread_cleanup_debug_mutex_lock(&dacp_server_information_lock, 500000, 4); pthread_mutex_lock_and_cleanup_push(&dacp_server_information_lock);
if (dacp_server.scan_enable == 0) { if (dacp_server.scan_enable == 0) {
metadata_hub_modify_prolog(); metadata_hub_modify_prolog();
int ch = (metadata_store.dacp_server_active != 0) || int ch = (metadata_store.dacp_server_active != 0) ||
+2 -2
View File
@@ -44,6 +44,7 @@
#include "metadata/hub.h" #include "metadata/hub.h"
#include "dbus-service.h" #include "dbus-service.h"
#include "utilities/exit.h"
#ifdef CONFIG_CONVOLUTION #ifdef CONFIG_CONVOLUTION
#include <FFTConvolver/convolver.h> #include <FFTConvolver/convolver.h>
@@ -934,9 +935,8 @@ static gboolean on_handle_quit(ShairportSync *skeleton, GDBusMethodInvocation *i
__attribute__((unused)) const gchar *command, __attribute__((unused)) const gchar *command,
__attribute__((unused)) gpointer user_data) { __attribute__((unused)) gpointer user_data) {
debug(1, ">> quit request..."); debug(1, ">> quit request...");
config.quit_requested_from_glib_mainloop = 1;
g_main_loop_quit(config.glib_worker_loop);
shairport_sync_complete_quit(skeleton, invocation); shairport_sync_complete_quit(skeleton, invocation);
exit_request(EXIT_SUCCESS);
return TRUE; return TRUE;
} }
+3 -3
View File
@@ -81,11 +81,11 @@ int pc_queue_add_item(pc_queue *the_queue, const void *the_stuff, int block) {
int rc; int rc;
if (the_queue) { if (the_queue) {
if (block == 0) { if (block == 0) {
rc = debug_mutex_lock(&the_queue->pc_queue_lock, 10000, 4); rc = pthread_mutex_lock(&the_queue->pc_queue_lock);
if (rc == EBUSY) if (rc == EBUSY)
return EBUSY; return EBUSY;
} else } else
rc = debug_mutex_lock(&the_queue->pc_queue_lock, 50000, 4); rc = pthread_mutex_lock(&the_queue->pc_queue_lock);
if (rc) if (rc)
debug(1, "Error %d (\"%s\") locking for pc_queue_add_item. Block is %d.", rc, strerror(rc), debug(1, "Error %d (\"%s\") locking for pc_queue_add_item. Block is %d.", rc, strerror(rc),
block); block);
@@ -137,7 +137,7 @@ int pc_queue_add_item(pc_queue *the_queue, const void *the_stuff, int block) {
int pc_queue_get_item(pc_queue *the_queue, void *the_stuff) { int pc_queue_get_item(pc_queue *the_queue, void *the_stuff) {
int rc; int rc;
if (the_queue) { if (the_queue) {
rc = debug_mutex_lock(&the_queue->pc_queue_lock, 50000, 4); rc = pthread_mutex_lock(&the_queue->pc_queue_lock);
if (rc) if (rc)
debug(1, "metadata queue \"%s\": error locking for pc_queue_get_item", the_queue->name); debug(1, "metadata queue \"%s\": error locking for pc_queue_get_item", the_queue->name);
pthread_cleanup_push(pc_queue_cleanup_handler, (void *)the_queue); pthread_cleanup_push(pc_queue_cleanup_handler, (void *)the_queue);
+2 -2
View File
@@ -41,6 +41,7 @@
#include "metadata/hub.h" #include "metadata/hub.h"
#include "mpris-service.h" #include "mpris-service.h"
#include "utilities/exit.h"
static guint ownerID = 0; static guint ownerID = 0;
static GBusType mpris_bus_type = G_BUS_TYPE_SYSTEM; // default is the dbus system message bus static GBusType mpris_bus_type = G_BUS_TYPE_SYSTEM; // default is the dbus system message bus
@@ -234,9 +235,8 @@ void mpris_metadata_watcher(struct metadata_bundle *argc, __attribute__((unused)
static gboolean on_handle_quit(MediaPlayer2 *skeleton, GDBusMethodInvocation *invocation, static gboolean on_handle_quit(MediaPlayer2 *skeleton, GDBusMethodInvocation *invocation,
__attribute__((unused)) gpointer user_data) { __attribute__((unused)) gpointer user_data) {
debug(1, ">> quit request..."); debug(1, ">> quit request...");
config.quit_requested_from_glib_mainloop = 1;
g_main_loop_quit(config.glib_worker_loop);
media_player2_complete_quit(skeleton, invocation); media_player2_complete_quit(skeleton, invocation);
exit_request(EXIT_SUCCESS);
return TRUE; return TRUE;
} }
+24 -23
View File
@@ -4,7 +4,7 @@
* All rights reserved. * All rights reserved.
* *
* Modifications for audio synchronisation, AirPlay 2 * Modifications for audio synchronisation, AirPlay 2
* and related work, copyright (c) Mike Brady 2014--2025 * and related work, copyright (c) Mike Brady 2014--2026
* All rights reserved. * All rights reserved.
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
@@ -332,7 +332,7 @@ static void free_audio_buffers(rtsp_conn_info *conn) {
int first_possibly_missing_frame = -1; int first_possibly_missing_frame = -1;
void reset_buffer(rtsp_conn_info *conn) { void reset_buffer(rtsp_conn_info *conn) {
pthread_cleanup_debug_mutex_lock(&conn->ab_mutex, 30000, 0); pthread_mutex_lock_and_cleanup_push(&conn->ab_mutex);
ab_resync(conn); ab_resync(conn);
pthread_cleanup_pop(1); pthread_cleanup_pop(1);
#if CONFIG_FFMPEG #if CONFIG_FFMPEG
@@ -349,7 +349,7 @@ void reset_buffer(rtsp_conn_info *conn) {
size_t get_audio_buffer_occupancy(rtsp_conn_info *conn) { size_t get_audio_buffer_occupancy(rtsp_conn_info *conn) {
size_t response = 0; size_t response = 0;
pthread_cleanup_debug_mutex_lock(&conn->ab_mutex, 30000, 0); pthread_mutex_lock_and_cleanup_push(&conn->ab_mutex);
if (conn->ab_synced) { if (conn->ab_synced) {
int16_t occ = int16_t occ =
conn->ab_write - conn->ab_read; // will be zero or positive if read and write are within conn->ab_write - conn->ab_read; // will be zero or positive if read and write are within
@@ -1412,7 +1412,7 @@ seq_t get_revised_seqno(rtsp_conn_info *conn, uint32_t timestamp) {
// go back through the buffers to find the first buffer following a buffer that predates // go back through the buffers to find the first buffer following a buffer that predates
// the given timestamp, if any. // the given timestamp, if any.
seq_t revised_seqno = conn->ab_write; seq_t revised_seqno = conn->ab_write;
pthread_cleanup_debug_mutex_lock(&conn->ab_mutex, 30000, 0); pthread_mutex_lock_and_cleanup_push(&conn->ab_mutex);
int older_seqno_found = 0; int older_seqno_found = 0;
while ((older_seqno_found == 0) && (revised_seqno != conn->ab_read)) { while ((older_seqno_found == 0) && (revised_seqno != conn->ab_read)) {
revised_seqno--; revised_seqno--;
@@ -1478,18 +1478,18 @@ uint32_t player_put_packet(uint32_t ssrc, seq_t seqno, uint32_t actual_timestamp
// ignore a request to flush that has been made before the first packet... // ignore a request to flush that has been made before the first packet...
if (conn->packet_count == 0) { if (conn->packet_count == 0) {
debug_mutex_lock(&conn->flush_mutex, 1000, 4); pthread_mutex_lock(&conn->flush_mutex);
conn->flush_requested = 0; conn->flush_requested = 0;
conn->flush_rtp_timestamp = 0; conn->flush_rtp_timestamp = 0;
debug_mutex_unlock(&conn->flush_mutex, 4); pthread_mutex_unlock(&conn->flush_mutex);
} }
pthread_cleanup_debug_mutex_lock(&conn->ab_mutex, 30000, 0); pthread_mutex_lock_and_cleanup_push(&conn->ab_mutex);
uint64_t time_now = get_absolute_time_in_ns(); uint64_t time_now = get_absolute_time_in_ns();
conn->packet_count++; conn->packet_count++;
conn->packet_count_since_flush++; conn->packet_count_since_flush++;
conn->time_of_last_audio_packet = time_now; conn->time_of_last_audio_packet = time_now;
if (conn->connection_state_to_output) { // if we are supposed to be processing these packets // if (conn->connection_state_to_output) { // if we are supposed to be processing these packets
abuf_t *abuf = 0; abuf_t *abuf = 0;
if (!conn->ab_synced) { if (!conn->ab_synced) {
conn->ab_write = seqno; conn->ab_write = seqno;
@@ -1795,7 +1795,7 @@ uint32_t player_put_packet(uint32_t ssrc, seq_t seqno, uint32_t actual_timestamp
if (number_of_missing_frames == 0) if (number_of_missing_frames == 0)
first_possibly_missing_frame = conn->ab_write; first_possibly_missing_frame = conn->ab_write;
} }
} // } // remove this
pthread_cleanup_pop(1); pthread_cleanup_pop(1);
// debug_mutex_unlock(&conn->ab_mutex, 0); // debug_mutex_unlock(&conn->ab_mutex, 0);
return input_packets_used; return input_packets_used;
@@ -2044,7 +2044,7 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn, int resync_requested) {
abuf_t *curframe = NULL; abuf_t *curframe = NULL;
int notified_buffer_empty = 0; // diagnostic only int notified_buffer_empty = 0; // diagnostic only
pthread_cleanup_debug_mutex_lock(&conn->ab_mutex, 30000, 0); pthread_mutex_lock_and_cleanup_push(&conn->ab_mutex);
int wait; int wait;
long dac_delay = 0; // long because alsa returns a long long dac_delay = 0; // long because alsa returns a long
@@ -2059,6 +2059,7 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn, int resync_requested) {
// we must have timing information before we can do anything here // we must have timing information before we can do anything here
if ((have_timestamp_timing_information(conn)) && (conn->input_format_is_valid != 0)) { if ((have_timestamp_timing_information(conn)) && (conn->input_format_is_valid != 0)) {
/*
int rco = get_requested_connection_state_to_output(); int rco = get_requested_connection_state_to_output();
if (conn->connection_state_to_output != rco) { if (conn->connection_state_to_output != rco) {
@@ -2066,23 +2067,23 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn, int resync_requested) {
// change happening // change happening
if (conn->connection_state_to_output == 0) { // going off if (conn->connection_state_to_output == 0) { // going off
debug(2, "request flush because connection_state_to_output is off"); debug(2, "request flush because connection_state_to_output is off");
debug_mutex_lock(&conn->flush_mutex, 1000, 1); pthread_mutex_lock(&conn->flush_mutex);
conn->flush_requested = 1; conn->flush_requested = 1;
conn->flush_rtp_timestamp = 0; conn->flush_rtp_timestamp = 0;
debug_mutex_unlock(&conn->flush_mutex, 3); pthread_mutex_unlock(&conn->flush_mutex);
} }
} }
*/
if (config.output->is_running) if (config.output->is_running)
if (config.output->is_running() != 0) { // if the back end isn't running for any reason if (config.output->is_running() != 0) { // if the back end isn't running for any reason
debug(2, "request flush because back end is not running"); debug(2, "request flush because back end is not running");
debug_mutex_lock(&conn->flush_mutex, 1000, 0); pthread_mutex_lock(&conn->flush_mutex);
conn->flush_requested = 1; conn->flush_requested = 1;
conn->flush_rtp_timestamp = 0; conn->flush_rtp_timestamp = 0;
debug_mutex_unlock(&conn->flush_mutex, 0); pthread_mutex_unlock(&conn->flush_mutex);
} }
pthread_cleanup_debug_mutex_lock(&conn->flush_mutex, 1000, 0); pthread_mutex_lock_and_cleanup_push(&conn->flush_mutex);
if (conn->flush_requested == 1) { if (conn->flush_requested == 1) {
if (conn->flush_output_flushed == 0) { if (conn->flush_output_flushed == 0) {
#if CONFIG_FFMPEG #if CONFIG_FFMPEG
@@ -3557,7 +3558,7 @@ void *player_thread_func(void *arg) {
} }
conn->session_corrections = 0; conn->session_corrections = 0;
conn->connection_state_to_output = get_requested_connection_state_to_output(); // conn->connection_state_to_output = get_requested_connection_state_to_output();
int number_of_statistics, oldest_statistic, newest_statistic; int number_of_statistics, oldest_statistic, newest_statistic;
uint32_t frames_since_last_stats_logged = 0; uint32_t frames_since_last_stats_logged = 0;
@@ -5017,7 +5018,7 @@ static void player_send_volume_metadata(uint8_t vol_mode_both, double airplay_vo
} }
void player_volume_without_notification(double airplay_volume, rtsp_conn_info *conn) { void player_volume_without_notification(double airplay_volume, rtsp_conn_info *conn) {
debug_mutex_lock(&conn->volume_control_mutex, 5000, 4); pthread_mutex_lock(&conn->volume_control_mutex);
// first, see if we are hw only, sw only, both with hw attenuation on the top or both with sw // first, see if we are hw only, sw only, both with hw attenuation on the top or both with sw
// attenuation on top // attenuation on top
@@ -5235,7 +5236,7 @@ void player_volume_without_notification(double airplay_volume, rtsp_conn_info *c
// here, store the volume for possible use in the future // here, store the volume for possible use in the future
config.airplay_volume = airplay_volume; config.airplay_volume = airplay_volume;
conn->own_airplay_volume = airplay_volume; conn->own_airplay_volume = airplay_volume;
debug_mutex_unlock(&conn->volume_control_mutex, 4); pthread_mutex_unlock(&conn->volume_control_mutex);
} }
void player_volume(double airplay_volume, rtsp_conn_info *conn) { void player_volume(double airplay_volume, rtsp_conn_info *conn) {
@@ -5246,11 +5247,11 @@ void player_volume(double airplay_volume, rtsp_conn_info *conn) {
void do_flush(uint32_t timestamp, rtsp_conn_info *conn) { void do_flush(uint32_t timestamp, rtsp_conn_info *conn) {
debug(3, "do_flush: flush to %u.", timestamp); debug(3, "do_flush: flush to %u.", timestamp);
debug_mutex_lock(&conn->flush_mutex, 1000, 1); pthread_mutex_lock(&conn->flush_mutex);
conn->flush_requested = 1; conn->flush_requested = 1;
conn->flush_rtp_timestamp = timestamp; // flush all packets up to, but not including, this one. conn->flush_rtp_timestamp = timestamp; // flush all packets up to, but not including, this one.
reset_input_flow_metrics(conn); reset_input_flow_metrics(conn);
debug_mutex_unlock(&conn->flush_mutex, 3); pthread_mutex_unlock(&conn->flush_mutex);
} }
void player_flush(uint32_t timestamp, rtsp_conn_info *conn) { void player_flush(uint32_t timestamp, rtsp_conn_info *conn) {
@@ -5280,7 +5281,7 @@ int player_play(rtsp_conn_info *conn) {
// access to the output device (i.e. knowing that it should not be in // access to the output device (i.e. knowing that it should not be in
// use by another program at this time). // use by another program at this time).
pthread_cleanup_debug_mutex_lock(&conn->player_create_delete_mutex, 5000, 1); pthread_mutex_lock_and_cleanup_push(&conn->player_create_delete_mutex);
if (conn->player_thread == NULL) { if (conn->player_thread == NULL) {
pthread_t *pt = malloc(sizeof(pthread_t)); pthread_t *pt = malloc(sizeof(pthread_t));
if (pt == NULL) if (pt == NULL)
@@ -5307,7 +5308,7 @@ int player_stop(rtsp_conn_info *conn) {
// note -- this may be called from another connection thread. // note -- this may be called from another connection thread.
debug(2, "Connection %d: player_stop.", conn->connection_number); debug(2, "Connection %d: player_stop.", conn->connection_number);
int response = 0; // okay int response = 0; // okay
pthread_cleanup_debug_mutex_lock(&conn->player_create_delete_mutex, 5000, 4); pthread_mutex_lock_and_cleanup_push(&conn->player_create_delete_mutex);
pthread_t *pt = conn->player_thread; pthread_t *pt = conn->player_thread;
if (pt) { if (pt) {
debug(3, "player_thread cancel..."); debug(3, "player_thread cancel...");
+1 -1
View File
@@ -322,7 +322,7 @@ typedef struct {
alac_file *decoder_info; alac_file *decoder_info;
uint64_t packet_count; uint64_t packet_count;
uint64_t packet_count_since_flush; uint64_t packet_count_since_flush;
int connection_state_to_output; // int connection_state_to_output;
uint64_t first_packet_time_to_play; uint64_t first_packet_time_to_play;
int64_t time_since_play_started; // nanoseconds int64_t time_since_play_started; // nanoseconds
// stats // stats
+8 -8
View File
@@ -487,7 +487,7 @@ void *rtp_control_receiver(void *arg) {
sync_rtp_timestamp = sync_rtp_timestamp - conn->latency; sync_rtp_timestamp = sync_rtp_timestamp - conn->latency;
debug_mutex_lock(&conn->reference_time_mutex, 1000, 0); pthread_mutex_lock(&conn->reference_time_mutex);
if (conn->initial_reference_time == 0) { if (conn->initial_reference_time == 0) {
if (conn->packet_count_since_flush > 0) { if (conn->packet_count_since_flush > 0) {
@@ -535,7 +535,7 @@ void *rtp_control_receiver(void *arg) {
conn->anchor_remote_info_is_valid = 1; conn->anchor_remote_info_is_valid = 1;
conn->latency_delayed_timestamp = rtp_timestamp_less_latency; conn->latency_delayed_timestamp = rtp_timestamp_less_latency;
debug_mutex_unlock(&conn->reference_time_mutex, 0); pthread_mutex_unlock(&conn->reference_time_mutex);
conn->reference_to_previous_time_difference = conn->reference_to_previous_time_difference =
remote_time_of_sync - old_remote_reference_time; remote_time_of_sync - old_remote_reference_time;
@@ -1111,11 +1111,11 @@ void rtp_setup(SOCKADDR *local, SOCKADDR *remote, uint16_t cport, uint16_t tport
} }
void reset_ntp_anchor_info(rtsp_conn_info *conn) { void reset_ntp_anchor_info(rtsp_conn_info *conn) {
debug_mutex_lock(&conn->reference_time_mutex, 1000, 1); pthread_mutex_lock(&conn->reference_time_mutex);
conn->anchor_remote_info_is_valid = 0; conn->anchor_remote_info_is_valid = 0;
conn->anchor_rtptime = 0; conn->anchor_rtptime = 0;
conn->anchor_time = 0; conn->anchor_time = 0;
debug_mutex_unlock(&conn->reference_time_mutex, 3); pthread_mutex_unlock(&conn->reference_time_mutex);
} }
int have_ntp_timing_information(rtsp_conn_info *conn) { int have_ntp_timing_information(rtsp_conn_info *conn) {
@@ -1132,7 +1132,7 @@ int frame_to_ntp_local_time(uint32_t timestamp, uint64_t *time, rtsp_conn_info *
// a zero result is good // a zero result is good
if (conn->anchor_remote_info_is_valid == 0) if (conn->anchor_remote_info_is_valid == 0)
debug(1, "no anchor information"); debug(1, "no anchor information");
debug_mutex_lock(&conn->reference_time_mutex, 1000, 0); pthread_mutex_lock(&conn->reference_time_mutex);
int result = -1; int result = -1;
if (conn->anchor_remote_info_is_valid != 0) { if (conn->anchor_remote_info_is_valid != 0) {
uint64_t remote_time_of_timestamp; uint64_t remote_time_of_timestamp;
@@ -1151,13 +1151,13 @@ int frame_to_ntp_local_time(uint32_t timestamp, uint64_t *time, rtsp_conn_info *
*time = remote_time_of_timestamp - local_to_remote_time_difference_now(conn); *time = remote_time_of_timestamp - local_to_remote_time_difference_now(conn);
result = 0; result = 0;
} }
debug_mutex_unlock(&conn->reference_time_mutex, 0); pthread_mutex_unlock(&conn->reference_time_mutex);
return result; return result;
} }
int local_ntp_time_to_frame(uint64_t time, uint32_t *frame, rtsp_conn_info *conn) { int local_ntp_time_to_frame(uint64_t time, uint32_t *frame, rtsp_conn_info *conn) {
// a zero result is good // a zero result is good
debug_mutex_lock(&conn->reference_time_mutex, 1000, 0); pthread_mutex_lock(&conn->reference_time_mutex);
int result = -1; int result = -1;
if (conn->anchor_remote_info_is_valid != 0) { if (conn->anchor_remote_info_is_valid != 0) {
// first, get from [local] time to remote time. // first, get from [local] time to remote time.
@@ -1176,7 +1176,7 @@ int local_ntp_time_to_frame(uint64_t time, uint32_t *frame, rtsp_conn_info *conn
*frame = new_frame; *frame = new_frame;
result = 0; result = 0;
} }
debug_mutex_unlock(&conn->reference_time_mutex, 0); pthread_mutex_unlock(&conn->reference_time_mutex);
return result; return result;
} }
+13 -13
View File
@@ -148,7 +148,7 @@ int RTSP_connection_index = 1;
// keep track of the threads we have spawned so we can join() them // keep track of the threads we have spawned so we can join() them
static int nconns = 0; static int nconns = 0;
static void track_thread(rtsp_conn_info *conn) { static void track_thread(rtsp_conn_info *conn) {
debug_mutex_lock(&conns_lock, 1000000, 4); pthread_mutex_lock(&conns_lock);
// look for an empty slot first // look for an empty slot first
int i = 0; int i = 0;
int found = 0; int found = 0;
@@ -170,7 +170,7 @@ static void track_thread(rtsp_conn_info *conn) {
die("could not reallocate memory for conns"); die("could not reallocate memory for conns");
} }
} }
debug_mutex_unlock(&conns_lock, 4); pthread_mutex_unlock(&conns_lock);
} }
// note: connection numbers start at 1, so an except_this_one value of zero means "all threads" // note: connection numbers start at 1, so an except_this_one value of zero means "all threads"
@@ -178,7 +178,7 @@ void cancel_all_RTSP_threads(airplay_stream_c stream_category, int except_this_o
// if the stream category is unspecified_stream_category // if the stream category is unspecified_stream_category
// all categories are elegible for cancellation // all categories are elegible for cancellation
// otherwise just the category itself // otherwise just the category itself
debug_mutex_lock(&conns_lock, 1000000, 3); pthread_mutex_lock(&conns_lock);
int i; int i;
for (i = 0; i < nconns; i++) { for (i = 0; i < nconns; i++) {
if ((conns[i] != NULL) && (conns[i]->running != 0) && if ((conns[i] != NULL) && (conns[i]->running != 0) &&
@@ -203,7 +203,7 @@ void cancel_all_RTSP_threads(airplay_stream_c stream_category, int except_this_o
conns[i] = NULL; conns[i] = NULL;
} }
} }
debug_mutex_unlock(&conns_lock, 3); pthread_mutex_unlock(&conns_lock);
} }
int old_connection_count = -1; int old_connection_count = -1;
@@ -214,7 +214,7 @@ void cleanup_threads(void) {
int i; int i;
int connection_count = 0; int connection_count = 0;
// debug(2, "culling threads."); // debug(2, "culling threads.");
debug_mutex_lock(&conns_lock, 1000000, 4); pthread_mutex_lock(&conns_lock);
for (i = 0; i < nconns; i++) { for (i = 0; i < nconns; i++) {
if ((conns[i] != NULL) && (conns[i]->running == 0)) { if ((conns[i] != NULL) && (conns[i]->running == 0)) {
debug(4, "found RTSP connection thread %d in a non-running state.", debug(4, "found RTSP connection thread %d in a non-running state.",
@@ -230,7 +230,7 @@ void cleanup_threads(void) {
connection_count++; connection_count++;
} }
} }
debug_mutex_unlock(&conns_lock, 4); pthread_mutex_unlock(&conns_lock);
if (old_connection_count != connection_count) { if (old_connection_count != connection_count) {
if (connection_count == 0) { if (connection_count == 0) {
@@ -247,7 +247,7 @@ void cleanup_threads(void) {
int terminate_conn(int connection_number) { int terminate_conn(int connection_number) {
// this will look for a connection by number, cancel it, join it and delete it. // this will look for a connection by number, cancel it, join it and delete it.
int found = 0; int found = 0;
debug_mutex_lock(&conns_lock, 1000000, 4); pthread_mutex_lock(&conns_lock);
// look for an empty slot first // look for an empty slot first
int i = 0; int i = 0;
while ((i < nconns) && (found == 0)) { while ((i < nconns) && (found == 0)) {
@@ -259,7 +259,7 @@ int terminate_conn(int connection_number) {
} else } else
i++; i++;
} }
debug_mutex_unlock(&conns_lock, 4); pthread_mutex_unlock(&conns_lock);
return found; return found;
} }
@@ -1341,7 +1341,7 @@ void handle_flushbuffered(rtsp_conn_info *conn, rtsp_message *req, rtsp_message
debug(4, "flushUntilTS is %" PRId64 ".", flushUntilTS); debug(4, "flushUntilTS is %" PRId64 ".", flushUntilTS);
} }
debug_mutex_lock(&conn->flush_mutex, 1000, 4); pthread_mutex_lock(&conn->flush_mutex);
if (flushFromValid == 0) { if (flushFromValid == 0) {
// an immediate flush is requested // an immediate flush is requested
@@ -1387,7 +1387,7 @@ void handle_flushbuffered(rtsp_conn_info *conn, rtsp_message *req, rtsp_message
} }
} }
debug_mutex_unlock(&conn->flush_mutex, 4); pthread_mutex_unlock(&conn->flush_mutex);
plist_free(messagePlist); plist_free(messagePlist);
} }
@@ -1465,7 +1465,7 @@ void handle_setrateanchori(rtsp_conn_info *conn, rtsp_message *req, rtsp_message
uint64_t rate; uint64_t rate;
plist_get_uint_val(item, &rate); plist_get_uint_val(item, &rate);
debug(3, "anchor rate 0x%016" PRIx64 ".", rate); debug(3, "anchor rate 0x%016" PRIx64 ".", rate);
pthread_cleanup_debug_mutex_lock(&conn->flush_mutex, 1000, 4); pthread_mutex_lock_and_cleanup_push(&conn->flush_mutex);
conn->ap2_rate = rate; conn->ap2_rate = rate;
if ((rate & 1) != 0) { if ((rate & 1) != 0) {
ptp_send_control_message_string( ptp_send_control_message_string(
@@ -4168,14 +4168,14 @@ static void *rtsp_conversation_thread_func(void *pconn) {
// check to see if a conn has been zeroed // check to see if a conn has been zeroed
debug_mutex_lock(&conns_lock, 1000000, 4); pthread_mutex_lock(&conns_lock);
int i; int i;
for (i = 0; i < nconns; i++) { for (i = 0; i < nconns; i++) {
if ((conns[i] != NULL) && (conns[i]->connection_number == 0)) { if ((conns[i] != NULL) && (conns[i]->connection_number == 0)) {
debug(1, "conns[%d] has a Connection Number of 0!", i); debug(1, "conns[%d] has a Connection Number of 0!", i);
} }
} }
debug_mutex_unlock(&conns_lock, 4); pthread_mutex_unlock(&conns_lock);
reply = rtsp_read_request(conn, &req); reply = rtsp_read_request(conn, &req);
if (reply == rtsp_read_request_response_ok) { if (reply == rtsp_read_request_response_ok) {
+9 -4
View File
@@ -21,7 +21,7 @@ general =
// The default is "auto", which means that the AirPlay 2 service will be provided if NQPTP is present and "classic" AirPlay will be provided otherwise, with "(Classic)" appended to the service name (see above). // The default is "auto", which means that the AirPlay 2 service will be provided if NQPTP is present and "classic" AirPlay will be provided otherwise, with "(Classic)" appended to the service name (see above).
// service_type = "auto"; // This can be "auto", "airplay2" or "classic". // service_type = "auto"; // This can be "auto", "airplay2" or "classic".
// The interpolation setting below controls how Shairport Sync adds or removes frames of audio to keep in sync. // The interpolation setting below controls how Shairport Sync adds or removes frames of audio to keep in sync.
// "auto" (default) measures the processor's floating point speed and chooses "soxr" if available and it is fast enough. Otherwise, "vernier" is selected. // "auto" (default) measures the processor's floating point speed and chooses "soxr" if available and if the processor is fast enough. Otherwise, "vernier" is selected.
// "vernier" recodes a packet of frames to a new packet containing more or fewer frames. This is recommended for low powered devices. // "vernier" recodes a packet of frames to a new packet containing more or fewer frames. This is recommended for low powered devices.
// "soxr" uses the SoX library to recode a packet of frames to a new packet containing more or fewer frames. This needs a processor with fast floating point capability. // "soxr" uses the SoX library to recode a packet of frames to a new packet containing more or fewer frames. This needs a processor with fast floating point capability.
// "basic" causes the simple removal or insertion of frames in a packet of frames. Not recommended. // "basic" causes the simple removal or insertion of frames in a packet of frames. Not recommended.
@@ -106,13 +106,18 @@ sessioncontrol =
// run_this_before_play_begins = "/full/path/to/application and args"; // make sure the application has executable permission. If it's a script, include the shebang (#!/bin/...) on the first line // run_this_before_play_begins = "/full/path/to/application and args"; // make sure the application has executable permission. If it's a script, include the shebang (#!/bin/...) on the first line
// run_this_after_play_ends = "/full/path/to/application and args"; // make sure the application has executable permission. If it's a script, include the shebang (#!/bin/...) on the first line // run_this_after_play_ends = "/full/path/to/application and args"; // make sure the application has executable permission. If it's a script, include the shebang (#!/bin/...) on the first line
// run_this_if_an_unfixable_error_is_detected = "/full/path/to/application and args"; // if a problem occurs that can't be cleared by Shairport Sync itself, hook a program on here to deal with it. // run_this_if_an_unfixable_error_is_detected = "/full/path/to/application and args"; // if a problem occurs that can't be cleared by Shairport Sync itself, hook a handler program on here to deal with it.
// An error code-string is passed as the last argument. // An error code-string is passed as the last argument.
// Shairport Sync will always wait for this handler to complete.
// Shairport Sync will not exit automatically after executing this handler. To exit, the handler must do it itself, perhaps via a script with "/usr/bin/kill $PPID" as its last line.
// Many of these "unfixable" problems are caused by malfunctioning output devices, and sometimes it is necessary to restart the whole device to clear the problem. // Many of these "unfixable" problems are caused by malfunctioning output devices, and sometimes it is necessary to restart the whole device to clear the problem.
// You could hook on a program to do this automatically, but beware -- the device may then power off and restart without warning! // You could hook on a program to do this automatically, but beware -- the device may then power off and restart without warning!
// wait_for_completion = "no"; // set to "yes" to get Shairport Sync to wait until the "run_this..." applications have terminated before continuing
// allow_session_interruption = "no"; // set to "yes" to allow another device to interrupt Shairport Sync while it's playing from an existing audio source // wait_for_completion = "no"; // set to "yes" to get Shairport Sync to wait until the "run_this..." applications have terminated before continuing,
// except the "run_this_if_an_unfixable_error_is_detected" handler, which will always wait for completion, irrespective of this setting.
// allow_session_interruption = "no"; // (Classic AirPlay only) Set to "yes" to allow another device to interrupt Shairport Sync while it's playing from an existing audio source
// session_timeout = 60; // wait for this number of seconds after a source disappears before terminating the session and becoming available again. // session_timeout = 60; // wait for this number of seconds after a source disappears before terminating the session and becoming available again.
}; };
+1 -1
View File
@@ -10,7 +10,7 @@ StartLimitIntervalSec=300
StartLimitBurst=5 StartLimitBurst=5
[Service] [Service]
ExecStart=@prefix@/bin/shairport-sync --log-to-syslog ExecStart=@prefix@/bin/shairport-sync
User=shairport-sync User=shairport-sync
Group=shairport-sync Group=shairport-sync
LimitRTPRIO=5 LimitRTPRIO=5
+40 -22
View File
@@ -83,6 +83,7 @@
#include "rtp.h" #include "rtp.h"
#include "rtsp.h" #include "rtsp.h"
#include "utilities/string_utilities.h" #include "utilities/string_utilities.h"
#include "utilities/exit.h"
#if defined(CONFIG_DACP_CLIENT) #if defined(CONFIG_DACP_CLIENT)
#include "dacp.h" #include "dacp.h"
@@ -140,6 +141,10 @@ int this_is_the_daemon_process = 0;
pthread_t rtsp_listener_thread; pthread_t rtsp_listener_thread;
#if defined(CONFIG_DBUS_INTERFACE) || defined(CONFIG_MPRIS_INTERFACE)
GMainLoop *glib_worker_loop = NULL;
#endif
int killOption = 0; int killOption = 0;
int daemonisewith = 0; int daemonisewith = 0;
int daemonisewithout = 0; int daemonisewithout = 0;
@@ -453,7 +458,7 @@ int parse_options(int argc, char **argv) {
break; break;
case 'u': case 'u':
inform("Warning: the option -u is no longer needed and is deprecated. Debug and statistics " inform("Warning: the option -u is no longer needed and is deprecated. Debug and statistics "
"output to STDERR is now the default. Use \"--log-to-syslog\" to revert."); "output to STDERR is now the default.");
break; break;
case 'D': case 'D':
inform("Warning: the option -D or --disconnectFromOutput is deprecated."); inform("Warning: the option -D or --disconnectFromOutput is deprecated.");
@@ -489,6 +494,7 @@ int parse_options(int argc, char **argv) {
} }
} }
if (c < -1) { if (c < -1) {
debug(1, "Oops");
die("%s: %s", poptBadOption(optCon, POPT_BADOPTION_NOALIAS), poptStrerror(c)); die("%s: %s", poptBadOption(optCon, POPT_BADOPTION_NOALIAS), poptStrerror(c));
} }
@@ -503,17 +509,16 @@ int parse_options(int argc, char **argv) {
} }
if (log_to_syslog_selected) { if (log_to_syslog_selected) {
// if this was the first command line argument, it'll already have been chosen inform("the diagnostic \"log-to-syslog\" command_line_option is obsolete and is ignored. All logging is to STDERR, which is directed to the system log when Shairport Sync is running as a service.");
if (log_to_syslog_select_is_first_command_line_argument == 0) { /*
inform("Suggestion: make \"--log-to-syslog\" the first command line argument to ensure "
"messages go to the syslog right from the beginning.");
}
#ifdef CONFIG_LIBDAEMON #ifdef CONFIG_LIBDAEMON
log_to_default = 0; // a specific log output modality has been selected. log_to_default = 0; // a specific log output modality has been selected.
#endif #endif
log_to_syslog(); log_to_syslog();
*/
} }
#ifdef CONFIG_LIBDAEMON #ifdef CONFIG_LIBDAEMON
if ((daemonisewith) && (daemonisewithout)) if ((daemonisewith) && (daemonisewithout))
die("Select either daemonize_with_pid_file or daemonize_without_pid_file -- you have selected " die("Select either daemonize_with_pid_file or daemonize_without_pid_file -- you have selected "
@@ -857,6 +862,7 @@ int parse_options(int argc, char **argv) {
/* Get the diagnostics output default. */ /* Get the diagnostics output default. */
if (config_lookup_string(config.cfg, "diagnostics.log_output_to", &str)) { if (config_lookup_string(config.cfg, "diagnostics.log_output_to", &str)) {
/*
#ifdef CONFIG_LIBDAEMON #ifdef CONFIG_LIBDAEMON
log_to_default = 0; // a specific log output modality has been selected. log_to_default = 0; // a specific log output modality has been selected.
#endif #endif
@@ -871,7 +877,11 @@ int parse_options(int argc, char **argv) {
config.log_fd = -1; config.log_fd = -1;
log_to_file(); log_to_file();
} }
*/
warn("the diagnostic \"log_output_to\" setting is obsolete and is ignored. All logging is to STDERR, which is directed to the system log when Shairport Sync is running as a service.");
} }
/* Get the ignore_volume_control setting. */ /* Get the ignore_volume_control setting. */
if (config_lookup_string(config.cfg, "general.ignore_volume_control", &str)) { if (config_lookup_string(config.cfg, "general.ignore_volume_control", &str)) {
if (strcasecmp(str, "no") == 0) if (strcasecmp(str, "no") == 0)
@@ -1838,7 +1848,7 @@ GThread *glib_worker_thread = NULL;
gpointer glib_worker_thread_function(__attribute__((unused)) gpointer data) { gpointer glib_worker_thread_function(__attribute__((unused)) gpointer data) {
// use the default global-default main context // use the default global-default main context
config.glib_worker_loop = g_main_loop_new(NULL, FALSE); glib_worker_loop = g_main_loop_new(NULL, FALSE);
// debug(1, "glib worker thread started."); // debug(1, "glib worker thread started.");
@@ -1853,7 +1863,7 @@ gpointer glib_worker_thread_function(__attribute__((unused)) gpointer data) {
// debug(1, "g_main_loop_run start."); // debug(1, "g_main_loop_run start.");
g_main_loop_run(config.glib_worker_loop); g_main_loop_run(glib_worker_loop);
// debug(1, "g_main_loop_run exit."); // debug(1, "g_main_loop_run exit.");
@@ -1869,11 +1879,7 @@ gpointer glib_worker_thread_function(__attribute__((unused)) gpointer data) {
// debug(1, "stopped D-Bus service"); // debug(1, "stopped D-Bus service");
#endif #endif
g_main_loop_unref(config.glib_worker_loop); g_main_loop_unref(glib_worker_loop);
if (config.quit_requested_from_glib_mainloop != 0) {
debug(2, "glib_mainloop_thread_function asking for exit");
exit(EXIT_SUCCESS);
}
return NULL; return NULL;
} }
@@ -1927,10 +1933,15 @@ void exit_function() {
#endif #endif
#if defined(CONFIG_DBUS_INTERFACE) || defined(CONFIG_MPRIS_INTERFACE) #if defined(CONFIG_DBUS_INTERFACE) || defined(CONFIG_MPRIS_INTERFACE)
if ((glib_worker_thread != NULL) && (config.quit_requested_from_glib_mainloop == 0)) { if (glib_worker_loop != NULL) { // may not have been initialised
g_main_loop_quit(config.glib_worker_loop); g_main_loop_quit(glib_worker_loop);
debug(2, "GMainLoop stop requested"); debug(2, "GMainLoop stop requested");
} }
if (glib_worker_thread != NULL) {
g_thread_join(glib_worker_thread);
debug(1, "GLib worker thread joined");
}
#endif #endif
#ifdef CONFIG_METADATA_HUB #ifdef CONFIG_METADATA_HUB
@@ -2063,12 +2074,12 @@ void handle_sigchld(__attribute__((unused)) int sig) {
// for clean exits // for clean exits
void intHandler(__attribute__((unused)) int k) { void intHandler(__attribute__((unused)) int k) {
debug(2, "exit on SIGINT"); debug(2, "exit on SIGINT");
exit(EXIT_SUCCESS); exit_request(EXIT_SUCCESS);
} }
void termHandler(__attribute__((unused)) int k) { void termHandler(__attribute__((unused)) int k) {
debug(2, "exit on SIGTERM"); debug(2, "exit on SIGTERM");
exit(EXIT_SUCCESS); exit_request(EXIT_SUCCESS);
} }
void _display_config(const char *filename, const int linenumber, __attribute__((unused)) int argc, void _display_config(const char *filename, const int linenumber, __attribute__((unused)) int argc,
@@ -2330,9 +2341,10 @@ const char *av_channel_layout_name(uint64_t channel_layout) {
#endif #endif
int main(int argc, char **argv) { int main(int argc, char **argv) {
exit_init(); // initialise the exit handler to give us a clean safe exit on request
// initialise debug messages stuff -- level 0, no elapsed time, relative time, file and line // initialise debug messages stuff -- level 0, no elapsed time, relative time, file and line
// debug_init(int level, int show_elapsed_time, int show_relative_time, int show_file_and_line) // debug_init(int level, int show_elapsed_time, int show_relative_time, int show_file_and_line)
debug_init(0, 0, 1, 1); debug_init(0, 0, 1, 1, exit_request);
memset(&config, 0, sizeof(config)); // also clears all strings, BTW memset(&config, 0, sizeof(config)); // also clears all strings, BTW
/* Check if we are called with -V or --version parameter */ /* Check if we are called with -V or --version parameter */
if (argc >= 2 && ((strcmp(argv[1], "-V") == 0) || (strcmp(argv[1], "--version") == 0))) { if (argc >= 2 && ((strcmp(argv[1], "-V") == 0) || (strcmp(argv[1], "--version") == 0))) {
@@ -2375,13 +2387,15 @@ int main(int argc, char **argv) {
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
} }
/* Check if we are called with -log-to-syslog */ /*
// Check if we are called with -log-to-syslog
if (argc >= 2 && (strcmp(argv[1], "--log-to-syslog") == 0)) { if (argc >= 2 && (strcmp(argv[1], "--log-to-syslog") == 0)) {
log_to_syslog_select_is_first_command_line_argument = 1; log_to_syslog_select_is_first_command_line_argument = 1;
log_to_syslog(); log_to_syslog();
} else { } else {
log_to_stderr(); log_to_stderr();
} }
*/
pid = getpid(); pid = getpid();
config.log_fd = -1; config.log_fd = -1;
@@ -2453,8 +2467,8 @@ int main(int argc, char **argv) {
config.packet_stuffing = ST_vernier; // you need to explicitly ask for "basic" (ST_basic) config.packet_stuffing = ST_vernier; // you need to explicitly ask for "basic" (ST_basic)
#endif #endif
set_requested_connection_state_to_output( // set_requested_connection_state_to_output(
1); // we expect to be able to connect to the output device // 1); // we expect to be able to connect to the output device
config.audio_backend_buffer_desired_length = 0.15; // seconds config.audio_backend_buffer_desired_length = 0.15; // seconds
config.audio_decoded_buffer_desired_length = 0.75; // seconds config.audio_decoded_buffer_desired_length = 0.75; // seconds
config.udp_port_base = 6001; config.udp_port_base = 6001;
@@ -3348,6 +3362,10 @@ int main(int argc, char **argv) {
// you'll see two threads named "listener" or whatever... // you'll see two threads named "listener" or whatever...
named_pthread_create(&rtsp_listener_thread, NULL, &rtsp_listen_loop, NULL, "listener"); named_pthread_create(&rtsp_listener_thread, NULL, &rtsp_listen_loop, NULL, "listener");
atexit(exit_rtsp_listener); atexit(exit_rtsp_listener);
pthread_join(rtsp_listener_thread, NULL);
// wait forever...
while (1) {
usleep(1000000);
}
return 0; return 0;
} }
+3 -3
View File
@@ -35,7 +35,7 @@
ssize_t buffered_read(buffered_tcp_desc *descriptor, void *buf, size_t count, ssize_t buffered_read(buffered_tcp_desc *descriptor, void *buf, size_t count,
size_t *bytes_remaining) { size_t *bytes_remaining) {
ssize_t response = -1; ssize_t response = -1;
if (debug_mutex_lock(&descriptor->mutex, 50000, 4) != 0) if (pthread_mutex_lock(&descriptor->mutex) != 0)
debug(1, "problem with mutex"); debug(1, "problem with mutex");
pthread_cleanup_push(mutex_unlock, (void *)&descriptor->mutex); pthread_cleanup_push(mutex_unlock, (void *)&descriptor->mutex);
// wipe the slate dlean before reading... // wipe the slate dlean before reading...
@@ -116,7 +116,7 @@ void *buffered_tcp_reader(void *arg) {
do { do {
int have_time_to_sleep = 0; int have_time_to_sleep = 0;
if (debug_mutex_lock(&descriptor->mutex, 500000, 4) != 0) if (pthread_mutex_lock(&descriptor->mutex) != 0)
debug(1, "problem with mutex"); debug(1, "problem with mutex");
pthread_cleanup_push(mutex_unlock, (void *)&descriptor->mutex); pthread_cleanup_push(mutex_unlock, (void *)&descriptor->mutex);
while (descriptor->buffer_occupancy == descriptor->buffer_max_size) { while (descriptor->buffer_occupancy == descriptor->buffer_max_size) {
@@ -150,7 +150,7 @@ void *buffered_tcp_reader(void *arg) {
nread = recv(fd, descriptor->eoq, bytes_to_request, 0); nread = recv(fd, descriptor->eoq, bytes_to_request, 0);
// debug(1, "Received %d bytes for a buffer size of %d bytes.",nread, // debug(1, "Received %d bytes for a buffer size of %d bytes.",nread,
// descriptor->buffer_occupancy + nread); // descriptor->buffer_occupancy + nread);
if (debug_mutex_lock(&descriptor->mutex, 50000, 4) != 0) if (pthread_mutex_lock(&descriptor->mutex) != 0)
debug(1, "problem with not empty mutex"); debug(1, "problem with not empty mutex");
pthread_cleanup_push(mutex_unlock, (void *)&descriptor->mutex); pthread_cleanup_push(mutex_unlock, (void *)&descriptor->mutex);
if (nread < 0) { if (nread < 0) {
+11 -2
View File
@@ -42,6 +42,8 @@ int debugger_show_file_and_line = 1;
static uint64_t ns_time_at_startup = 0; static uint64_t ns_time_at_startup = 0;
static uint64_t ns_time_at_last_debug_message; static uint64_t ns_time_at_last_debug_message;
void (*local_exit_requester)(const int exit_status) = NULL;
// always lock use this when accessing the ns_time_at_last_debug_message // always lock use this when accessing the ns_time_at_last_debug_message
static pthread_mutex_t debug_timing_lock = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t debug_timing_lock = PTHREAD_MUTEX_INITIALIZER;
@@ -57,13 +59,14 @@ uint64_t debug_get_absolute_time_in_ns() {
return time_now_ns; return time_now_ns;
} }
void debug_init(int level, int show_elapsed_time, int show_relative_time, int show_file_and_line) { void debug_init(int level, int show_elapsed_time, int show_relative_time, int show_file_and_line, void (*exit_requester)(const int exit_status)) {
ns_time_at_startup = debug_get_absolute_time_in_ns(); ns_time_at_startup = debug_get_absolute_time_in_ns();
ns_time_at_last_debug_message = ns_time_at_startup; ns_time_at_last_debug_message = ns_time_at_startup;
debuglev = level; debuglev = level;
debugger_show_elapsed_time = show_elapsed_time; debugger_show_elapsed_time = show_elapsed_time;
debugger_show_relative_time = show_relative_time; debugger_show_relative_time = show_relative_time;
debugger_show_file_and_line = show_file_and_line; debugger_show_file_and_line = show_file_and_line;
local_exit_requester = exit_requester;
} }
int debug_level() { return debuglev; }; int debug_level() { return debuglev; };
@@ -144,9 +147,15 @@ void _die(const char *filename, const int linenumber, const char *format, ...) {
// syslog(LOG_ERR, "%s", b); // syslog(LOG_ERR, "%s", b);
fprintf(stderr, "%s\n", b); fprintf(stderr, "%s\n", b);
pthread_setcancelstate(oldState, NULL); pthread_setcancelstate(oldState, NULL);
usleep(2000000); //allow some time for printing of message if (local_exit_requester != NULL) {
local_exit_requester(EXIT_FAILURE);
// wait for the exit request to be honoured
usleep(1000000);
// if not, head for the hills
fprintf(stderr, "fatal error: exit cleanup could not be completed.\n");
_Exit(EXIT_FAILURE); _Exit(EXIT_FAILURE);
} }
}
void _warn(const char *filename, const int linenumber, const char *format, ...) { void _warn(const char *filename, const int linenumber, const char *format, ...) {
int oldState; int oldState;
+2 -2
View File
@@ -1,7 +1,7 @@
/* /*
MIT License MIT License
Copyright (c) 2023--2025 Mike Brady 4265913+mikebrady@users.noreply.github.com Copyright (c) 2023--2026 Mike Brady 4265913+mikebrady@users.noreply.github.com
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
@@ -38,7 +38,7 @@ SOFTWARE.
// level 0 is no messages, level 3 is most messages // level 0 is no messages, level 3 is most messages
EXTERNC void debug_init(int level, int show_elapsed_time, int show_relative_time, EXTERNC void debug_init(int level, int show_elapsed_time, int show_relative_time,
int show_file_and_line); int show_file_and_line, void (*exit_requester)(const int exit_status));
EXTERNC int debug_level(); EXTERNC int debug_level();
EXTERNC void set_debug_level(int level); EXTERNC void set_debug_level(int level);
+66
View File
@@ -0,0 +1,66 @@
/*
MIT License
Copyright (c) 2023--2026 Mike Brady 4265913+mikebrady@users.noreply.github.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
// exit_init() starts a thread what waits for the exit_request_flag to
// be set to non-zero.
// It then runs the standard exit(exit_status) to
// terminate the program, running all the atexit handlers first.
// exit_request() can be called from anywhere in the program
// including a SIG handler and
// the thread will take care of terminating the program cleanly.
// pass in EXIT_SUCCESS or EXIT_FAILURE in the request.
#include <signal.h> // for sig_atomic_t
#include <stdlib.h> // for EXIT_SUCCESS
#include <string.h> // for memset
#include <unistd.h> // for usleep
#include <pthread.h>
#include "exit.h"
#include "common.h"
volatile sig_atomic_t exit_request_flag = 0;
volatile sig_atomic_t exit_status = EXIT_SUCCESS;
pthread_t exit_manager_thread;
void *exit_manager(__attribute__((unused)) void *arg) {
while(exit_request_flag == 0) {
usleep(100000);
}
exit(exit_status);
return NULL;
}
void exit_init() {
memset(&exit_manager_thread, 0, sizeof(pthread_t));
named_pthread_create(&exit_manager_thread, NULL, &exit_manager, NULL, "exit_manager");
}
void exit_request(const int exit_status_requested) {
exit_status = exit_status_requested; // EXIT_SUCCESS or EXIT_FAILURE
exit_request_flag = 1; // ask for exit
}
+30
View File
@@ -0,0 +1,30 @@
/*
MIT License
Copyright (c) 2023--2026 Mike Brady 4265913+mikebrady@users.noreply.github.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#pragma once
#include <stddef.h>
void exit_init();
void exit_request(const int exit_status); // call this to ask for the program to be exited.
// pass in EXIT_SUCCESS or EXIT_FAILURE.
+2 -2
View File
@@ -52,7 +52,7 @@ int _safe_socket_close(const char *filename, const int linenumber, int *sockfd)
int result = 0; int result = 0;
int oldstate; int oldstate;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate); pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate);
debug_mutex_lock(&safe_socket_lock, 1000000, 1); pthread_mutex_lock(&safe_socket_lock);
if (*sockfd == 0) { if (*sockfd == 0) {
_debug(filename, linenumber, 1, "_safe_socket_close: socket is zero!"); _debug(filename, linenumber, 1, "_safe_socket_close: socket is zero!");
} }
@@ -64,7 +64,7 @@ int _safe_socket_close(const char *filename, const int linenumber, int *sockfd)
} else { } else {
_debug(filename, linenumber, 1, "_safe_socket_close: socket already closed!"); _debug(filename, linenumber, 1, "_safe_socket_close: socket already closed!");
} }
debug_mutex_unlock(&safe_socket_lock, 4); pthread_mutex_unlock(&safe_socket_lock);
pthread_setcancelstate(oldstate, NULL); pthread_setcancelstate(oldstate, NULL);
return result; return result;
} }
+4 -4
View File
@@ -45,7 +45,7 @@ static pthread_mutex_t reference_counter_lock = PTHREAD_MUTEX_INITIALIZER;
static int msg_indexes = 1; static int msg_indexes = 1;
void msg_retain(rtsp_message *msg) { void msg_retain(rtsp_message *msg) {
int rc = debug_mutex_lock(&reference_counter_lock, 500000, 4); int rc = pthread_mutex_lock(&reference_counter_lock);
if (rc) if (rc)
debug(1, "Error %d locking reference counter lock", rc); debug(1, "Error %d locking reference counter lock", rc);
if (msg > (rtsp_message *)0x00010000) { if (msg > (rtsp_message *)0x00010000) {
@@ -63,7 +63,7 @@ void msg_retain(rtsp_message *msg) {
rtsp_message *msg_init(void) { rtsp_message *msg_init(void) {
// no thread cancellation points here // no thread cancellation points here
int rc = debug_mutex_lock(&reference_counter_lock, 500000, 4); int rc = pthread_mutex_lock(&reference_counter_lock);
if (rc) if (rc)
debug(1, "Error %d locking reference counter lock", rc); debug(1, "Error %d locking reference counter lock", rc);
@@ -128,7 +128,7 @@ void _debug_print_msg_headers(rtsp_conn_info *conn, const char *filename, const
} }
void msg_free(rtsp_message **msgh) { void msg_free(rtsp_message **msgh) {
debug_mutex_lock(&reference_counter_lock, 1000, 0); pthread_mutex_lock(&reference_counter_lock);
if (*msgh > (rtsp_message *)0x00010000) { if (*msgh > (rtsp_message *)0x00010000) {
rtsp_message *msg = *msgh; rtsp_message *msg = *msgh;
msg->referenceCount--; msg->referenceCount--;
@@ -161,7 +161,7 @@ void msg_free(rtsp_message **msgh) {
"%" PRIxPTR ".", "%" PRIxPTR ".",
(uintptr_t)*msgh); (uintptr_t)*msgh);
} }
debug_mutex_unlock(&reference_counter_lock, 0); pthread_mutex_unlock(&reference_counter_lock);
} }
int msg_handle_line(rtsp_message **pmsg, char *line) { int msg_handle_line(rtsp_message **pmsg, char *line) {