forked from espressif/esp-idf
[Examples] [Async Handlers] simplify code
This commit is contained in:
@@ -38,7 +38,7 @@ static const char *TAG = "example";
|
|||||||
|
|
||||||
// Async requests are queued here while they wait to
|
// Async requests are queued here while they wait to
|
||||||
// be processed by the workers
|
// be processed by the workers
|
||||||
static QueueHandle_t async_req_queue;
|
static QueueHandle_t request_queue;
|
||||||
|
|
||||||
// Track the number of free workers at any given time
|
// Track the number of free workers at any given time
|
||||||
static SemaphoreHandle_t worker_ready_count;
|
static SemaphoreHandle_t worker_ready_count;
|
||||||
@@ -53,22 +53,8 @@ typedef struct {
|
|||||||
httpd_req_handler_t handler;
|
httpd_req_handler_t handler;
|
||||||
} httpd_async_req_t;
|
} httpd_async_req_t;
|
||||||
|
|
||||||
|
// queue an HTTP req to the worker queue
|
||||||
static bool is_on_async_worker_thread(void)
|
static esp_err_t queue_request(httpd_req_t *req, httpd_req_handler_t handler)
|
||||||
{
|
|
||||||
// is our handle one of the known async handles?
|
|
||||||
TaskHandle_t handle = xTaskGetCurrentTaskHandle();
|
|
||||||
for (int i = 0; i < CONFIG_EXAMPLE_MAX_ASYNC_REQUESTS; i++) {
|
|
||||||
if (worker_handles[i] == handle) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Submit an HTTP req to the async worker queue
|
|
||||||
static esp_err_t submit_async_req(httpd_req_t *req, httpd_req_handler_t handler)
|
|
||||||
{
|
{
|
||||||
// must create a copy of the request that we own
|
// must create a copy of the request that we own
|
||||||
httpd_req_t* copy = NULL;
|
httpd_req_t* copy = NULL;
|
||||||
@@ -97,7 +83,7 @@ static esp_err_t submit_async_req(httpd_req_t *req, httpd_req_handler_t handler)
|
|||||||
|
|
||||||
// Since worker_ready_count > 0 the queue should already have space.
|
// Since worker_ready_count > 0 the queue should already have space.
|
||||||
// But lets wait up to 100ms just to be safe.
|
// But lets wait up to 100ms just to be safe.
|
||||||
if (xQueueSend(async_req_queue, &async_req, pdMS_TO_TICKS(100)) == false) {
|
if (xQueueSend(request_queue, &async_req, pdMS_TO_TICKS(100)) == false) {
|
||||||
ESP_LOGE(TAG, "worker queue is full");
|
ESP_LOGE(TAG, "worker queue is full");
|
||||||
httpd_req_async_handler_complete(copy); // cleanup
|
httpd_req_async_handler_complete(copy); // cleanup
|
||||||
return ESP_FAIL;
|
return ESP_FAIL;
|
||||||
@@ -106,25 +92,10 @@ static esp_err_t submit_async_req(httpd_req_t *req, httpd_req_handler_t handler)
|
|||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* handle long request (on async thread) */
|
||||||
/* A long running HTTP GET handler */
|
static esp_err_t long_async(httpd_req_t *req)
|
||||||
static esp_err_t long_async_handler(httpd_req_t *req)
|
|
||||||
{
|
{
|
||||||
ESP_LOGI(TAG, "uri: /long");
|
ESP_LOGI(TAG, "running: /long");
|
||||||
// This handler is first invoked on the httpd thread.
|
|
||||||
// In order to free the httpd thread to handle other requests,
|
|
||||||
// we must resubmit our request to be handled on an async worker thread.
|
|
||||||
if (is_on_async_worker_thread() == false) {
|
|
||||||
|
|
||||||
// submit
|
|
||||||
if (submit_async_req(req, long_async_handler) == ESP_OK) {
|
|
||||||
return ESP_OK;
|
|
||||||
} else {
|
|
||||||
httpd_resp_set_status(req, "503 Busy");
|
|
||||||
httpd_resp_sendstr(req, "<div> no workers available. server busy.</div>");
|
|
||||||
return ESP_OK;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// track the number of long requests
|
// track the number of long requests
|
||||||
static uint8_t req_count = 0;
|
static uint8_t req_count = 0;
|
||||||
@@ -154,7 +125,8 @@ static esp_err_t long_async_handler(httpd_req_t *req)
|
|||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void async_req_worker_task(void *p)
|
// each worker thread loops forever, processing requests
|
||||||
|
static void worker_task(void *p)
|
||||||
{
|
{
|
||||||
ESP_LOGI(TAG, "starting async req task worker");
|
ESP_LOGI(TAG, "starting async req task worker");
|
||||||
|
|
||||||
@@ -166,7 +138,7 @@ static void async_req_worker_task(void *p)
|
|||||||
|
|
||||||
// wait for a request
|
// wait for a request
|
||||||
httpd_async_req_t async_req;
|
httpd_async_req_t async_req;
|
||||||
if (xQueueReceive(async_req_queue, &async_req, portMAX_DELAY)) {
|
if (xQueueReceive(request_queue, &async_req, portMAX_DELAY)) {
|
||||||
|
|
||||||
ESP_LOGI(TAG, "invoking %s", async_req.req->uri);
|
ESP_LOGI(TAG, "invoking %s", async_req.req->uri);
|
||||||
|
|
||||||
@@ -185,7 +157,8 @@ static void async_req_worker_task(void *p)
|
|||||||
vTaskDelete(NULL);
|
vTaskDelete(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void start_async_req_workers(void)
|
// start worker threads
|
||||||
|
static void start_workers(void)
|
||||||
{
|
{
|
||||||
|
|
||||||
// counting semaphore keeps track of available workers
|
// counting semaphore keeps track of available workers
|
||||||
@@ -198,9 +171,9 @@ static void start_async_req_workers(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// create queue
|
// create queue
|
||||||
async_req_queue = xQueueCreate(1, sizeof(httpd_async_req_t));
|
request_queue = xQueueCreate(1, sizeof(httpd_async_req_t));
|
||||||
if (async_req_queue == NULL){
|
if (request_queue == NULL){
|
||||||
ESP_LOGE(TAG, "Failed to create async_req_queue");
|
ESP_LOGE(TAG, "Failed to create request_queue");
|
||||||
vSemaphoreDelete(worker_ready_count);
|
vSemaphoreDelete(worker_ready_count);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -208,7 +181,7 @@ static void start_async_req_workers(void)
|
|||||||
// start worker tasks
|
// start worker tasks
|
||||||
for (int i = 0; i < CONFIG_EXAMPLE_MAX_ASYNC_REQUESTS; i++) {
|
for (int i = 0; i < CONFIG_EXAMPLE_MAX_ASYNC_REQUESTS; i++) {
|
||||||
|
|
||||||
bool success = xTaskCreate(async_req_worker_task, "async_req_worker",
|
bool success = xTaskCreate(worker_task, "async_req_worker",
|
||||||
ASYNC_WORKER_TASK_STACK_SIZE, // stack size
|
ASYNC_WORKER_TASK_STACK_SIZE, // stack size
|
||||||
(void *)0, // argument
|
(void *)0, // argument
|
||||||
ASYNC_WORKER_TASK_PRIORITY, // priority
|
ASYNC_WORKER_TASK_PRIORITY, // priority
|
||||||
@@ -221,6 +194,20 @@ static void start_async_req_workers(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* adds /long request to the request queue */
|
||||||
|
static esp_err_t long_handler(httpd_req_t *req)
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG, "uri: /long");
|
||||||
|
|
||||||
|
// add to the async request queue
|
||||||
|
if (queue_request(req, long_async) == ESP_OK) {
|
||||||
|
return ESP_OK;
|
||||||
|
} else {
|
||||||
|
httpd_resp_set_status(req, "503 Busy");
|
||||||
|
httpd_resp_sendstr(req, "<div> no workers available. server busy.</div>");
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* A quick HTTP GET handler, which does not
|
/* A quick HTTP GET handler, which does not
|
||||||
use any asynchronous features */
|
use any asynchronous features */
|
||||||
@@ -271,7 +258,7 @@ static httpd_handle_t start_webserver(void)
|
|||||||
const httpd_uri_t long_uri = {
|
const httpd_uri_t long_uri = {
|
||||||
.uri = "/long",
|
.uri = "/long",
|
||||||
.method = HTTP_GET,
|
.method = HTTP_GET,
|
||||||
.handler = long_async_handler,
|
.handler = long_handler,
|
||||||
};
|
};
|
||||||
|
|
||||||
const httpd_uri_t quick_uri = {
|
const httpd_uri_t quick_uri = {
|
||||||
@@ -346,7 +333,7 @@ void app_main(void)
|
|||||||
#endif // CONFIG_EXAMPLE_CONNECT_ETHERNET
|
#endif // CONFIG_EXAMPLE_CONNECT_ETHERNET
|
||||||
|
|
||||||
// start workers
|
// start workers
|
||||||
start_async_req_workers();
|
start_workers();
|
||||||
|
|
||||||
/* Start the server for the first time */
|
/* Start the server for the first time */
|
||||||
server = start_webserver();
|
server = start_webserver();
|
||||||
|
Reference in New Issue
Block a user