diff --git a/src/usb_moded-common.c b/src/usb_moded-common.c index c2b0b29..5bf91fe 100644 --- a/src/usb_moded-common.c +++ b/src/usb_moded-common.c @@ -52,7 +52,8 @@ void common_acquire_wakelock (const char *wakelock_name); void common_release_wakelock (const char *wakelock_name); int common_system_ (const char *file, int line, const char *func, const char *command); FILE *common_popen_ (const char *file, int line, const char *func, const char *command, const char *type); -void common_usleep_ (const char *file, int line, const char *func, useconds_t usec); +waitres_t common_wait (unsigned tot_ms, bool (*ready_cb)(void *aptr), void *aptr); +bool common_msleep_ (const char *file, int line, const char *func, unsigned msec); static bool common_mode_in_list (const char *mode, char *const *modes); int common_valid_mode (const char *mode); gchar *common_get_mode_list (mode_list_type_t type); @@ -338,40 +339,60 @@ common_popen_(const char *file, int line, const char *func, return popen(command, type); } -/** Wrapper to give visibility to blocking sleeps usb-moded is making - */ -void -common_usleep_(const char *file, int line, const char *func, - useconds_t usec) +waitres_t +common_wait(unsigned tot_ms, bool (*ready_cb)(void *aptr), void *aptr) { - struct timespec ts = { - .tv_sec = (usec / 1000000), - .tv_nsec = (usec % 1000000) * 1000 - }; + struct timespec ts; - long ms = (ts.tv_nsec + 1000000 - 1) / 1000000; + waitres_t res = WAIT_FAILED; - if( !ms ) { - log_debug("SLEEP %ld seconds; from %s:%d: %s()", - (long)ts.tv_sec, file, line, func); - } - else if( ts.tv_sec ) { - log_debug("SLEEP %ld.%03ld seconds; from %s:%d: %s()", - (long)ts.tv_sec, ms, file, line, func); - } - else { - log_debug("SLEEP %ld milliseconds; from %s:%d: %s()", - ms, file, line, func); - } + for( ;; ) { + unsigned nap_ms = (tot_ms > 200) ? 200 : tot_ms; + + ts.tv_sec = (nap_ms / 1000); + ts.tv_nsec = (nap_ms % 1000); + ts.tv_nsec *= 1000 * 1000; + + for( ;; ) { + if( ready_cb && ready_cb(aptr) ) { + res = WAIT_READY; + goto EXIT; + } + + if( tot_ms <= 0 ) { + res = WAIT_TIMEOUT; + goto EXIT; + } + + if( worker_bailing_out() ) { + log_warning("wait canceled"); + goto EXIT; + } - do { - if( worker_bailing_out() ) { - log_warning("SLEEP %ld milliseconds - ignored; from %s:%d: %s()", - ms, file, line, func); - break; + if( nanosleep(&ts, &ts) == 0 ) + break; + + if( errno != EINTR ) { + log_warning("wait failed: %m"); + goto EXIT; + } } - } while( nanosleep(&ts, &ts) == -1 && errno != EINTR ); + tot_ms -= nap_ms; + } + +EXIT: + return res; +} + +/** Wrapper to give visibility to blocking sleeps usb-moded is making + */ +bool +common_msleep_(const char *file, int line, const char *func, unsigned msec) +{ + log_debug("SLEEP %u.%03u seconds; from %s:%d: %s()", + msec / 1000u, msec % 1000u,file, line, func); + return common_wait(msec, 0, 0) == WAIT_TIMEOUT; } /* ------------------------------------------------------------------------- * diff --git a/src/usb_moded-common.h b/src/usb_moded-common.h index c14a5f9..d8d06fe 100644 --- a/src/usb_moded-common.h +++ b/src/usb_moded-common.h @@ -2,6 +2,7 @@ # define USB_MODED_COMMON_H_ # include +# include # include /* ========================================================================= * @@ -25,6 +26,13 @@ typedef enum { CABLE_STATE_NUMOF } cable_state_t; +typedef enum waitres_t +{ + WAIT_FAILED, + WAIT_READY, + WAIT_TIMEOUT, +} waitres_t; + /* ========================================================================= * * Functions * ========================================================================= */ @@ -45,7 +53,8 @@ void common_acquire_wakelock (const char *wakelock_name); void common_release_wakelock (const char *wakelock_name); int common_system_ (const char *file, int line, const char *func, const char *command); FILE *common_popen_ (const char *file, int line, const char *func, const char *command, const char *type); -void common_usleep_ (const char *file, int line, const char *func, useconds_t usec); +waitres_t common_wait (unsigned tot_ms, bool (*ready_cb)(void *aptr), void *aptr); +bool common_msleep_ (const char *file, int line, const char *func, unsigned msec); int common_valid_mode (const char *mode); gchar *common_get_mode_list (mode_list_type_t type); @@ -55,8 +64,7 @@ gchar *common_get_mode_list (mode_list_type_t type); # define common_system(command) common_system_(__FILE__,__LINE__,__FUNCTION__,(command)) # define common_popen(command, type) common_popen_(__FILE__,__LINE__,__FUNCTION__,(command),(type)) -# define common_usleep(usec) common_usleep_(__FILE__,__LINE__,__FUNCTION__,(usec)) -# define common_msleep(msec) common_usleep_(__FILE__,__LINE__,__FUNCTION__,(msec)*1000) -# define common_sleep(sec) common_usleep_(__FILE__,__LINE__,__FUNCTION__,(sec)*1000000) +# define common_msleep(msec) common_msleep_(__FILE__,__LINE__,__FUNCTION__,(msec)) +# define common_sleep(sec) common_msleep_(__FILE__,__LINE__,__FUNCTION__,(sec)*1000) #endif /* USB_MODED_COMMON_H_ */ diff --git a/src/usb_moded-modesetting.c b/src/usb_moded-modesetting.c index 2bd078d..3c99836 100644 --- a/src/usb_moded-modesetting.c +++ b/src/usb_moded-modesetting.c @@ -774,7 +774,8 @@ bool modesetting_enter_dynamic_mode(void) /* In case of failure, retry upto 3 times */ for( int i = 0; error && i < 3; ++i ) { log_warning("Retry setting up the network"); - common_msleep(1000); + if( !common_msleep(1000) ) + break; if( !(error = network_up(data)) ) log_warning("Setting up the network succeeded"); } diff --git a/src/usb_moded-network.c b/src/usb_moded-network.c index 26d3846..d0f9a9b 100644 --- a/src/usb_moded-network.c +++ b/src/usb_moded-network.c @@ -533,7 +533,8 @@ gboolean connman_set_tethering(const char *path, gboolean on) { if (i>0) { - common_msleep(200); + if( !common_msleep(200) ) + break; } if (connman_try_set_tethering(connection, path, on)) { diff --git a/src/usb_moded-worker.c b/src/usb_moded-worker.c index d460c82..4806367 100644 --- a/src/usb_moded-worker.c +++ b/src/usb_moded-worker.c @@ -115,7 +115,7 @@ worker_thread_p(void) bool worker_bailing_out(void) { - // ref: see common_usleep_() + // ref: see common_msleep_() return worker_thread_p() && worker_bailout > 0; } @@ -123,6 +123,26 @@ worker_bailing_out(void) * MTP_DAEMON * ------------------------------------------------------------------------- */ +/** Maximum time to wait for mtpd to start [ms] + * + * This needs to include time to start systemd unit + * plus however long it might take for mtpd to scan + * all files exposed over mtp. On a slow device with + * lots of files it can easily take over 30 seconds, + * especially during the 1st mtp connect after reboot. + * + * Use two minutes as some kind of worst case estimate. + */ +static unsigned worker_mtp_start_delay = 120 * 1000; + +/** Maximum time to wait for mtpd to stop [ms] + * + * This is just regular service stop. Expected to + * take max couple of seconds, but use someting + * in the ballbark of systemd default i.e. 15 seconds + */ +static unsigned worker_mtp_stop_delay = 15 * 1000; + static bool worker_mode_is_mtp_mode(const char *mode) { return mode && !strcmp(mode, "mtp_mode"); @@ -155,73 +175,77 @@ static bool worker_is_mtpd_running(void) return ack; } +static bool +worker_mtpd_running_p(void *aptr) +{ + (void)aptr; + return worker_is_mtpd_running(); +} + +static bool +worker_mtpd_stopped_p(void *aptr) +{ + (void)aptr; + return !worker_is_mtpd_running(); +} + static bool worker_stop_mtpd(void) { - bool ack = !worker_is_mtpd_running(); + bool ack = false; - if( ack ) { + if( worker_mtpd_stopped_p(0) ) { log_debug("mtp daemon is not running"); - goto EXIT; + goto SUCCESS; } int rc = common_system("systemctl-user stop buteo-mtp.service"); if( rc != 0 ) { log_warning("failed to stop mtp daemon; exit code = %d", rc); - goto EXIT; + goto FAILURE; } - for( int attempts = 3; ; ) { - if( (ack = !worker_is_mtpd_running()) ) { - log_debug("mtp daemon has stopped"); - break; - } + if( common_wait(worker_mtp_stop_delay, worker_mtpd_stopped_p, 0) != WAIT_READY ) { + log_warning("failed to stop mtp daemon; giving up"); + goto FAILURE; + } - if( --attempts <= 0) { - log_warning("failed to stop mtp daemon; giving up"); - break; - } + log_debug("mtp daemon has stopped"); - log_debug("waiting for mtp daemon to stop"); - common_msleep(2000); - } -EXIT: +SUCCESS: + ack = true; +FAILURE: return ack; } static bool worker_start_mtpd(void) { - bool ack = worker_is_mtpd_running(); + bool ack = false; - if( ack ) { - log_debug("mtp daemon is not running"); - goto EXIT; + if( worker_mtpd_running_p(0) ) { + log_debug("mtp daemon is running"); + goto SUCCESS; } int rc = common_system("systemctl-user start buteo-mtp.service"); if( rc != 0 ) { log_warning("failed to start mtp daemon; exit code = %d", rc); - goto EXIT; + goto FAILURE; } - for( int attempts = 15; ; ) { - if( (ack = worker_is_mtpd_running()) ) { - log_debug("mtp daemon has started"); - break; - } + if( common_wait(worker_mtp_start_delay, worker_mtpd_running_p, 0) != WAIT_READY ) { + log_warning("failed to start mtp daemon; giving up"); + goto FAILURE; + } - if( --attempts <= 0) { - log_warning("failed to start mtp daemon; giving up"); - break; - } + log_debug("mtp daemon has started"); - log_debug("waiting for mtp daemon to start"); - common_msleep(2000); - } -EXIT: +SUCCESS: + ack = true; +FAILURE: return ack; }