Commit: 4bc647b80a40ca0a4f40a97e929241b7c0dac469
Parent: 9f1baf0d742a1a6826f928322f73d0b6e83d0f40
Author: Randy Palamar
Date: Sun, 4 May 2025 16:39:03 -0600
lib: add error codes for failure cases
this is done in w32 GetLastError() style so that callers can still
treat all functions as having boolean returns. functions were
added for retrieving the last error code as a number or as a
string and for converting an error number to a string.
Diffstat:
4 files changed, 140 insertions(+), 82 deletions(-)
diff --git a/beamformer_parameters.h b/beamformer_parameters.h
@@ -66,6 +66,7 @@ typedef enum {
#define DAS_CYCLE_T_UNIFORM_LOC 3
#define MAX_BEAMFORMED_SAVED_FRAMES 16
+#define MAX_COMPUTE_SHADER_STAGES 16
/* TODO(rnp): actually use a substruct but generate a header compatible with MATLAB */
/* X(name, type, size, gltype, glsize, comment) */
diff --git a/beamformer_work_queue.h b/beamformer_work_queue.h
@@ -84,7 +84,7 @@ typedef struct {
};
};
- ComputeShaderID compute_stages[16];
+ ComputeShaderID compute_stages[MAX_COMPUTE_SHADER_STAGES];
u32 compute_stages_count;
i32 parameters_sync;
diff --git a/helpers/ogl_beamformer_lib.c b/helpers/ogl_beamformer_lib.c
@@ -6,6 +6,7 @@
#define PIPE_RETRY_PERIOD_MS (100ULL)
global BeamformerSharedMemory *g_bp;
+global BeamformerLibErrorKind g_lib_last_error;
#if defined(__linux__)
#include "../os_linux.c"
@@ -162,50 +163,91 @@ check_shared_memory(void)
b32 result = 1;
if (!g_bp) {
g_bp = os_open_shared_memory_area(OS_SHARED_MEMORY_NAME);
- if (!g_bp) result = 0;
+ if (!g_bp) {
+ result = 0;
+ g_lib_last_error = BF_LIB_ERR_KIND_SHARED_MEMORY;
+ }
}
return result;
}
-b32
-set_beamformer_pipeline(i32 *stages, i32 stages_count)
+function BeamformWork *
+try_push_work_queue(void)
{
- if (stages_count > ARRAY_COUNT(g_bp->compute_stages)) {
- //error_msg("maximum stage count is %lu", ARRAY_COUNT(g_bp->compute_stages));
- return 0;
- }
+ BeamformWork *result = beamform_work_queue_push(&g_bp->external_work_queue);
+ if (!result) g_lib_last_error = BF_LIB_ERR_KIND_WORK_QUEUE_FULL;
+ return result;
+}
- if (!check_shared_memory())
- return 0;
+function b32
+lib_try_wait_sync(i32 *sync, i32 timeout_ms, os_wait_on_value_fn *os_wait_on_value)
+{
+ b32 result = try_wait_sync(sync, timeout_ms, os_wait_on_value);
+ if (!result) g_lib_last_error = BF_LIB_ERR_KIND_SYNC_VARIABLE;
+ return result;
+}
- for (i32 i = 0; i < stages_count; i++) {
- b32 valid = 0;
- #define X(en, number, sfn, nh, pn) if (number == stages[i]) valid = 1;
- COMPUTE_SHADERS
- #undef X
- if (!valid) {
- //error_msg("invalid shader stage: %d", stages[i]);
- return 0;
- }
+const char *
+beamformer_error_string(BeamformerLibErrorKind kind)
+{
+ #define X(type, num, string) string,
+ local_persist const char *error_string_table[] = {BEAMFORMER_LIB_ERRORS "invalid error kind"};
+ #undef X
+ return error_string_table[MIN(kind, countof(error_string_table) - 1)];
+}
- g_bp->compute_stages[i] = stages[i];
- }
- g_bp->compute_stages_count = stages_count;
+BeamformerLibErrorKind
+beamformer_get_last_error(void)
+{
+ return g_lib_last_error;
+}
- return 1;
+const char *
+beamformer_get_last_error_string(void)
+{
+ return beamformer_error_string(beamformer_get_last_error());
+}
+
+b32
+set_beamformer_pipeline(i32 *stages, i32 stages_count)
+{
+ b32 result = 0;
+ if (stages_count <= countof(g_bp->compute_stages)) {
+ if (check_shared_memory()) {
+ g_bp->compute_stages_count = 0;
+ for (i32 i = 0; i < stages_count; i++)
+ if (BETWEEN(stages[i], 0, CS_LAST))
+ g_bp->compute_stages[g_bp->compute_stages_count++] = stages[i];
+
+ result = g_bp->compute_stages_count == stages_count;
+ if (!result) {
+ g_lib_last_error = BF_LIB_ERR_KIND_INVALID_COMPUTE_STAGE;
+ g_bp->compute_stages_count = 0;
+ }
+ }
+ } else {
+ g_lib_last_error = BF_LIB_ERR_KIND_COMPUTE_STAGE_OVERFLOW;
+ }
+ return result;
}
b32
beamformer_start_compute(u32 image_plane_tag)
{
- b32 result = image_plane_tag < IPT_LAST && check_shared_memory();
- if (result) {
- result = !atomic_load(&g_bp->dispatch_compute_sync);
- if (result) {
- g_bp->current_image_plane = image_plane_tag;
- atomic_store(&g_bp->dispatch_compute_sync, 1);
+ b32 result = 0;
+ if (image_plane_tag < IPT_LAST) {
+ if (check_shared_memory()) {
+ if (atomic_load(&g_bp->dispatch_compute_sync) == 0) {
+ g_bp->current_image_plane = image_plane_tag;
+ atomic_store(&g_bp->dispatch_compute_sync, 1);
+ result = 1;
+ } else {
+ g_lib_last_error = BF_LIB_ERR_KIND_SYNC_VARIABLE;
+ }
}
+ } else {
+ g_lib_last_error = BF_LIB_ERR_KIND_INVALID_IMAGE_PLANE;
}
return result;
}
@@ -214,10 +256,10 @@ function b32
beamformer_upload_buffer(void *data, u32 size, i32 store_offset, i32 sync_offset,
BeamformerUploadKind kind, i32 timeout_ms)
{
- b32 result = check_shared_memory();
- if (result) {
- BeamformWork *work = beamform_work_queue_push(&g_bp->external_work_queue);
- result = work && try_wait_sync((i32 *)((u8 *)g_bp + sync_offset), timeout_ms, os_wait_on_value);
+ b32 result = 0;
+ if (check_shared_memory()) {
+ BeamformWork *work = try_push_work_queue();
+ result = work && lib_try_wait_sync((i32 *)((u8 *)g_bp + sync_offset), timeout_ms, os_wait_on_value);
if (result) {
BeamformerUploadContext *uc = &work->upload_context;
uc->shared_memory_offset = store_offset;
@@ -239,12 +281,14 @@ beamformer_upload_buffer(void *data, u32 size, i32 store_offset, i32 sync_offset
#define X(name, dtype, elements, command) \
b32 beamformer_push_##name (dtype *data, u32 count, i32 timeout_ms) { \
- b32 result = count <= ARRAY_COUNT(g_bp->name); \
- if (result) { \
+ b32 result = 0; \
+ if (count <= countof(g_bp->name)) { \
result = beamformer_upload_buffer(data, count * elements * sizeof(dtype), \
offsetof(BeamformerSharedMemory, name), \
offsetof(BeamformerSharedMemory, name##_sync), \
BU_KIND_##command, timeout_ms); \
+ } else { \
+ g_lib_last_error = BF_LIB_ERR_KIND_BUFFER_OVERFLOW; \
} \
return result; \
}
@@ -264,11 +308,13 @@ beamformer_push_parameters(BeamformerParameters *bp, i32 timeout_ms)
b32
beamformer_push_data(void *data, u32 data_size, i32 timeout_ms)
{
- b32 result = data_size <= BEAMFORMER_MAX_RF_DATA_SIZE;
- if (result) {
+ b32 result = 0;
+ if (data_size <= BEAMFORMER_MAX_RF_DATA_SIZE) {
result = beamformer_upload_buffer(data, data_size, BEAMFORMER_RF_DATA_OFF,
offsetof(BeamformerSharedMemory, raw_data_sync),
BU_KIND_RF_DATA, timeout_ms);
+ } else {
+ g_lib_last_error = BF_LIB_ERR_KIND_BUFFER_OVERFLOW;
}
return result;
}
@@ -276,10 +322,10 @@ beamformer_push_data(void *data, u32 data_size, i32 timeout_ms)
b32
beamformer_push_parameters_ui(BeamformerUIParameters *bp, i32 timeout_ms)
{
- b32 result = check_shared_memory();
- if (result) {
- BeamformWork *work = beamform_work_queue_push(&g_bp->external_work_queue);
- result = work && try_wait_sync(&g_bp->parameters_ui_sync, timeout_ms, os_wait_on_value);
+ b32 result = 0;
+ if (check_shared_memory()) {
+ BeamformWork *work = try_push_work_queue();
+ result = work && lib_try_wait_sync(&g_bp->parameters_ui_sync, timeout_ms, os_wait_on_value);
if (result) {
BeamformerUploadContext *uc = &work->upload_context;
uc->shared_memory_offset = offsetof(BeamformerSharedMemory, parameters);
@@ -297,10 +343,10 @@ beamformer_push_parameters_ui(BeamformerUIParameters *bp, i32 timeout_ms)
b32
beamformer_push_parameters_head(BeamformerParametersHead *bp, i32 timeout_ms)
{
- b32 result = check_shared_memory();
- if (result) {
- BeamformWork *work = beamform_work_queue_push(&g_bp->external_work_queue);
- result = work && try_wait_sync(&g_bp->parameters_head_sync, timeout_ms, os_wait_on_value);
+ b32 result = 0;
+ if (check_shared_memory()) {
+ BeamformWork *work = try_push_work_queue();
+ result = work && lib_try_wait_sync(&g_bp->parameters_head_sync, timeout_ms, os_wait_on_value);
if (result) {
BeamformerUploadContext *uc = &work->upload_context;
uc->shared_memory_offset = offsetof(BeamformerSharedMemory, parameters);
@@ -334,19 +380,13 @@ set_beamformer_parameters(BeamformerParametersV0 *new_bp)
b32
send_data(void *data, u32 data_size)
{
- b32 result = beamformer_push_data(data, data_size, 0);
- if (result) {
- if (beamformer_start_compute(0)) {
+ b32 result = 0;
+ if (beamformer_push_data(data, data_size, 0)) {
+ result = beamformer_start_compute(0);
+ if (result) {
/* TODO(rnp): should we just set timeout on acquiring the lock instead of this? */
try_wait_sync(&g_bp->raw_data_sync, -1, os_wait_on_value);
atomic_store(&g_bp->raw_data_sync, 1);
- } else {
- result = 0;
- /* TODO(rnp): HACK: this is strictly meant for matlab; we need a real
- * recovery method. for most (all?) old api uses this won't be hit */
- //warning_msg("failed to start compute after sending data\n"
- // "library in a borked state\n"
- // "try calling beamformer_start_compute()");
}
}
return result;
@@ -355,34 +395,31 @@ send_data(void *data, u32 data_size)
b32
beamform_data_synchronized(void *data, u32 data_size, u32 output_points[3], f32 *out_data, i32 timeout_ms)
{
- if (!check_shared_memory())
- return 0;
-
- output_points[0] = MIN(1, output_points[0]);
- output_points[1] = MIN(1, output_points[1]);
- output_points[2] = MIN(1, output_points[2]);
-
- g_bp->parameters.output_points[0] = output_points[0];
- g_bp->parameters.output_points[1] = output_points[1];
- g_bp->parameters.output_points[2] = output_points[2];
- g_bp->export_next_frame = 1;
-
- Pipe export_pipe = os_open_read_pipe(OS_EXPORT_PIPE_NAME);
- if (export_pipe.file == INVALID_FILE) {
- //error_msg("failed to open export pipe");
- return 0;
- }
+ b32 result = 0;
+ if (check_shared_memory()) {
+ output_points[0] = MIN(1, output_points[0]);
+ output_points[1] = MIN(1, output_points[1]);
+ output_points[2] = MIN(1, output_points[2]);
+
+ g_bp->parameters.output_points[0] = output_points[0];
+ g_bp->parameters.output_points[1] = output_points[1];
+ g_bp->parameters.output_points[2] = output_points[2];
+ g_bp->export_next_frame = 1;
+
+ Pipe export_pipe = os_open_read_pipe(OS_EXPORT_PIPE_NAME);
+ if (export_pipe.file != INVALID_FILE) {
+ if (send_data(data, data_size)) {
+ iz output_size = output_points[0] * output_points[1] *
+ output_points[2] * sizeof(f32) * 2;
+ result = os_wait_read_pipe(export_pipe, out_data, output_size, timeout_ms);
+ if (!result) g_lib_last_error = BF_LIB_ERR_KIND_READ_EXPORT_PIPE;
+ }
- b32 result = send_data(data, data_size);
- if (result) {
- iz output_size = output_points[0] * output_points[1] * output_points[2] * sizeof(f32) * 2;
- result = os_wait_read_pipe(export_pipe, out_data, output_size, timeout_ms);
- //if (!result)
- // warning_msg("failed to read full export data from pipe");
+ os_disconnect_pipe(export_pipe);
+ os_close_pipe(&export_pipe.file, export_pipe.name);
+ } else {
+ g_lib_last_error = BF_LIB_ERR_KIND_OPEN_EXPORT_PIPE;
+ }
}
-
- os_disconnect_pipe(export_pipe);
- os_close_pipe(&export_pipe.file, export_pipe.name);
-
return result;
}
diff --git a/helpers/ogl_beamformer_lib.h b/helpers/ogl_beamformer_lib.h
@@ -8,6 +8,26 @@
#define LIB_FN
#endif
+#define BEAMFORMER_LIB_ERRORS \
+ X(NONE, 0, "None") \
+ X(COMPUTE_STAGE_OVERFLOW, 1, "compute stage overflow: maximum stages: " str(MAX_COMPUTE_SHADER_STAGES)) \
+ X(INVALID_COMPUTE_STAGE, 2, "invalid compute shader stage") \
+ X(INVALID_IMAGE_PLANE, 3, "invalid image plane") \
+ X(BUFFER_OVERFLOW, 4, "passed buffer size exceeds available space") \
+ X(WORK_QUEUE_FULL, 5, "work queue full") \
+ X(OPEN_EXPORT_PIPE, 6, "failed to open export pipe") \
+ X(READ_EXPORT_PIPE, 7, "failed to read full export data from pipe") \
+ X(SHARED_MEMORY, 8, "failed to open shared memory region") \
+ X(SYNC_VARIABLE, 9, "failed to acquire lock within timeout period")
+
+#define X(type, num, string) BF_LIB_ERR_KIND_ ##type = num,
+typedef enum {BEAMFORMER_LIB_ERRORS} BeamformerLibErrorKind;
+#undef X
+
+LIB_FN BeamformerLibErrorKind beamformer_get_last_error(void);
+LIB_FN const char *beamformer_get_last_error_string(void);
+LIB_FN const char *beamformer_error_string(BeamformerLibErrorKind kind);
+
/* IMPORTANT: timeout of -1 will block forever */
LIB_FN uint32_t set_beamformer_parameters(BeamformerParametersV0 *);