| 1298 | } |
| 1299 | |
| 1300 | static bool runtime_worker_handle_application(cbm_daemon_runtime_worker_t *worker, |
| 1301 | const uint8_t *payload, uint32_t length) { |
| 1302 | if (!payload || length < APPLICATION_REQUEST_PREFIX_SIZE) { |
| 1303 | return false; |
| 1304 | } |
| 1305 | cbm_daemon_runtime_application_token_t request_token = runtime_get_u64(payload); |
| 1306 | uint32_t request_length = runtime_get_u32(payload + 8); |
| 1307 | if (request_length > CBM_DAEMON_RUNTIME_APPLICATION_PAYLOAD_MAX || |
| 1308 | length != APPLICATION_REQUEST_PREFIX_SIZE + request_length || |
| 1309 | request_token == CBM_DAEMON_RUNTIME_APPLICATION_TOKEN_INVALID || |
| 1310 | request_token <= worker->last_application_request_token) { |
| 1311 | return false; |
| 1312 | } |
| 1313 | /* A structurally valid token is consumed at admission, including BUSY, |
| 1314 | * UNAVAILABLE, and local allocation/thread failures. Reusing a token after |
| 1315 | * any terminal response would make late cancellation controls ambiguous. */ |
| 1316 | worker->last_application_request_token = request_token; |
| 1317 | cbm_daemon_runtime_service_t *service = worker->service; |
| 1318 | if (!service->application.request) { |
| 1319 | return runtime_worker_send_application_response( |
| 1320 | worker, request_token, CBM_DAEMON_RUNTIME_APPLICATION_UNAVAILABLE, NULL, 0, false); |
| 1321 | } |
| 1322 | (void)runtime_worker_reap_application(worker, false); |
| 1323 | if (worker->application_thread_started) { |
| 1324 | return runtime_worker_send_application_response( |
| 1325 | worker, request_token, CBM_DAEMON_RUNTIME_APPLICATION_BUSY, NULL, 0, false); |
| 1326 | } |
| 1327 | |
| 1328 | uint8_t *request_copy = NULL; |
| 1329 | if (request_length > 0) { |
| 1330 | request_copy = malloc(request_length); |
| 1331 | if (!request_copy) { |
| 1332 | return runtime_worker_send_application_response( |
| 1333 | worker, request_token, CBM_DAEMON_RUNTIME_APPLICATION_HANDLER_ERROR, NULL, 0, |
| 1334 | false); |
| 1335 | } |
| 1336 | memcpy(request_copy, payload + APPLICATION_REQUEST_PREFIX_SIZE, request_length); |
| 1337 | } |
| 1338 | worker->application_request_token = request_token; |
| 1339 | worker->application_request = request_copy; |
| 1340 | worker->application_request_length = request_length; |
| 1341 | atomic_store_explicit(&worker->application_thread_done, false, memory_order_release); |
| 1342 | if (cbm_thread_create(&worker->application_thread, RUNTIME_WORKER_STACK_SIZE, |
| 1343 | runtime_application_worker, worker) != 0) { |
| 1344 | free(worker->application_request); |
| 1345 | worker->application_request = NULL; |
| 1346 | worker->application_request_length = 0; |
| 1347 | return runtime_worker_send_application_response( |
| 1348 | worker, request_token, CBM_DAEMON_RUNTIME_APPLICATION_HANDLER_ERROR, NULL, 0, false); |
| 1349 | } |
| 1350 | worker->application_thread_started = true; |
| 1351 | return true; |
| 1352 | } |
| 1353 | |
| 1354 | static bool runtime_worker_handle_application_cancel(cbm_daemon_runtime_worker_t *worker, |
| 1355 | const uint8_t *payload, uint32_t length) { |
no test coverage detected