refactor: switch task creation to withcaps APIs

This commit is contained in:
zhouli
2026-04-20 15:02:47 +08:00
parent 2abde1fe58
commit 70fc08e612
16 changed files with 391 additions and 247 deletions
+1 -1
View File
@@ -383,7 +383,7 @@ esp_err_t app_claw_start(const basic_demo_settings_t *settings)
core_config.append_session_turn = claw_memory_append_session_turn_callback;
#endif
core_config.call_cap = claw_cap_call_from_core;
core_config.task_stack_size = 6 * 1024;
core_config.task_stack_size = 16 * 1024;
core_config.task_priority = 5;
core_config.task_core = tskNO_AFFINITY;
core_config.max_tool_iterations = 20;
@@ -18,10 +18,10 @@
#include "cap_im_attachment.h"
#include "claw_cap.h"
#include "claw_task.h"
#include "claw_event_publisher.h"
#include "cJSON.h"
#include "esp_crt_bundle.h"
#include "esp_heap_caps.h"
#include "esp_event.h"
#include "esp_http_client.h"
#include "esp_log.h"
@@ -54,22 +54,10 @@
#define CAP_IM_FEISHU_DEDUP_CACHE_SIZE 64
#define CAP_IM_FEISHU_RECONNECT_DELAY_MS 3000
#define CAP_IM_FEISHU_INITIAL_CONNECT_TIMEOUT_MS 15000
#define CAP_IM_FEISHU_ATTACHMENT_STACK 8192
#define CAP_IM_FEISHU_ATTACHMENT_QUEUE_LEN 8
static const char *TAG = "cap_im_feishu";
static UBaseType_t cap_im_feishu_task_memory_caps(void)
{
#if defined(CONFIG_FREERTOS_TASK_CREATE_ALLOW_EXT_MEM) && CONFIG_FREERTOS_TASK_CREATE_ALLOW_EXT_MEM
if (heap_caps_get_total_size(MALLOC_CAP_SPIRAM) > 0) {
return MALLOC_CAP_SPIRAM;
}
#endif
return MALLOC_CAP_INTERNAL;
}
typedef struct {
char *buf;
size_t len;
@@ -1553,7 +1541,7 @@ static void cap_im_feishu_attachment_task(void *arg)
}
s_feishu.attachment_task = NULL;
vTaskDeleteWithCaps(NULL);
claw_task_delete(NULL);
}
static void cap_im_feishu_queue_attachment(const char *chat_id,
@@ -1965,7 +1953,7 @@ static void cap_im_feishu_ws_task(void *arg)
s_feishu.ws_client = NULL;
s_feishu.ws_task = NULL;
vTaskDeleteWithCaps(NULL);
claw_task_delete(NULL);
}
static esp_err_t cap_im_feishu_gateway_init(void)
@@ -2554,14 +2542,16 @@ esp_err_t cap_im_feishu_start(void)
}
}
if (!s_feishu.attachment_task) {
ok = xTaskCreatePinnedToCoreWithCaps(cap_im_feishu_attachment_task,
"feishu_attach",
CAP_IM_FEISHU_ATTACHMENT_STACK,
NULL,
5,
&s_feishu.attachment_task,
tskNO_AFFINITY,
cap_im_feishu_task_memory_caps());
ok = claw_task_create(&(claw_task_config_t){
.name = "feishu_attach",
.stack_size = 8192,
.priority = 5,
.core_id = tskNO_AFFINITY,
.stack_policy = CLAW_TASK_STACK_PREFER_PSRAM,
},
cap_im_feishu_attachment_task,
NULL,
&s_feishu.attachment_task);
if (ok != pdPASS) {
vQueueDelete(s_feishu.attachment_queue);
s_feishu.attachment_queue = NULL;
@@ -2571,17 +2561,19 @@ esp_err_t cap_im_feishu_start(void)
}
s_feishu.stop_requested = false;
ok = xTaskCreatePinnedToCoreWithCaps(cap_im_feishu_ws_task,
"feishu_ws",
8 * 1024,
NULL,
5,
&s_feishu.ws_task,
tskNO_AFFINITY,
cap_im_feishu_task_memory_caps());
ok = claw_task_create(&(claw_task_config_t){
.name = "feishu_ws",
.stack_size = 8192,
.priority = 5,
.core_id = tskNO_AFFINITY,
.stack_policy = CLAW_TASK_STACK_PREFER_PSRAM,
},
cap_im_feishu_ws_task,
NULL,
&s_feishu.ws_task);
if (ok != pdPASS) {
if (s_feishu.attachment_task) {
vTaskDeleteWithCaps(s_feishu.attachment_task);
claw_task_delete(s_feishu.attachment_task);
s_feishu.attachment_task = NULL;
}
if (s_feishu.attachment_queue) {
@@ -17,9 +17,9 @@
#include <sys/types.h>
#include "cJSON.h"
#include "claw_task.h"
#include "claw_event_publisher.h"
#include "esp_crt_bundle.h"
#include "esp_heap_caps.h"
#include "esp_http_client.h"
#include "esp_log.h"
#include "esp_timer.h"
@@ -41,7 +41,6 @@ static const char *TAG = "cap_im_qq";
#define CAP_IM_QQ_HTTP_RESP_INIT 2048
#define CAP_IM_QQ_WS_TASK_STACK 6144
#define CAP_IM_QQ_WS_CLIENT_STACK 8192
#define CAP_IM_QQ_INBOUND_TASK_STACK 8192
#define CAP_IM_QQ_WS_PRIO 5
#define CAP_IM_QQ_INBOUND_QUEUE_LEN 8
#define CAP_IM_QQ_DEDUP_CACHE_SIZE 64
@@ -68,17 +67,6 @@ static const char *TAG = "cap_im_qq";
#define BASIC_DEMO_QQ_APP_SECRET ""
#endif
static UBaseType_t cap_im_qq_task_memory_caps(void)
{
#if defined(CONFIG_FREERTOS_TASK_CREATE_ALLOW_EXT_MEM) && CONFIG_FREERTOS_TASK_CREATE_ALLOW_EXT_MEM
if (heap_caps_get_total_size(MALLOC_CAP_SPIRAM) > 0) {
return MALLOC_CAP_SPIRAM;
}
#endif
return MALLOC_CAP_INTERNAL;
}
typedef struct {
char *buf;
size_t len;
@@ -1065,7 +1053,7 @@ static void cap_im_qq_inbound_task(void *arg)
}
s_qq.inbound_task = NULL;
vTaskDeleteWithCaps(NULL);
claw_task_delete(NULL);
}
static void cap_im_qq_ws_event_handler(void *arg,
@@ -1640,7 +1628,7 @@ static void cap_im_qq_ws_task(void *arg)
s_qq.ws_task = NULL;
s_qq.ws_client = NULL;
vTaskDeleteWithCaps(NULL);
claw_task_delete(NULL);
}
static void cap_im_qq_reset_runtime_state(void)
@@ -1676,14 +1664,16 @@ static esp_err_t cap_im_qq_gateway_start(void)
}
}
ok = xTaskCreatePinnedToCoreWithCaps(cap_im_qq_inbound_task,
"qq_inbound",
CAP_IM_QQ_INBOUND_TASK_STACK,
NULL,
CAP_IM_QQ_WS_PRIO,
&s_qq.inbound_task,
tskNO_AFFINITY,
cap_im_qq_task_memory_caps());
ok = claw_task_create(&(claw_task_config_t){
.name = "qq_inbound",
.stack_size = 8192,
.priority = 5,
.core_id = tskNO_AFFINITY,
.stack_policy = CLAW_TASK_STACK_PREFER_PSRAM,
},
cap_im_qq_inbound_task,
NULL,
&s_qq.inbound_task);
if (ok != pdPASS) {
vQueueDelete(s_qq.inbound_queue);
s_qq.inbound_queue = NULL;
@@ -1691,14 +1681,16 @@ static esp_err_t cap_im_qq_gateway_start(void)
return ESP_FAIL;
}
ok = xTaskCreatePinnedToCoreWithCaps(cap_im_qq_ws_task,
"qq_ws",
CAP_IM_QQ_WS_TASK_STACK,
NULL,
CAP_IM_QQ_WS_PRIO,
&s_qq.ws_task,
tskNO_AFFINITY,
cap_im_qq_task_memory_caps());
ok = claw_task_create(&(claw_task_config_t){
.name = "qq_ws",
.stack_size = 6144,
.priority = 5,
.core_id = tskNO_AFFINITY,
.stack_policy = CLAW_TASK_STACK_PREFER_PSRAM,
},
cap_im_qq_ws_task,
NULL,
&s_qq.ws_task);
if (ok != pdPASS) {
s_qq.stop_requested = true;
s_qq.ws_task = NULL;
@@ -17,9 +17,9 @@
#include <sys/types.h>
#include "cJSON.h"
#include "claw_task.h"
#include "claw_event_publisher.h"
#include "esp_crt_bundle.h"
#include "esp_heap_caps.h"
#include "esp_http_client.h"
#include "esp_log.h"
#include "esp_timer.h"
@@ -35,9 +35,6 @@ static const char *TAG = "cap_im_tg";
#define CAP_IM_TG_MAX_MSG_LEN 4096
#define CAP_IM_TG_POLL_TIMEOUT_S 20
#define CAP_IM_TG_RETRY_DELAY_MS 3000
#define CAP_IM_TG_TASK_STACK 6144
#define CAP_IM_TG_ATTACHMENT_STACK 8192
#define CAP_IM_TG_TASK_PRIO 5
#define CAP_IM_TG_ATTACHMENT_QUEUE_LEN 8
#define CAP_IM_TG_DEDUP_CACHE_SIZE 64
#define CAP_IM_TG_PATH_BUF_SIZE 256
@@ -48,17 +45,6 @@ static const char *TAG = "cap_im_tg";
#define BASIC_DEMO_TG_BOT_TOKEN ""
#endif
static UBaseType_t cap_im_tg_task_memory_caps(void)
{
#if defined(CONFIG_FREERTOS_TASK_CREATE_ALLOW_EXT_MEM) && CONFIG_FREERTOS_TASK_CREATE_ALLOW_EXT_MEM
if (heap_caps_get_total_size(MALLOC_CAP_SPIRAM) > 0) {
return MALLOC_CAP_SPIRAM;
}
#endif
return MALLOC_CAP_INTERNAL;
}
typedef struct {
char *buf;
size_t len;
@@ -558,7 +544,7 @@ static void cap_im_tg_attachment_task(void *arg)
}
s_tg.attachment_task = NULL;
vTaskDeleteWithCaps(NULL);
claw_task_delete(NULL);
}
static void cap_im_tg_queue_attachment(const char *chat_id,
@@ -1127,7 +1113,7 @@ static void cap_im_tg_poll_task(void *arg)
}
s_tg.poll_task = NULL;
vTaskDeleteWithCaps(NULL);
claw_task_delete(NULL);
}
static esp_err_t cap_im_tg_gateway_init(void)
@@ -1161,28 +1147,32 @@ static esp_err_t cap_im_tg_gateway_start(void)
return ESP_ERR_NO_MEM;
}
}
ok = xTaskCreatePinnedToCoreWithCaps(cap_im_tg_attachment_task,
"tg_attach",
CAP_IM_TG_ATTACHMENT_STACK,
NULL,
CAP_IM_TG_TASK_PRIO,
&s_tg.attachment_task,
tskNO_AFFINITY,
cap_im_tg_task_memory_caps());
ok = claw_task_create(&(claw_task_config_t){
.name = "tg_attach",
.stack_size = 8192,
.priority = 5,
.core_id = tskNO_AFFINITY,
.stack_policy = CLAW_TASK_STACK_PREFER_PSRAM,
},
cap_im_tg_attachment_task,
NULL,
&s_tg.attachment_task);
if (ok != pdPASS) {
vQueueDelete(s_tg.attachment_queue);
s_tg.attachment_queue = NULL;
s_tg.attachment_task = NULL;
return ESP_FAIL;
}
ok = xTaskCreatePinnedToCoreWithCaps(cap_im_tg_poll_task,
"tg_poll",
CAP_IM_TG_TASK_STACK,
NULL,
CAP_IM_TG_TASK_PRIO,
&s_tg.poll_task,
tskNO_AFFINITY,
cap_im_tg_task_memory_caps());
ok = claw_task_create(&(claw_task_config_t){
.name = "tg_poll",
.stack_size = 6144,
.priority = 5,
.core_id = tskNO_AFFINITY,
.stack_policy = CLAW_TASK_STACK_PREFER_PSRAM,
},
cap_im_tg_poll_task,
NULL,
&s_tg.poll_task);
if (ok != pdPASS) {
s_tg.stop_requested = true;
s_tg.poll_task = NULL;
@@ -17,10 +17,10 @@
#include "cap_im_attachment.h"
#include "cJSON.h"
#include "claw_cap.h"
#include "claw_task.h"
#include "claw_event_publisher.h"
#include "esp_crt_bundle.h"
#include "esp_err.h"
#include "esp_heap_caps.h"
#include "esp_http_client.h"
#include "esp_log.h"
#include "esp_random.h"
@@ -40,8 +40,6 @@ static const char *TAG = "cap_im_wechat";
#define CAP_IM_WECHAT_MAX_MSG_LEN 4000
#define CAP_IM_WECHAT_POLL_TIMEOUT_MS 35000
#define CAP_IM_WECHAT_RETRY_DELAY_MS 2000
#define CAP_IM_WECHAT_TASK_STACK 6144
#define CAP_IM_WECHAT_TASK_PRIO 5
#define CAP_IM_WECHAT_DEDUP_CACHE_SIZE 64
#define CAP_IM_WECHAT_CONTEXT_CACHE_SIZE 32
#define CAP_IM_WECHAT_PATH_BUF_SIZE 256
@@ -121,17 +119,6 @@ typedef struct {
cap_im_wechat_qr_state_t qr;
} cap_im_wechat_state_t;
static UBaseType_t cap_im_wechat_task_memory_caps(void)
{
#if defined(CONFIG_FREERTOS_TASK_CREATE_ALLOW_EXT_MEM) && CONFIG_FREERTOS_TASK_CREATE_ALLOW_EXT_MEM
if (heap_caps_get_total_size(MALLOC_CAP_SPIRAM) > 0) {
return MALLOC_CAP_SPIRAM;
}
#endif
return MALLOC_CAP_INTERNAL;
}
static cap_im_wechat_state_t s_wechat = {
.base_url = CAP_IM_WECHAT_DEFAULT_BASE_URL,
.cdn_base_url = CAP_IM_WECHAT_DEFAULT_CDN_BASE_URL,
@@ -1483,7 +1470,7 @@ static void cap_im_wechat_poll_task(void *arg)
}
s_wechat.poll_task = NULL;
vTaskDeleteWithCaps(NULL);
claw_task_delete(NULL);
}
static esp_err_t cap_im_wechat_send_message_json(cJSON *msg_root)
@@ -2005,14 +1992,16 @@ static esp_err_t cap_im_wechat_gateway_start(void)
}
s_wechat.stop_requested = false;
ok = xTaskCreatePinnedToCoreWithCaps(cap_im_wechat_poll_task,
"wechat_poll",
CAP_IM_WECHAT_TASK_STACK,
NULL,
CAP_IM_WECHAT_TASK_PRIO,
&s_wechat.poll_task,
tskNO_AFFINITY,
cap_im_wechat_task_memory_caps());
ok = claw_task_create(&(claw_task_config_t){
.name = "wechat_poll",
.stack_size = 6144,
.priority = 5,
.core_id = tskNO_AFFINITY,
.stack_policy = CLAW_TASK_STACK_PREFER_PSRAM,
},
cap_im_wechat_poll_task,
NULL,
&s_wechat.poll_task);
if (ok != pdPASS) {
s_wechat.poll_task = NULL;
return ESP_FAIL;
@@ -2178,14 +2167,16 @@ esp_err_t cap_im_wechat_qr_login_start(const char *account_id, bool force)
}
if (!s_wechat.qr_task) {
ok = xTaskCreatePinnedToCoreWithCaps(cap_im_wechat_qr_task,
"wechat_qr",
CAP_IM_WECHAT_TASK_STACK,
NULL,
CAP_IM_WECHAT_TASK_PRIO,
&s_wechat.qr_task,
tskNO_AFFINITY,
cap_im_wechat_task_memory_caps());
ok = claw_task_create(&(claw_task_config_t){
.name = "wechat_qr",
.stack_size = 6144,
.priority = 5,
.core_id = tskNO_AFFINITY,
.stack_policy = CLAW_TASK_STACK_PREFER_PSRAM,
},
cap_im_wechat_qr_task,
NULL,
&s_wechat.qr_task);
if (ok != pdPASS) {
s_wechat.qr.active = false;
s_wechat.qr_task = NULL;
@@ -2599,5 +2590,5 @@ static void cap_im_wechat_qr_task(void *arg)
vTaskDelay(pdMS_TO_TICKS(1000));
}
vTaskDeleteWithCaps(NULL);
claw_task_delete(NULL);
}
@@ -11,6 +11,7 @@
#include <stdlib.h>
#include <string.h>
#include "claw_task.h"
#include "esp_heap_caps.h"
#include "esp_log.h"
#include "esp_random.h"
@@ -54,12 +55,6 @@ static SemaphoreHandle_t s_slot_terminal_sem[CAP_LUA_ASYNC_MAX_JOBS];
static size_t s_running_jobs;
static bool s_runner_started;
static UBaseType_t cap_lua_task_memory_caps(void)
{
/* Lua async jobs touch FATFS/flash-backed files, so their stacks must stay internal. */
return MALLOC_CAP_INTERNAL;
}
const char *cap_lua_job_status_name(cap_lua_job_status_t status)
{
switch (status) {
@@ -233,7 +228,7 @@ static void cap_lua_job_task(void *arg)
esp_err_t err;
if (!ctx) {
vTaskDeleteWithCaps(NULL);
claw_task_delete(NULL);
return;
}
@@ -242,7 +237,7 @@ static void cap_lua_job_task(void *arg)
cap_lua_finish_job(ctx, false, NULL, "failed to allocate output buffer");
free(ctx->args_json);
free(ctx);
vTaskDeleteWithCaps(NULL);
claw_task_delete(NULL);
return;
}
@@ -256,7 +251,7 @@ static void cap_lua_job_task(void *arg)
free(output);
free(ctx->args_json);
free(ctx);
vTaskDeleteWithCaps(NULL);
claw_task_delete(NULL);
}
esp_err_t cap_lua_async_init(void)
@@ -653,40 +648,43 @@ static esp_err_t cap_lua_async_submit_once(const cap_lua_async_job_t *job,
s_running_jobs++;
xSemaphoreGive(s_job_lock);
if (xTaskCreatePinnedToCoreWithCaps(cap_lua_job_task,
"cap_lua_async",
CAP_LUA_ASYNC_STACK,
ctx,
CAP_LUA_ASYNC_PRIO,
&s_jobs[slot].task_handle,
tskNO_AFFINITY,
cap_lua_task_memory_caps()) != pdPASS) {
UBaseType_t caps = cap_lua_task_memory_caps();
size_t free_bytes = heap_caps_get_free_size(caps);
size_t largest = heap_caps_get_largest_free_block(caps);
ESP_LOGE(TAG, "Failed to spawn task (stack=%u, free=%u, largest_block=%u)",
(unsigned)CAP_LUA_ASYNC_STACK,
(unsigned)free_bytes,
(unsigned)largest);
if (err_out && err_out_size > 0) {
snprintf(err_out, err_out_size,
"Out of internal RAM for Lua task stack "
"(need %u bytes, largest free block %u bytes, total free %u bytes). "
"Stop another job and retry.",
(unsigned)CAP_LUA_ASYNC_STACK,
(unsigned)largest,
(unsigned)free_bytes);
}
if (xSemaphoreTake(s_job_lock, pdMS_TO_TICKS(1000)) == pdTRUE) {
cap_lua_clear_slot(&s_jobs[slot]);
if (s_running_jobs > 0) {
s_running_jobs--;
{
claw_task_config_t task_config = {
.name = "cap_lua_async",
.stack_size = 12 * 1024,
.priority = 4,
.core_id = tskNO_AFFINITY,
.stack_policy = CLAW_TASK_STACK_PREFER_PSRAM,
};
if (claw_task_create(&task_config, cap_lua_job_task, ctx, &s_jobs[slot].task_handle) != pdPASS) {
size_t free_bytes = heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
size_t largest = heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL);
ESP_LOGE(TAG, "Failed to spawn task (stack=%u, free=%u, largest_block=%u)",
(unsigned)task_config.stack_size,
(unsigned)free_bytes,
(unsigned)largest);
if (err_out && err_out_size > 0) {
snprintf(err_out, err_out_size,
"Out of task memory for Lua task stack "
"(need %u bytes, largest free block %u bytes, total free %u bytes). "
"Stop another job and retry.",
(unsigned)task_config.stack_size,
(unsigned)largest,
(unsigned)free_bytes);
}
xSemaphoreGive(s_job_lock);
if (xSemaphoreTake(s_job_lock, pdMS_TO_TICKS(1000)) == pdTRUE) {
cap_lua_clear_slot(&s_jobs[slot]);
if (s_running_jobs > 0) {
s_running_jobs--;
}
xSemaphoreGive(s_job_lock);
}
free(ctx->args_json);
free(ctx);
return ESP_ERR_NO_MEM;
}
free(ctx->args_json);
free(ctx);
return ESP_ERR_NO_MEM;
}
if (xSemaphoreTake(s_job_lock, pdMS_TO_TICKS(1000)) == pdTRUE) {
@@ -20,8 +20,6 @@
#define CAP_LUA_ASYNC_DEFAULT_TIMEOUT_MS 0
#define CAP_LUA_ASYNC_MAX_JOBS 16
#define CAP_LUA_ASYNC_MAX_CONCURRENT 4
#define CAP_LUA_ASYNC_STACK (12 * 1024)
#define CAP_LUA_ASYNC_PRIO 4
#define CAP_LUA_MAX_MODULES 16
#define CAP_LUA_JOB_NAME_MAX 32
@@ -10,8 +10,10 @@
#include "cap_scheduler_internal.h"
#include "claw_cap.h"
#include "claw_task.h"
#include "esp_check.h"
#include "esp_log.h"
#include "freertos/idf_additions.h"
static const char *TAG = "cap_scheduler";
@@ -568,7 +570,7 @@ static void cap_scheduler_task(void *arg)
}
s_cap_scheduler.started = false;
s_cap_scheduler.task_handle = NULL;
vTaskDelete(NULL);
claw_task_delete(NULL);
}
static esp_err_t cap_scheduler_load_from_disk_locked(void)
@@ -809,6 +811,13 @@ esp_err_t cap_scheduler_init(const cap_scheduler_config_t *config)
esp_err_t cap_scheduler_start(void)
{
BaseType_t ok;
claw_task_config_t task_config = {
.name = "cap_scheduler",
.stack_size = s_cap_scheduler.config.task_stack_size,
.priority = s_cap_scheduler.config.task_priority,
.core_id = s_cap_scheduler.config.task_core,
.stack_policy = CLAW_TASK_STACK_PREFER_PSRAM,
};
if (!s_cap_scheduler.initialized) {
return ESP_ERR_INVALID_STATE;
@@ -818,22 +827,7 @@ esp_err_t cap_scheduler_start(void)
}
s_cap_scheduler.stop_requested = false;
if (s_cap_scheduler.config.task_core == tskNO_AFFINITY) {
ok = xTaskCreate(cap_scheduler_task,
"cap_scheduler",
s_cap_scheduler.config.task_stack_size,
NULL,
s_cap_scheduler.config.task_priority,
&s_cap_scheduler.task_handle);
} else {
ok = xTaskCreatePinnedToCore(cap_scheduler_task,
"cap_scheduler",
s_cap_scheduler.config.task_stack_size,
NULL,
s_cap_scheduler.config.task_priority,
&s_cap_scheduler.task_handle,
s_cap_scheduler.config.task_core);
}
ok = claw_task_create(&task_config, cap_scheduler_task, NULL, &s_cap_scheduler.task_handle);
if (ok != pdPASS) {
s_cap_scheduler.task_handle = NULL;
return ESP_FAIL;
@@ -13,21 +13,20 @@
#include "cJSON.h"
#include "claw_cap.h"
#include "claw_task.h"
#include "esp_chip_info.h"
#include "esp_err.h"
#include "esp_heap_caps.h"
#include "esp_log.h"
#include "esp_netif.h"
#include "esp_system.h"
#include "esp_timer.h"
#include "esp_wifi.h"
#include "freertos/FreeRTOS.h"
#include "freertos/idf_additions.h"
#include "freertos/task.h"
static const char *TAG = "cap_system";
#define CAP_SYSTEM_RESTART_TASK_STACK_SIZE 3072
#define CAP_SYSTEM_RESTART_TASK_PRIORITY 5
#define CAP_SYSTEM_RESTART_DEFAULT_DELAY_MS 500
#ifdef CONFIG_CLAW_CAP_SYSTEM_DEBUG_LOGS
@@ -406,12 +405,16 @@ static esp_err_t cap_system_restart_async(uint32_t delay_ms)
task_args->delay_ms = delay_ms;
// Restart is deferred to let the current response flush out first.
ok = xTaskCreate(cap_system_restart_task,
"cap_system_restart",
CAP_SYSTEM_RESTART_TASK_STACK_SIZE,
task_args,
CAP_SYSTEM_RESTART_TASK_PRIORITY,
NULL);
ok = claw_task_create(&(claw_task_config_t){
.name = "cap_system_restart",
.stack_size = 3072,
.priority = 5,
.core_id = tskNO_AFFINITY,
.stack_policy = CLAW_TASK_STACK_PREFER_PSRAM,
},
cap_system_restart_task,
task_args,
NULL);
if (ok != pdPASS) {
free(task_args);
ESP_LOGE(TAG, "restart task create failed");
@@ -13,11 +13,13 @@
#include <time.h>
#include "claw_cap.h"
#include "claw_task.h"
#include "esp_check.h"
#include "esp_crt_bundle.h"
#include "esp_http_client.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/idf_additions.h"
#include "freertos/semphr.h"
#include "freertos/task.h"
@@ -29,8 +31,6 @@ static const char *TAG = "cap_time";
#define CAP_TIME_UTC_TIMEZONE "UTC0"
#define CAP_TIME_DEFAULT_DISCONNECTED_RETRY_MS 5000
#define CAP_TIME_DEFAULT_SYNC_RETRY_MS 30000
#define CAP_TIME_SYNC_TASK_STACK_SIZE 4096
#define CAP_TIME_SYNC_TASK_PRIORITY 5
static const char *s_month_names[] = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
@@ -321,7 +321,7 @@ static void cap_time_sync_service_task(void *arg)
s_time_service.running = false;
s_time_service.task_handle = NULL;
vTaskDelete(NULL);
claw_task_delete(NULL);
}
static esp_err_t cap_time_execute(const char *input_json,
@@ -404,12 +404,16 @@ esp_err_t cap_time_sync_service_start(const cap_time_sync_service_config_t *conf
s_time_service.running = true;
ok = xTaskCreate(cap_time_sync_service_task,
"cap_time_sync",
CAP_TIME_SYNC_TASK_STACK_SIZE,
NULL,
CAP_TIME_SYNC_TASK_PRIORITY,
&s_time_service.task_handle);
ok = claw_task_create(&(claw_task_config_t){
.name = "cap_time_sync",
.stack_size = 4096,
.priority = 5,
.core_id = tskNO_AFFINITY,
.stack_policy = CLAW_TASK_STACK_PREFER_PSRAM,
},
cap_time_sync_service_task,
NULL,
&s_time_service.task_handle);
if (ok != pdPASS || !s_time_service.task_handle) {
s_time_service.running = false;
s_time_service.task_handle = NULL;
@@ -1,6 +1,7 @@
idf_component_register(
SRCS
"src/claw_core.c"
"src/claw_task.c"
"src/claw_core_llm.c"
"src/llm/claw_llm_runtime.c"
"src/llm/claw_llm_http_transport.c"
@@ -14,6 +15,7 @@ idf_component_register(
REQUIRES
esp_http_client
esp-tls
freertos
json
mbedtls
PRIV_REQUIRES
@@ -0,0 +1,67 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "esp_err.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
CLAW_TASK_STACK_INTERNAL_ONLY = 0,
CLAW_TASK_STACK_PREFER_PSRAM,
CLAW_TASK_STACK_PSRAM_ONLY,
} claw_task_stack_policy_t;
typedef struct {
const char *name;
uint32_t stack_size;
UBaseType_t priority;
BaseType_t core_id;
claw_task_stack_policy_t stack_policy;
} claw_task_config_t;
/**
* @brief Creates a task using the provided config, optionally overridden by the internal task config table
*
* This function is similar to xTaskCreatePinnedToCoreWithCaps(), except that it
* first resolves the caller-provided config against the internal override table.
* If an entry with the same task name exists in the override table, that entry
* is used as the effective task config. Otherwise, the caller-provided config is
* used as-is.
*
* The selected stack policy controls whether the task stack is allocated from
* internal RAM or PSRAM. However, the selected memory capabilities will NOT
* apply to the task's TCB as a TCB must always be in internal RAM.
*
* @param config Pointer to the caller-provided task config
* @param task_func Pointer to the task entry function
* @param arg Pointer that will be used as the parameter for the task being created
* @param task_handle Used to pass back a handle by which the created task can be referenced
* @return pdPASS if the task was successfully created and added to a ready list,
* otherwise an error code defined in the file projdefs.h
*/
BaseType_t claw_task_create(const claw_task_config_t *config,
TaskFunction_t task_func,
void *arg,
TaskHandle_t *task_handle);
/**
* @brief Deletes a task created through xTaskCreatePinnedToCoreWithCaps()
*
* This function is similar to vTaskDeleteWithCaps() and should be used to
* delete tasks whose stacks were allocated with explicit memory capabilities.
*
* @param task_handle Handle of the task to delete, or NULL to delete the calling task
*/
void claw_task_delete(TaskHandle_t task_handle);
#ifdef __cplusplus
}
#endif
@@ -4,6 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
#include "claw_core.h"
#include "claw_task.h"
#include <inttypes.h>
#include <stdbool.h>
@@ -14,7 +15,9 @@
#include <time.h>
#include "cJSON.h"
#include "esp_heap_caps.h"
#include "esp_log.h"
#include "freertos/idf_additions.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "freertos/task.h"
@@ -1395,22 +1398,16 @@ esp_err_t claw_core_start(void)
return ESP_OK;
}
if (s_core.task_core == tskNO_AFFINITY) {
task_result = xTaskCreate(claw_core_task,
"claw_core",
s_core.task_stack_size,
NULL,
s_core.task_priority,
&s_core.task_handle);
} else {
task_result = xTaskCreatePinnedToCore(claw_core_task,
"claw_core",
s_core.task_stack_size,
NULL,
s_core.task_priority,
&s_core.task_handle,
s_core.task_core);
}
task_result = claw_task_create(&(claw_task_config_t){
.name = "claw_core",
.stack_size = s_core.task_stack_size,
.priority = s_core.task_priority,
.core_id = s_core.task_core,
.stack_policy = CLAW_TASK_STACK_PREFER_PSRAM,
},
claw_core_task,
NULL,
&s_core.task_handle);
if (task_result != pdPASS) {
return ESP_FAIL;
@@ -0,0 +1,112 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "claw_task.h"
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
#include "esp_heap_caps.h"
#include "esp_log.h"
#include "freertos/idf_additions.h"
static const char *TAG = "claw_task";
/* Entries in this table override the config passed by the caller. Keep a trailing sentinel so the table can be left effectively empty. */
static const claw_task_config_t s_task_configs[] = {
{0},
};
static const claw_task_config_t *claw_task_find_override(const char *name)
{
size_t i;
if (!name || !name[0]) {
return NULL;
}
for (i = 0; s_task_configs[i].name; i++) {
if (strcmp(s_task_configs[i].name, name) == 0) {
return &s_task_configs[i];
}
}
return NULL;
}
static bool claw_task_external_memory_available(void)
{
#if defined(CONFIG_FREERTOS_TASK_CREATE_ALLOW_EXT_MEM) && CONFIG_FREERTOS_TASK_CREATE_ALLOW_EXT_MEM
return heap_caps_get_total_size(MALLOC_CAP_SPIRAM) > 0;
#else
return false;
#endif
}
static esp_err_t claw_task_resolve_config(const claw_task_config_t *input, claw_task_config_t *out)
{
const claw_task_config_t *override = NULL;
if (!input || !input->name || !input->name[0] || !out) {
return ESP_ERR_INVALID_ARG;
}
*out = *input;
override = claw_task_find_override(input->name);
if (override) {
*out = *override;
}
return ESP_OK;
}
static UBaseType_t claw_task_memory_caps(claw_task_stack_policy_t policy)
{
if (policy != CLAW_TASK_STACK_INTERNAL_ONLY && claw_task_external_memory_available()) {
return MALLOC_CAP_SPIRAM;
}
return MALLOC_CAP_INTERNAL;
}
BaseType_t claw_task_create(const claw_task_config_t *config,
TaskFunction_t task_func,
void *arg,
TaskHandle_t *task_handle)
{
claw_task_config_t resolved = {0};
UBaseType_t memory_caps;
if (claw_task_resolve_config(config, &resolved) != ESP_OK || !task_func || resolved.stack_size == 0) {
return errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
}
memory_caps = claw_task_memory_caps(resolved.stack_policy);
if (resolved.stack_policy == CLAW_TASK_STACK_PSRAM_ONLY && memory_caps != MALLOC_CAP_SPIRAM) {
ESP_LOGE(TAG, "task '%s' requires PSRAM stack but PSRAM is unavailable", resolved.name);
return errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
}
ESP_LOGD(TAG, "Creating task '%s' with stack size %u, priority %u, on core %d, with memory caps %s\n",
resolved.name,
(unsigned int)resolved.stack_size,
(unsigned int)resolved.priority,
resolved.core_id,
memory_caps == MALLOC_CAP_SPIRAM ? "SPIRAM" : "INTERNAL");
return xTaskCreatePinnedToCoreWithCaps(task_func,
resolved.name,
resolved.stack_size,
arg,
resolved.priority,
task_handle,
resolved.core_id,
memory_caps);
}
void claw_task_delete(TaskHandle_t task_handle)
{
vTaskDeleteWithCaps(task_handle);
}
@@ -19,7 +19,10 @@
#include "cJSON.h"
#include "claw_core.h"
#include "claw_event_publisher.h"
#include "claw_task.h"
#include "esp_heap_caps.h"
#include "esp_log.h"
#include "freertos/idf_additions.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "freertos/task.h"
@@ -2028,7 +2031,7 @@ static void claw_event_router_task(void *arg)
s_runtime.task_handle = NULL;
s_runtime.started = false;
vTaskDelete(NULL);
claw_task_delete(NULL);
}
esp_err_t claw_event_router_init(const claw_event_router_config_t *config)
@@ -2106,22 +2109,16 @@ esp_err_t claw_event_router_start(void)
s_runtime.config.core_receive_timeout_ms : CLAW_EVENT_ROUTER_DEFAULT_RECEIVE;
s_runtime.stop_requested = false;
if (core == tskNO_AFFINITY) {
task_ok = xTaskCreate(claw_event_router_task,
"event_router",
stack_size,
NULL,
priority,
&s_runtime.task_handle);
} else {
task_ok = xTaskCreatePinnedToCore(claw_event_router_task,
"event_router",
stack_size,
NULL,
priority,
&s_runtime.task_handle,
core);
}
task_ok = claw_task_create(&(claw_task_config_t){
.name = "event_router",
.stack_size = stack_size,
.priority = priority,
.core_id = core,
.stack_policy = CLAW_TASK_STACK_PREFER_PSRAM,
},
claw_event_router_task,
NULL,
&s_runtime.task_handle);
if (task_ok != pdPASS) {
s_runtime.task_handle = NULL;
return ESP_FAIL;
@@ -4,6 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
#include "claw_memory_internal.h"
#include "claw_task.h"
#include <ctype.h>
#include <inttypes.h>
@@ -12,6 +13,8 @@
#include <string.h>
#include "esp_log.h"
#include "esp_heap_caps.h"
#include "freertos/idf_additions.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "freertos/task.h"
@@ -378,7 +381,7 @@ static void claw_memory_async_extract_deinit(void)
job = next;
}
if (s_async_extract.task_handle) {
vTaskDelete(s_async_extract.task_handle);
claw_task_delete(s_async_extract.task_handle);
s_async_extract.task_handle = NULL;
}
if (s_async_extract.queue) {
@@ -447,12 +450,16 @@ esp_err_t claw_memory_async_extract_init(const claw_memory_config_t *config)
return ESP_ERR_NO_MEM;
}
task_result = xTaskCreate(claw_memory_async_extract_task,
"claw_mem_extract",
CLAW_MEMORY_ASYNC_EXTRACT_STACK_SIZE,
NULL,
CLAW_MEMORY_ASYNC_EXTRACT_PRIORITY,
&s_async_extract.task_handle);
task_result = claw_task_create(&(claw_task_config_t){
.name = "claw_mem_extract",
.stack_size = CLAW_MEMORY_ASYNC_EXTRACT_STACK_SIZE,
.priority = CLAW_MEMORY_ASYNC_EXTRACT_PRIORITY,
.core_id = tskNO_AFFINITY,
.stack_policy = CLAW_TASK_STACK_PREFER_PSRAM,
},
claw_memory_async_extract_task,
NULL,
&s_async_extract.task_handle);
if (task_result != pdPASS) {
claw_memory_async_extract_deinit();
return ESP_FAIL;