diff --git a/Makefile.am b/Makefile.am index 64763533..96bf0cb4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 \ rtsp.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 AM_CXXFLAGS = -I/usr/local/include -Wno-multichar -Wall -Wextra -Wno-deprecated-declarations -pthread -DSYSCONFDIR=\"$(sysconfdir)\" diff --git a/activity_monitor.c b/activity_monitor.c index 06fe4fee..368e8589 100644 --- a/activity_monitor.c +++ b/activity_monitor.c @@ -131,7 +131,7 @@ void activity_monitor_signify_activity(int active) { pthread_mutex_unlock(&activity_monitor_mutex); } // 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_cleanup_pop(1); // release the mutex } diff --git a/ap2_buffered_audio_processor.c b/ap2_buffered_audio_processor.c index 47f89c5d..1bf18882 100644 --- a/ap2_buffered_audio_processor.c +++ b/ap2_buffered_audio_processor.c @@ -344,8 +344,7 @@ void *rtp_buffered_audio_processor(void *arg) { } if (finished == 0) { - pthread_cleanup_debug_mutex_lock(&conn->flush_mutex, 25000, - 4); // 25 ms is a long time to wait! + pthread_mutex_lock_and_cleanup_push(&conn->flush_mutex); if (blocks_read != 0) { if (conn->ap2_immediate_flush_requested != 0) { if (ap2_immediate_flush_requested == 0) { diff --git a/ap2_event_message_handler.c b/ap2_event_message_handler.c index 94a84d0d..457416a8 100644 --- a/ap2_event_message_handler.c +++ b/ap2_event_message_handler.c @@ -44,7 +44,7 @@ void decodeAndLogPlist(plist_t plist_to_log) { // number if okay ssize_t ap2_event_port_send_message(rtsp_conn_info *conn, char *data, size_t data_length) { 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); if (conn->event_channel_fd != 0) { result = write_encrypted(conn->event_channel_fd, &conn->ap2_pairing_context.event_cipher_bundle, diff --git a/ap2_event_receiver.c b/ap2_event_receiver.c index 29f23970..f7923a40 100644 --- a/ap2_event_receiver.c +++ b/ap2_event_receiver.c @@ -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 send_ssnc_metadata('disc', conn->client_ip_string, strlen(conn->client_ip_string), 1); #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); safe_socket_close(&conn->event_channel_fd); pthread_cleanup_pop(1); // unlock the mutex @@ -65,7 +65,7 @@ void *ap2_event_receiver(void *arg) { memset(&remote_addr, 0, 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); conn->event_channel_fd = eintr_checked_accept(conn->event_socket, (struct sockaddr *)&remote_addr, &addr_size); diff --git a/audio_alsa.c b/audio_alsa.c index 83ed20df..7571a375 100644 --- a/audio_alsa.c +++ b/audio_alsa.c @@ -1,7 +1,7 @@ /* * libalsa output driver. This file is part of Shairport. * Copyright (c) Muffinman, Skaman 2013 - * Copyright (c) Mike Brady 2014--2025 + * Copyright (c) Mike Brady 2014--2026 * All rights reserved. * * Permission is hereby granted, free of charge, to any person @@ -212,9 +212,10 @@ void handle_unfixable_error(int errorCode) { if (config.cmd_unfixable) { command_execute(config.cmd_unfixable, messageString, 1); } else { - die("An unrecoverable error, \"output_device_error_%d\", has been " - "detected. Doing an emergency exit, as no run_this_if_an_unfixable_error_is_detected " - "program.", + pthread_mutex_unlock(&alsa_mutex); // release the alsa mutex to allow a clean exit + die("an unrecoverable error, \"output_device_error_%d\", has been " + "detected. Doing an emergency exit, as no \"run_this_if_an_unfixable_error_is_detected\" " + "handler has been provided.", errorCode); } } @@ -246,7 +247,7 @@ static int get_permissible_configuration_settings() { snd_pcm_hw_params_alloca(&local_alsa_params); snd_pcm_info_t *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; ret = snd_pcm_open(&temporary_alsa_handle, alsa_out_dev, SND_PCM_STREAM_PLAYBACK, 0); if (ret == 0) { @@ -1061,7 +1062,7 @@ static int prepare_mixer() { // Now, start trying to initialise the alsa device with the settings // 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 (snd_mixer_selem_get_playback_volume_range(alsa_mix_elem, &alsa_mix_minv, &alsa_mix_maxv) < 0) { @@ -1147,7 +1148,7 @@ static int prepare_mixer() { if (response == 0) 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_setcancelstate(oldState, NULL); } @@ -1593,7 +1594,7 @@ static int configure(int32_t requested_encoded_format, char **channel_map) { int response = 0; int oldState; 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 == 0) 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)) { *channel_map = get_channel_map_str(); } - debug_mutex_unlock(&alsa_mutex, 0); + pthread_mutex_unlock(&alsa_mutex); pthread_cleanup_pop(0); pthread_setcancelstate(oldState, NULL); if (response != 0) @@ -1623,7 +1624,7 @@ static void deinit(void) { int oldState; pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable 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) { debug(3, "alsa: closing the output device."); 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 oldState; 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) && (open_mixer() == 0)) { response = 0; // okay if actually using the mute facility @@ -1674,7 +1675,7 @@ static int set_mute_state() { } 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_setcancelstate(oldState, NULL); return response; @@ -1910,11 +1911,11 @@ static int delay(long *the_delay) { snd_pcm_state_t state; 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); - debug_mutex_unlock(&alsa_mutex, 0); + pthread_mutex_unlock(&alsa_mutex); pthread_cleanup_pop(0); 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 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) { 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. if (frames_sent_to_dac != NULL) *frames_sent_to_dac = frames_sent_for_playing; - debug_mutex_unlock(&alsa_mutex, 0); + pthread_mutex_unlock(&alsa_mutex); pthread_cleanup_pop(0); pthread_setcancelstate(oldState, NULL); 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 // just check the state of the DAC - if ((state != SND_PCM_STATE_PREPARED) && (state != SND_PCM_STATE_RUNNING) && - (state != SND_PCM_STATE_XRUN)) { - debug(1, "alsa: DAC in odd SND_PCM_STATE_* %d prior to writing.", state); + if ((state != SND_PCM_STATE_PREPARED) && (state != SND_PCM_STATE_RUNNING)) { + debug(1, "alsa: DAC in unexpected state %s prior to writing.", snd_pcm_state_name(state)); } 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); } @@ -2141,7 +2140,7 @@ static int play(void *buf, int samples, __attribute__((unused)) int sample_type, 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) { ret = do_open(); @@ -2163,13 +2162,13 @@ static int play(void *buf, int samples, __attribute__((unused)) int sample_type, ret = do_play(buf, samples); } - debug_mutex_unlock(&alsa_mutex, 0); + pthread_mutex_unlock(&alsa_mutex); pthread_cleanup_pop(0); // release the mutex return ret; } 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... // do nothing for a flush if config.keep_dac_busy is true if (config.keep_dac_busy == 0) { @@ -2178,19 +2177,19 @@ static void flush(void) { } else { 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 } 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 (config.keep_dac_busy == 0) { do_close(); } } else 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 } @@ -2200,7 +2199,7 @@ static void do_volume(double vol) { // caller is assumed to have the alsa_mutex int oldState; pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable 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 (has_softvol) { 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 close_mixer(); } - debug_mutex_unlock(&alsa_mixer_mutex, 4); + pthread_mutex_unlock(&alsa_mixer_mutex); pthread_cleanup_pop(0); // release the mutex 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. " "Disabling standby may not work properly!", 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 if ((alsa_backend_state == abm_disconnected) && (config.keep_dac_busy != 0)) { // 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 uint64_t tsb = get_absolute_time_in_ns(); usleep(sleep_time_us); // has a cancellation point in it diff --git a/audio_sndio.c b/audio_sndio.c index dd1d3bac..35637d03 100644 --- a/audio_sndio.c +++ b/audio_sndio.c @@ -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 int current_encoded_output_format; -static struct sio_hdl *hdl; +static struct sio_hdl *ohdl; 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_round; // parameters for opening the output device @@ -51,7 +51,7 @@ static size_t written; uint64_t time_of_last_onmove_cb; int at_least_one_onmove_cb_seen; -struct sio_par par; +struct sio_par opar; struct sndio_formats { 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() { int ret = 0; 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); if (hdl != NULL) { struct sio_cap cap; @@ -198,7 +198,7 @@ static int get_permissible_configuration_settings() { proposed_par.rate = sps_rate_actual_rate(r); proposed_par.pchan = c; 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.sig = format_info->sig; 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) { 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"); written = played = is_running = 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; debug(3, "Requested %u/%u/%u/%u/%u (rate/bits/signed/le/channels)", par.rate, par.bits, par.sig, par.le, par.pchan); - if (sio_setpar(hdl, &par) == 1) { + if (sio_setpar(ohdl, &par) == 1) { 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, apar.sig, apar.le, apar.pchan); 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(); debug(2, "sndio: output device name is \"%s\".", output_device_name); - hdl = sio_open(output_device_name, SIO_PLAY, 0); - if (!hdl) + ohdl = sio_open(output_device_name, SIO_PLAY, 0); + if (!ohdl) die("sndio: cannot open audio device"); - sio_onmove(hdl, onmove_cb, NULL); + sio_onmove(ohdl, onmove_cb, NULL); // debug(1, "sndio: init done"); return 0; } static void deinit() { - pthread_cleanup_debug_mutex_lock(&sndio_mutex, 1000, 1); - if (hdl != NULL) { + pthread_mutex_lock_and_cleanup_push(&sndio_mutex); + if (ohdl != NULL) { if (is_running != 0) { - sio_flush(hdl); + sio_flush(ohdl); is_running = 0; } - sio_close(hdl); - hdl = NULL; + sio_close(ohdl); + ohdl = NULL; } 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) { 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 (hdl != NULL) { - if (sio_start(hdl) != 1) + if (ohdl != NULL) { + if (sio_start(ohdl) != 1) debug(1, "sndio: unable to start"); is_running = 1; 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!"); } } - written += sio_write(hdl, buf, frames * framesize); + written += sio_write(ohdl, buf, frames * framesize); pthread_cleanup_pop(1); // unlock the mutex } return 0; @@ -486,10 +486,10 @@ static int play(void *buf, int frames, __attribute__((unused)) int sample_type, static void stop() { - pthread_cleanup_debug_mutex_lock(&sndio_mutex, 1000, 1); - if (hdl != NULL) { + pthread_mutex_lock_and_cleanup_push(&sndio_mutex); + if (ohdl != NULL) { if (is_running != 0) { - if (sio_flush(hdl) != 1) + if (sio_flush(ohdl) != 1) debug(1, "sndio: unable to stop"); written = played = is_running = 0; } else { @@ -509,7 +509,7 @@ int get_delay(long *delay) { // 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 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; // sanity check -- total estimate can not exceed frames written. if ((estimated_extra_frames_output + played) > written / framesize) { @@ -526,8 +526,8 @@ int get_delay(long *delay) { static int delay(long *delay) { int result = 0; - pthread_cleanup_debug_mutex_lock(&sndio_mutex, 1000, 1); - if (hdl != NULL) { + pthread_mutex_lock_and_cleanup_push(&sndio_mutex); + if (ohdl != NULL) { if (is_running != 0) { result = get_delay(delay); } else { @@ -543,10 +543,10 @@ static int delay(long *delay) { } static void flush() { - pthread_cleanup_debug_mutex_lock(&sndio_mutex, 1000, 1); - if (hdl != NULL) { + pthread_mutex_lock_and_cleanup_push(&sndio_mutex); + if (ohdl != NULL) { if (is_running != 0) { - if (sio_flush(hdl) != 1) + if (sio_flush(ohdl) != 1) debug(1, "sndio: unable to flush"); written = played = is_running = 0; } else { diff --git a/common.c b/common.c index 6efe070d..f041c226 100644 --- a/common.c +++ b/common.c @@ -133,8 +133,6 @@ void set_alsa_out_dev(char *); config_t config_file_stuff; uint64_t minimum_dac_queue_size; -pthread_mutex_t the_conn_lock = PTHREAD_MUTEX_INITIALIZER; - unsigned int sps_format_sample_size_array[] = { 0, // unknown 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 -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 // alternatively you can direct it to stderr using a command line option @@ -290,7 +290,7 @@ int create_log_file(const char *path) { int flags = fcntl(fd, F_GETFL); if (flags == -1) { // strerror_r(errno, (char - //*)errorstring, sizeof(errorstring)); + // *)errorstring, sizeof(errorstring)); // debug(1, "create_log_file -- error %d (\"%s\") getting flags of pipe: \"%s\".", // errno, // (char *)errorstring, pathname); @@ -336,12 +336,12 @@ void log_to_syslog() { #else sps_log = syslog; #endif + } +*/ shairport_cfg config; -sigset_t pselect_sigset; - static uint16_t UDPPortIndex = 0; void resetFreeUDPPort() { @@ -502,9 +502,9 @@ uint16_t bind_UDP_port(int ip_family, const char *self_ip_address, uint32_t scop 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) { #pragma GCC diagnostic push @@ -1806,13 +1806,6 @@ uint64_t nctoh64(const uint8_t *p) { 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) { struct timespec req, rem; int result; @@ -1827,98 +1820,6 @@ void sps_nanosleep(const time_t sec, const long nanosec) { 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) { // the address of the malloc variable is passed in case a realloc is done as some time // debug(1, "malloc cleanup called."); diff --git a/common.h b/common.h index 395ca539..ca156a53 100644 --- a/common.h +++ b/common.h @@ -433,9 +433,6 @@ typedef struct { unsigned int output_channel_map_size; // number of output channels #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; #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 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_stdout(); // call this to direct logging to stdout; 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; +*/ // 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( 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; \ } -// 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); 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); -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, ...); @@ -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 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); @@ -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); +// for pthread_push and pop +// careful with the difference between cleanup and unlock! + +void malloc_cleanup(void *arg); void socket_cleanup(void *arg); void mutex_unlock(void *arg); -void rwlock_unlock(void *arg); void mutex_cleanup(void *arg); +void rwlock_unlock(void *arg); void cv_cleanup(void *arg); void thread_cleanup(void *arg); #ifdef CONFIG_AIRPLAY_2 diff --git a/dacp.c b/dacp.c index f8d90404..e0347a52 100644 --- a/dacp.c +++ b/dacp.c @@ -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(); pthread_cleanup_push(addrinfo_cleanup, (void *)&res); // only do this one at a time -- not sure it is necessary, but better safe than sorry - - // 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); + int mutex_reply = pthread_mutex_lock(&dacp_conversation_lock); if (mutex_reply == 0) { 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 // 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 - 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) 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... @@ -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 void set_dacp_server_information(rtsp_conn_info *conn) { // 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; 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); 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) { - debug_mutex_lock(&dacp_server_information_lock, 500000, 2); + pthread_mutex_lock(&dacp_server_information_lock); debug(3, "dacp_monitor_port_update_callback with Remote ID \"%s\", target ID \"%s\" and port " "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); - debug_mutex_unlock(&dacp_server_information_lock, 3); + pthread_mutex_unlock(&dacp_server_information_lock); } void *dacp_monitor_thread_code(__attribute__((unused)) void *na) { @@ -510,7 +506,7 @@ void *dacp_monitor_thread_code(__attribute__((unused)) void *na) { while (1) { int result = 0; 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) { metadata_hub_modify_prolog(); int ch = (metadata_store.dacp_server_active != 0) || diff --git a/dbus-service.c b/dbus-service.c index 4dbd1a6a..397ac759 100644 --- a/dbus-service.c +++ b/dbus-service.c @@ -44,6 +44,7 @@ #include "metadata/hub.h" #include "dbus-service.h" +#include "utilities/exit.h" #ifdef CONFIG_CONVOLUTION #include @@ -934,9 +935,8 @@ static gboolean on_handle_quit(ShairportSync *skeleton, GDBusMethodInvocation *i __attribute__((unused)) const gchar *command, __attribute__((unused)) gpointer user_data) { 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); + exit_request(EXIT_SUCCESS); return TRUE; } diff --git a/metadata/pc_queue.c b/metadata/pc_queue.c index 86bc9845..e216558c 100644 --- a/metadata/pc_queue.c +++ b/metadata/pc_queue.c @@ -81,11 +81,11 @@ int pc_queue_add_item(pc_queue *the_queue, const void *the_stuff, int block) { int rc; if (the_queue) { 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) return EBUSY; } else - rc = debug_mutex_lock(&the_queue->pc_queue_lock, 50000, 4); + rc = pthread_mutex_lock(&the_queue->pc_queue_lock); if (rc) debug(1, "Error %d (\"%s\") locking for pc_queue_add_item. Block is %d.", rc, strerror(rc), 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 rc; 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) 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); diff --git a/mpris-service.c b/mpris-service.c index 06d506c2..71e254f3 100644 --- a/mpris-service.c +++ b/mpris-service.c @@ -41,6 +41,7 @@ #include "metadata/hub.h" #include "mpris-service.h" +#include "utilities/exit.h" static guint ownerID = 0; 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, __attribute__((unused)) gpointer user_data) { 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); + exit_request(EXIT_SUCCESS); return TRUE; } diff --git a/player.c b/player.c index edc7f347..4f139c6b 100644 --- a/player.c +++ b/player.c @@ -4,7 +4,7 @@ * All rights reserved. * * 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. * * 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; 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); pthread_cleanup_pop(1); #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 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) { int16_t occ = 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 // the given timestamp, if any. 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; while ((older_seqno_found == 0) && (revised_seqno != conn->ab_read)) { 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... 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_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(); conn->packet_count++; conn->packet_count_since_flush++; 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; if (!conn->ab_synced) { 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) first_possibly_missing_frame = conn->ab_write; } - } + // } // remove this pthread_cleanup_pop(1); // debug_mutex_unlock(&conn->ab_mutex, 0); 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; 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; 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 if ((have_timestamp_timing_information(conn)) && (conn->input_format_is_valid != 0)) { +/* int rco = get_requested_connection_state_to_output(); 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 if (conn->connection_state_to_output == 0) { // going 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_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() != 0) { // if the back end isn't running for any reason 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_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_output_flushed == 0) { #if CONFIG_FFMPEG @@ -3557,7 +3558,7 @@ void *player_thread_func(void *arg) { } 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; 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) { - 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 // 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 config.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) { @@ -5246,11 +5247,11 @@ void player_volume(double airplay_volume, rtsp_conn_info *conn) { void do_flush(uint32_t timestamp, rtsp_conn_info *conn) { 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_rtp_timestamp = timestamp; // flush all packets up to, but not including, this one. 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) { @@ -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 // 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) { pthread_t *pt = malloc(sizeof(pthread_t)); if (pt == NULL) @@ -5307,7 +5308,7 @@ int player_stop(rtsp_conn_info *conn) { // note -- this may be called from another connection thread. debug(2, "Connection %d: player_stop.", conn->connection_number); 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; if (pt) { debug(3, "player_thread cancel..."); diff --git a/player.h b/player.h index 05c035f6..a5b2c27c 100644 --- a/player.h +++ b/player.h @@ -322,7 +322,7 @@ typedef struct { alac_file *decoder_info; uint64_t packet_count; uint64_t packet_count_since_flush; - int connection_state_to_output; + // int connection_state_to_output; uint64_t first_packet_time_to_play; int64_t time_since_play_started; // nanoseconds // stats diff --git a/rtp.c b/rtp.c index 569c8fc3..7b7ee774 100644 --- a/rtp.c +++ b/rtp.c @@ -487,7 +487,7 @@ void *rtp_control_receiver(void *arg) { 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->packet_count_since_flush > 0) { @@ -535,7 +535,7 @@ void *rtp_control_receiver(void *arg) { conn->anchor_remote_info_is_valid = 1; 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 = 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) { - 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_rtptime = 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) { @@ -1132,7 +1132,7 @@ int frame_to_ntp_local_time(uint32_t timestamp, uint64_t *time, rtsp_conn_info * // a zero result is good if (conn->anchor_remote_info_is_valid == 0) debug(1, "no anchor information"); - debug_mutex_lock(&conn->reference_time_mutex, 1000, 0); + pthread_mutex_lock(&conn->reference_time_mutex); int result = -1; if (conn->anchor_remote_info_is_valid != 0) { 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); result = 0; } - debug_mutex_unlock(&conn->reference_time_mutex, 0); + pthread_mutex_unlock(&conn->reference_time_mutex); return result; } int local_ntp_time_to_frame(uint64_t time, uint32_t *frame, rtsp_conn_info *conn) { // a zero result is good - debug_mutex_lock(&conn->reference_time_mutex, 1000, 0); + pthread_mutex_lock(&conn->reference_time_mutex); int result = -1; if (conn->anchor_remote_info_is_valid != 0) { // 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; result = 0; } - debug_mutex_unlock(&conn->reference_time_mutex, 0); + pthread_mutex_unlock(&conn->reference_time_mutex); return result; } diff --git a/rtsp.c b/rtsp.c index 9ae4a214..60a9929c 100644 --- a/rtsp.c +++ b/rtsp.c @@ -148,7 +148,7 @@ int RTSP_connection_index = 1; // keep track of the threads we have spawned so we can join() them static int nconns = 0; 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 int i = 0; int found = 0; @@ -170,7 +170,7 @@ static void track_thread(rtsp_conn_info *conn) { 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" @@ -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 // all categories are elegible for cancellation // otherwise just the category itself - debug_mutex_lock(&conns_lock, 1000000, 3); + pthread_mutex_lock(&conns_lock); int i; for (i = 0; i < nconns; i++) { 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; } } - debug_mutex_unlock(&conns_lock, 3); + pthread_mutex_unlock(&conns_lock); } int old_connection_count = -1; @@ -214,7 +214,7 @@ void cleanup_threads(void) { int i; int connection_count = 0; // debug(2, "culling threads."); - debug_mutex_lock(&conns_lock, 1000000, 4); + pthread_mutex_lock(&conns_lock); for (i = 0; i < nconns; i++) { if ((conns[i] != NULL) && (conns[i]->running == 0)) { debug(4, "found RTSP connection thread %d in a non-running state.", @@ -230,7 +230,7 @@ void cleanup_threads(void) { connection_count++; } } - debug_mutex_unlock(&conns_lock, 4); + pthread_mutex_unlock(&conns_lock); if (old_connection_count != connection_count) { if (connection_count == 0) { @@ -247,7 +247,7 @@ void cleanup_threads(void) { int terminate_conn(int connection_number) { // this will look for a connection by number, cancel it, join it and delete it. int found = 0; - debug_mutex_lock(&conns_lock, 1000000, 4); + pthread_mutex_lock(&conns_lock); // look for an empty slot first int i = 0; while ((i < nconns) && (found == 0)) { @@ -259,7 +259,7 @@ int terminate_conn(int connection_number) { } else i++; } - debug_mutex_unlock(&conns_lock, 4); + pthread_mutex_unlock(&conns_lock); 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_mutex_lock(&conn->flush_mutex, 1000, 4); + pthread_mutex_lock(&conn->flush_mutex); if (flushFromValid == 0) { // 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); } @@ -1465,7 +1465,7 @@ void handle_setrateanchori(rtsp_conn_info *conn, rtsp_message *req, rtsp_message uint64_t rate; plist_get_uint_val(item, &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; if ((rate & 1) != 0) { 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 - debug_mutex_lock(&conns_lock, 1000000, 4); + pthread_mutex_lock(&conns_lock); int i; for (i = 0; i < nconns; i++) { if ((conns[i] != NULL) && (conns[i]->connection_number == 0)) { 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); if (reply == rtsp_read_request_response_ok) { diff --git a/scripts/shairport-sync.conf b/scripts/shairport-sync.conf index aa8d35e2..21ddd599 100644 --- a/scripts/shairport-sync.conf +++ b/scripts/shairport-sync.conf @@ -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). // 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. -// "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. // "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. @@ -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_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. +// 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. // 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. }; diff --git a/scripts/shairport-sync.service.in b/scripts/shairport-sync.service.in index 190fc9de..dc24e2db 100644 --- a/scripts/shairport-sync.service.in +++ b/scripts/shairport-sync.service.in @@ -10,7 +10,7 @@ StartLimitIntervalSec=300 StartLimitBurst=5 [Service] -ExecStart=@prefix@/bin/shairport-sync --log-to-syslog +ExecStart=@prefix@/bin/shairport-sync User=shairport-sync Group=shairport-sync LimitRTPRIO=5 diff --git a/shairport.c b/shairport.c index d1a0cbc6..5f7831ca 100644 --- a/shairport.c +++ b/shairport.c @@ -83,6 +83,7 @@ #include "rtp.h" #include "rtsp.h" #include "utilities/string_utilities.h" +#include "utilities/exit.h" #if defined(CONFIG_DACP_CLIENT) #include "dacp.h" @@ -140,6 +141,10 @@ int this_is_the_daemon_process = 0; pthread_t rtsp_listener_thread; +#if defined(CONFIG_DBUS_INTERFACE) || defined(CONFIG_MPRIS_INTERFACE) +GMainLoop *glib_worker_loop = NULL; +#endif + int killOption = 0; int daemonisewith = 0; int daemonisewithout = 0; @@ -453,7 +458,7 @@ int parse_options(int argc, char **argv) { break; case 'u': 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; case 'D': inform("Warning: the option -D or --disconnectFromOutput is deprecated."); @@ -489,6 +494,7 @@ int parse_options(int argc, char **argv) { } } if (c < -1) { + debug(1, "Oops"); 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 this was the first command line argument, it'll already have been chosen - 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."); - } + 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."); +/* #ifdef CONFIG_LIBDAEMON log_to_default = 0; // a specific log output modality has been selected. #endif log_to_syslog(); +*/ } + #ifdef CONFIG_LIBDAEMON if ((daemonisewith) && (daemonisewithout)) 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. */ if (config_lookup_string(config.cfg, "diagnostics.log_output_to", &str)) { + /* #ifdef CONFIG_LIBDAEMON log_to_default = 0; // a specific log output modality has been selected. #endif @@ -871,7 +877,11 @@ int parse_options(int argc, char **argv) { config.log_fd = -1; 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. */ if (config_lookup_string(config.cfg, "general.ignore_volume_control", &str)) { if (strcasecmp(str, "no") == 0) @@ -1838,7 +1848,7 @@ GThread *glib_worker_thread = NULL; gpointer glib_worker_thread_function(__attribute__((unused)) gpointer data) { // 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."); @@ -1853,7 +1863,7 @@ gpointer glib_worker_thread_function(__attribute__((unused)) gpointer data) { // 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."); @@ -1869,11 +1879,7 @@ gpointer glib_worker_thread_function(__attribute__((unused)) gpointer data) { // debug(1, "stopped D-Bus service"); #endif - g_main_loop_unref(config.glib_worker_loop); - if (config.quit_requested_from_glib_mainloop != 0) { - debug(2, "glib_mainloop_thread_function asking for exit"); - exit(EXIT_SUCCESS); - } + g_main_loop_unref(glib_worker_loop); return NULL; } @@ -1927,10 +1933,15 @@ void exit_function() { #endif #if defined(CONFIG_DBUS_INTERFACE) || defined(CONFIG_MPRIS_INTERFACE) - if ((glib_worker_thread != NULL) && (config.quit_requested_from_glib_mainloop == 0)) { - g_main_loop_quit(config.glib_worker_loop); - debug(2, "GMainLoop stop requested"); - } + if (glib_worker_loop != NULL) { // may not have been initialised + g_main_loop_quit(glib_worker_loop); + debug(2, "GMainLoop stop requested"); + } + if (glib_worker_thread != NULL) { + g_thread_join(glib_worker_thread); + debug(1, "GLib worker thread joined"); + } + #endif #ifdef CONFIG_METADATA_HUB @@ -2063,12 +2074,12 @@ void handle_sigchld(__attribute__((unused)) int sig) { // for clean exits void intHandler(__attribute__((unused)) int k) { debug(2, "exit on SIGINT"); - exit(EXIT_SUCCESS); + exit_request(EXIT_SUCCESS); } void termHandler(__attribute__((unused)) int k) { 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, @@ -2330,9 +2341,10 @@ const char *av_channel_layout_name(uint64_t channel_layout) { #endif 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 // 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 /* Check if we are called with -V or --version parameter */ 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); } - /* 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)) { log_to_syslog_select_is_first_command_line_argument = 1; log_to_syslog(); } else { log_to_stderr(); } +*/ pid = getpid(); 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) #endif - set_requested_connection_state_to_output( - 1); // we expect to be able to connect to the output device + // set_requested_connection_state_to_output( + // 1); // we expect to be able to connect to the output device config.audio_backend_buffer_desired_length = 0.15; // seconds config.audio_decoded_buffer_desired_length = 0.75; // seconds config.udp_port_base = 6001; @@ -3348,6 +3362,10 @@ int main(int argc, char **argv) { // you'll see two threads named "listener" or whatever... named_pthread_create(&rtsp_listener_thread, NULL, &rtsp_listen_loop, NULL, "listener"); atexit(exit_rtsp_listener); - pthread_join(rtsp_listener_thread, NULL); + + // wait forever... + while (1) { + usleep(1000000); + } return 0; } diff --git a/utilities/buffered_read.c b/utilities/buffered_read.c index ac90c24a..2f5a07cd 100644 --- a/utilities/buffered_read.c +++ b/utilities/buffered_read.c @@ -35,7 +35,7 @@ ssize_t buffered_read(buffered_tcp_desc *descriptor, void *buf, size_t count, size_t *bytes_remaining) { 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"); pthread_cleanup_push(mutex_unlock, (void *)&descriptor->mutex); // wipe the slate dlean before reading... @@ -116,7 +116,7 @@ void *buffered_tcp_reader(void *arg) { do { 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"); pthread_cleanup_push(mutex_unlock, (void *)&descriptor->mutex); 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); // debug(1, "Received %d bytes for a buffer size of %d bytes.",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"); pthread_cleanup_push(mutex_unlock, (void *)&descriptor->mutex); if (nread < 0) { diff --git a/utilities/debug.c b/utilities/debug.c index 5a97b727..ebc7ba8c 100644 --- a/utilities/debug.c +++ b/utilities/debug.c @@ -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_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 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; } -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_last_debug_message = ns_time_at_startup; debuglev = level; debugger_show_elapsed_time = show_elapsed_time; debugger_show_relative_time = show_relative_time; debugger_show_file_and_line = show_file_and_line; + local_exit_requester = exit_requester; } int debug_level() { return debuglev; }; @@ -144,8 +147,14 @@ void _die(const char *filename, const int linenumber, const char *format, ...) { // syslog(LOG_ERR, "%s", b); fprintf(stderr, "%s\n", b); pthread_setcancelstate(oldState, NULL); - usleep(2000000); //allow some time for printing of message - _Exit(EXIT_FAILURE); + 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); + } } void _warn(const char *filename, const int linenumber, const char *format, ...) { diff --git a/utilities/debug.h b/utilities/debug.h index 8491ed74..751bbd26 100644 --- a/utilities/debug.h +++ b/utilities/debug.h @@ -1,7 +1,7 @@ /* 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 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 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 void set_debug_level(int level); diff --git a/utilities/exit.c b/utilities/exit.c new file mode 100644 index 00000000..9ba01afa --- /dev/null +++ b/utilities/exit.c @@ -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 // for sig_atomic_t +#include // for EXIT_SUCCESS +#include // for memset +#include // for usleep +#include + +#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 +} \ No newline at end of file diff --git a/utilities/exit.h b/utilities/exit.h new file mode 100644 index 00000000..359268fd --- /dev/null +++ b/utilities/exit.h @@ -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 + +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. diff --git a/utilities/network_utilities.c b/utilities/network_utilities.c index 096c31f9..51141310 100644 --- a/utilities/network_utilities.c +++ b/utilities/network_utilities.c @@ -52,7 +52,7 @@ int _safe_socket_close(const char *filename, const int linenumber, int *sockfd) int result = 0; int oldstate; pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate); - debug_mutex_lock(&safe_socket_lock, 1000000, 1); + pthread_mutex_lock(&safe_socket_lock); if (*sockfd == 0) { _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 { _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); return result; } \ No newline at end of file diff --git a/utilities/rtsp_message_utilities.c b/utilities/rtsp_message_utilities.c index 015376fe..23ac6594 100644 --- a/utilities/rtsp_message_utilities.c +++ b/utilities/rtsp_message_utilities.c @@ -45,7 +45,7 @@ static pthread_mutex_t reference_counter_lock = PTHREAD_MUTEX_INITIALIZER; static int msg_indexes = 1; 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) debug(1, "Error %d locking reference counter lock", rc); if (msg > (rtsp_message *)0x00010000) { @@ -63,7 +63,7 @@ void msg_retain(rtsp_message *msg) { rtsp_message *msg_init(void) { // 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) 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) { - debug_mutex_lock(&reference_counter_lock, 1000, 0); + pthread_mutex_lock(&reference_counter_lock); if (*msgh > (rtsp_message *)0x00010000) { rtsp_message *msg = *msgh; msg->referenceCount--; @@ -161,7 +161,7 @@ void msg_free(rtsp_message **msgh) { "%" PRIxPTR ".", (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) {