keep track of bg tasks for clean up
2 files changed, 103 insertions(+), 6 deletions(-) M src/camera.c M src/camera.h
M src/camera.c => src/camera.c +96-4
@@ 6,9 6,11 @@ #include <stdio.h> #include <sys/ioctl.h> #include <sys/mman.h> #include <sys/wait.h> #include <unistd.h> #define MAX_VIDEO_BUFFERS 20 #define MAX_BG_TASKS 8 static const char *pixel_format_names[MP_PIXEL_FMT_MAX] = { @@ "unsupported", "BGGR8", "GBRG8", "GRBG8", "RGGB8", "BGGR10P", 219,6 221,9 @@ struct _MPCamera { struct video_buffer buffers[MAX_VIDEO_BUFFERS]; uint32_t num_buffers; // keeping track of background task child-PIDs for cleanup code int child_bg_pids[MAX_BG_TASKS]; bool use_mplane; }; @@ 250,12 255,15 @@ mp_camera_new(int video_fd, int subdev_fd) camera->has_set_mode = false; camera->num_buffers = 0; camera->use_mplane = use_mplane; memset(camera->child_bg_pids, 0, sizeof(camera->child_bg_pids[0]) * MAX_BG_TASKS); return camera; } void mp_camera_free(MPCamera *camera) { mp_camera_wait_bg_tasks(camera); g_warn_if_fail(camera->num_buffers == 0); if (camera->num_buffers != 0) { @@ mp_camera_stop_capture(camera); 264,6 272,83 @@ mp_camera_free(MPCamera *camera) free(camera); } void mp_camera_add_bg_task(MPCamera *camera, pid_t pid) { int status; while (true) { for (size_t i = 0; i < MAX_BG_TASKS; ++i) { if (camera->child_bg_pids[i] == 0) { camera->child_bg_pids[i] = pid; return; } else { // error == -1, still running == 0 if (waitpid(camera->child_bg_pids[i], &status, WNOHANG) <= 0) continue; // consider errored wait still running if (WIFEXITED(status)) { // replace exited camera->child_bg_pids[i] = pid; return; } } } // wait for any status change on child processes pid_t changed = waitpid(-1, &status, 0); if (WIFEXITED(status)) { // some child exited for (size_t i = 0; i < MAX_BG_TASKS; ++i) { if (camera->child_bg_pids[i] == changed) { camera->child_bg_pids[i] = pid; return; } } } // no luck, repeat and check if something exited maybe } } void mp_camera_wait_bg_tasks(MPCamera *camera) { for (size_t i = 0; i < MAX_BG_TASKS; ++i) { if (camera->child_bg_pids[i] != 0) { // ignore errors waitpid(camera->child_bg_pids[i], NULL, 0); } } } bool mp_camera_check_task_complete(MPCamera *camera, pid_t pid) { // this method is potentially unsafe because pid could already be reused at // this point, but extremely unlikely so we won't implement this. int status; if (pid == 0) return true; // ignore errors (-1), no exit == 0 int pidchange = waitpid(pid, &status, WNOHANG); if (pidchange == -1) // error or exists and runs return false; if (WIFEXITED(status)) { for (size_t i = 0; i < MAX_BG_TASKS; ++i) { if (camera->child_bg_pids[i] == pid) { camera->child_bg_pids[i] = 0; break; } } return true; } else { return false; } } bool mp_camera_is_subdev(MPCamera *camera) @@ { 1178,7 1263,7 @@ control_impl_int32(MPCamera *camera, uint32_t id, int request, int32_t *value) return true; } void pid_t mp_camera_control_set_int32_bg(MPCamera *camera, uint32_t id, int32_t v) { @@ struct v4l2_ext_control ctrl = {}; 1195,8 1280,15 @@ mp_camera_control_set_int32_bg(MPCamera *camera, uint32_t id, int32_t v) int fd = control_fd(camera); // fork only after all the memory has been read if (fork() != 0) return; // discard errors, nothing to do in parent process pid_t pid = fork(); if (pid == -1) { return 0; // discard errors, nothing to do in parent process } else if (pid != 0) { // parent process adding pid to wait list (to clear zombie processes) mp_camera_add_bg_task(camera, pid); return pid; } // ignore errors xioctl(fd, VIDIOC_S_EXT_CTRLS, &ctrls); @@ // exit without calling exit handlers 1247,7 1339,7 @@ mp_camera_control_get_bool(MPCamera *camera, uint32_t id) return v; } void pid_t mp_camera_control_set_bool_bg(MPCamera *camera, uint32_t id, bool v) { int32_t value = v;
M src/camera.h => src/camera.h +7-2
@@ 1,6 1,7 @@ #pragma once #include <linux/v4l2-subdev.h> #include <sys/wait.h> #include <stdbool.h> #include <stdint.h> @@ 56,6 57,10 @@ typedef struct _MPCamera MPCamera; MPCamera *mp_camera_new(int video_fd, int subdev_fd); void mp_camera_free(MPCamera *camera); void mp_camera_add_bg_task(MPCamera *camera, pid_t pid); void mp_camera_wait_bg_tasks(MPCamera *camera); bool mp_camera_check_task_complete(MPCamera *camera, pid_t pid); bool mp_camera_is_subdev(MPCamera *camera); int mp_camera_get_video_fd(MPCamera *camera); @@ int mp_camera_get_subdev_fd(MPCamera *camera); 112,10 117,10 @@ bool mp_camera_control_try_int32(MPCamera *camera, uint32_t id, int32_t *v); bool mp_camera_control_set_int32(MPCamera *camera, uint32_t id, int32_t v); int32_t mp_camera_control_get_int32(MPCamera *camera, uint32_t id); // set the value in the background, discards result void mp_camera_control_set_int32_bg(MPCamera *camera, uint32_t id, int32_t v); pid_t mp_camera_control_set_int32_bg(MPCamera *camera, uint32_t id, int32_t v); bool mp_camera_control_try_bool(MPCamera *camera, uint32_t id, bool *v); bool mp_camera_control_set_bool(MPCamera *camera, uint32_t id, bool v); bool mp_camera_control_get_bool(MPCamera *camera, uint32_t id); // set the value in the background, discards result void mp_camera_control_set_bool_bg(MPCamera *camera, uint32_t id, bool v); pid_t mp_camera_control_set_bool_bg(MPCamera *camera, uint32_t id, bool v);