blob: b534b9e9d613275b92f2cb72a28bfbfe3eef8b2b [file] [log] [blame]
From 231bda3642ea8c3128eba1da899eb93e22256d1f Mon Sep 17 00:00:00 2001
From: DavieV <davidvalleau@gmail.com>
Date: Tue, 6 Aug 2019 11:09:38 -0700
Subject: [PATCH] Adding exponential backoff for empty read responses
---
src/ippusbxd.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 53 insertions(+), 3 deletions(-)
diff --git a/src/ippusbxd.c b/src/ippusbxd.c
index 3ef91ad..5fd7c11 100644
--- a/src/ippusbxd.c
+++ b/src/ippusbxd.c
@@ -47,6 +47,12 @@ struct service_thread_param {
struct libusb_callback_data {
int *read_inflight;
+ /*
+ * Indicates that the previous read response was empty. This is used to
+ * perform exponential backoff in service_printer_connection() to avoid
+ * overloading the printer with read requests when there is nothing to read.
+ */
+ int *empty_response;
uint32_t thread_num;
struct tcp_conn_t *tcp;
struct uds_conn_t *uds;
@@ -76,12 +82,14 @@ static int get_read_inflight(const int *read_inflight,
pthread_mutex_t *read_inflight_mutex);
static struct libusb_callback_data *setup_libusb_callback_data(
- struct http_packet_t *pkt, int *read_inflight,
+ struct http_packet_t *pkt, int *read_inflight, int *empty_response,
struct service_thread_param *thread_param,
pthread_mutex_t *read_inflight_mutex);
static int is_socket_open(const struct service_thread_param *param);
+static int update_backoff(int backoff);
+
/* Global variables */
static pthread_mutex_t thread_register_mutex;
static struct service_thread_param **service_threads = NULL;
@@ -89,6 +97,13 @@ static uint32_t num_service_threads = 0;
static struct timeval start_time;
+/* Constants */
+
+/* Times to wait in milliseconds before sending another read request to the
+ printer. */
+const int initial_backoff = 100;
+const int maximum_backoff = 1000;
+
static void sigterm_handler(int sig)
{
/* Flag that we should stop and return... */
@@ -247,6 +262,9 @@ static void read_transfer_callback(struct libusb_transfer *transfer)
/* Mark the tcp socket as active. */
set_is_active(user_data->tcp, 1);
}
+ } else {
+ /* Set that we received an empty response from the printer. */
+ *user_data->empty_response = 1;
}
break;
@@ -453,7 +471,13 @@ static void *service_printer_connection(void *params_void)
/* Register clean-up handler. */
pthread_cleanup_push(cleanup_handler, &thread_num);
+ /* Amount of time to wait in milliseconds before sending another read request
+ if we received a 0-byte response from the printer. */
+ int backoff = initial_backoff;
+
int read_inflight = 0;
+ int empty_response = 0;
+
pthread_mutex_t read_inflight_mutex;
if (pthread_mutex_init(&read_inflight_mutex, NULL))
goto cleanup;
@@ -474,6 +498,22 @@ static void *service_printer_connection(void *params_void)
if (!is_socket_open(params) || g_options.terminate)
break;
+ /* If we received an empty response from the printer then wait for |backoff|
+ milliseconds and update the backoff period. */
+ if (empty_response) {
+ /* usleep accepts microseconds. */
+ usleep(backoff * 1000);
+ backoff = update_backoff(backoff);
+ /* Reset the empty response indicator before sending the next read
+ request. A mutex should not be needed here since the transfer callback
+ won't be fired until after calling libusb_submit_transfer(). */
+ empty_response = 0;
+ } else {
+ /* If we received a non-empty response from the printer then reset the
+ backoff to 100ms. */
+ backoff = initial_backoff;
+ }
+
NOTE("Thread #%u: No read in flight, starting a new one", thread_num);
struct http_packet_t *pkt = packet_new();
if (pkt == NULL) {
@@ -482,7 +522,7 @@ static void *service_printer_connection(void *params_void)
}
struct libusb_callback_data *user_data = setup_libusb_callback_data(
- pkt, &read_inflight, params, &read_inflight_mutex);
+ pkt, &read_inflight, &empty_response, params, &read_inflight_mutex);
if (user_data == NULL) {
ERR("Thread #%u: Failed to allocate memory for libusb_callback_data",
@@ -654,7 +694,7 @@ static int setup_communication_thread(void *(*routine)(void *),
}
static struct libusb_callback_data *setup_libusb_callback_data(
- struct http_packet_t *pkt, int *read_inflight,
+ struct http_packet_t *pkt, int *read_inflight, int *empty_response,
struct service_thread_param *thread_param,
pthread_mutex_t *read_inflight_mutex) {
struct libusb_callback_data *data = calloc(1, sizeof(*data));
@@ -663,6 +703,7 @@ static struct libusb_callback_data *setup_libusb_callback_data(
data->pkt = pkt;
data->read_inflight = read_inflight;
+ data->empty_response = empty_response;
data->thread_num = thread_param->thread_num;
data->read_inflight_mutex = read_inflight_mutex;
data->read_inflight_cond = thread_param->cond;
@@ -675,6 +716,15 @@ static struct libusb_callback_data *setup_libusb_callback_data(
return data;
}
+static int update_backoff(int backoff) {
+ int updated = backoff * 2;
+ /* Cap the maximum backoff time at 1 second. */
+ if (updated > maximum_backoff) {
+ updated = maximum_backoff;
+ }
+ return updated;
+}
+
static int is_socket_open(const struct service_thread_param *param) {
if (g_options.unix_socket_mode)
return !param->uds->is_closed;
--
2.22.0.770.g0f2c4a37fd-goog