Kaynağa Gözat

esp_http_server: update httpd_queue_work() API to use semaphore

Closes: https://github.com/espressif/esp-idf/issues/8440
Harshit Malpani 3 yıl önce
ebeveyn
işleme
1bb03e17a7

+ 7 - 0
components/esp_http_server/Kconfig

@@ -45,4 +45,11 @@ menu "HTTP Server"
         help
             This sets the WebSocket server support.
 
+    config HTTPD_QUEUE_WORK_BLOCKING
+        bool "httpd_queue_work as blocking API"
+        help
+            This makes httpd_queue_work() API to wait until a message space is available on UDP control socket.
+            It internally uses a counting semaphore with count set to `LWIP_UDP_RECVMBOX_SIZE` to achieve this.
+            This config will slightly change API behavior to block until message gets delivered on control socket.
+
 endmenu

+ 3 - 0
components/esp_http_server/src/esp_httpd_priv.h

@@ -107,6 +107,9 @@ struct httpd_data {
     httpd_config_t config;                  /*!< HTTPD server configuration */
     int listen_fd;                          /*!< Server listener FD */
     int ctrl_fd;                            /*!< Ctrl message receiver FD */
+#if CONFIG_HTTPD_QUEUE_WORK_BLOCKING
+    SemaphoreHandle_t ctrl_sock_semaphore;  /*!< Ctrl socket semaphore */
+#endif
     int msg_fd;                             /*!< Ctrl message sender FD */
     struct thread_data hd_td;               /*!< Information for the HTTPD thread */
     struct sock_db *hd_sd;                  /*!< The socket database */

+ 44 - 7
components/esp_http_server/src/httpd_main.c

@@ -15,6 +15,9 @@
 #include <esp_http_server.h>
 #include "esp_httpd_priv.h"
 #include "ctrl_sock.h"
+#if CONFIG_HTTPD_QUEUE_WORK_BLOCKING
+#include "freertos/semphr.h"
+#endif
 
 typedef struct {
     fd_set *fdset;
@@ -89,14 +92,24 @@ esp_err_t httpd_queue_work(httpd_handle_t handle, httpd_work_fn_t work, void *ar
         .hc_work = work,
         .hc_work_arg = arg,
     };
-
-    int ret = cs_send_to_ctrl_sock(hd->msg_fd, hd->config.ctrl_port, &msg, sizeof(msg));
-    if (ret < 0) {
-        ESP_LOGW(TAG, LOG_FMT("failed to queue work"));
-        return ESP_FAIL;
+#if CONFIG_HTTPD_QUEUE_WORK_BLOCKING
+    // Semaphore is acquired here and released after work function is executed.
+    if (xSemaphoreTake(hd->ctrl_sock_semaphore, portMAX_DELAY) == pdTRUE) {
+#endif
+        int ret = cs_send_to_ctrl_sock(hd->msg_fd, hd->config.ctrl_port, &msg, sizeof(msg));
+        if (ret < 0) {
+            ESP_LOGW(TAG, LOG_FMT("failed to queue work"));
+#if CONFIG_HTTPD_QUEUE_WORK_BLOCKING
+            xSemaphoreGive(hd->ctrl_sock_semaphore);
+#endif
+            return ESP_FAIL;
+        }
+        return ESP_OK;
+#if CONFIG_HTTPD_QUEUE_WORK_BLOCKING
     }
-
-    return ESP_OK;
+    ESP_LOGE(TAG, "Unable to acquire semaphore");
+    return ESP_FAIL;
+#endif
 }
 
 esp_err_t httpd_get_client_list(httpd_handle_t handle, size_t *fds, int *client_fds)
@@ -136,10 +149,16 @@ static void httpd_process_ctrl_msg(struct httpd_data *hd)
     int ret = recv(hd->ctrl_fd, &msg, sizeof(msg), 0);
     if (ret <= 0) {
         ESP_LOGW(TAG, LOG_FMT("error in recv (%d)"), errno);
+#if CONFIG_HTTPD_QUEUE_WORK_BLOCKING
+        xSemaphoreGive(hd->ctrl_sock_semaphore);
+#endif
         return;
     }
     if (ret != sizeof(msg)) {
         ESP_LOGW(TAG, LOG_FMT("incomplete msg"));
+#if CONFIG_HTTPD_QUEUE_WORK_BLOCKING
+        xSemaphoreGive(hd->ctrl_sock_semaphore);
+#endif
         return;
     }
 
@@ -157,6 +176,9 @@ static void httpd_process_ctrl_msg(struct httpd_data *hd)
     default:
         break;
     }
+#if CONFIG_HTTPD_QUEUE_WORK_BLOCKING
+    xSemaphoreGive(hd->ctrl_sock_semaphore);
+#endif
 }
 
 // Called for each session from httpd_server
@@ -419,6 +441,18 @@ esp_err_t httpd_start(httpd_handle_t *handle, const httpd_config_t *config)
         /* Failed to allocate memory */
         return ESP_ERR_HTTPD_ALLOC_MEM;
     }
+#if CONFIG_HTTPD_QUEUE_WORK_BLOCKING
+    /* Using a Counting Semaphore with count equals CONFIG_LWIP_UDP_RECVMBOX_SIZE
+     * as the number of UDP messages which can be stored is equal to UDP mailbox size.
+     * Using this, we can make sure that the work function is always received by the ctrl socket.
+     */
+    hd->ctrl_sock_semaphore = xSemaphoreCreateCounting(CONFIG_LWIP_UDP_RECVMBOX_SIZE, CONFIG_LWIP_UDP_RECVMBOX_SIZE);
+    if (hd->ctrl_sock_semaphore == NULL) {
+        ESP_LOGE(TAG, "Failed to create Semaphore");
+        httpd_delete(hd);
+        return ESP_ERR_HTTPD_ALLOC_MEM;
+    }
+#endif
 
     if (httpd_server_init(hd) != ESP_OK) {
         httpd_delete(hd);
@@ -482,6 +516,9 @@ esp_err_t httpd_stop(httpd_handle_t handle)
     }
 
     ESP_LOGD(TAG, LOG_FMT("server stopped"));
+#if CONFIG_HTTPD_QUEUE_WORK_BLOCKING
+    vSemaphoreDelete(hd->ctrl_sock_semaphore);
+#endif
     httpd_delete(hd);
     return ESP_OK;
 }