summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke T. Shumaker <lukeshu@lukeshu.com>2025-04-06 23:12:55 -0600
committerLuke T. Shumaker <lukeshu@lukeshu.com>2025-04-07 01:36:34 -0600
commitb46d9f8886517c5e54cafc8bba1a6e0e30fdb691 (patch)
tree65e969e9434e00d1070894da54ce6c19c99a682c
parentd5036ba89dcb89b7a05e5c6b2d7cd9cc53850d4d (diff)
libcr_ipc: Add tests for select
-rw-r--r--libcr_ipc/CMakeLists.txt2
-rw-r--r--libcr_ipc/include/libcr_ipc/select.h50
-rw-r--r--libcr_ipc/tests/config.h4
-rw-r--r--libcr_ipc/tests/test_select.c90
4 files changed, 118 insertions, 28 deletions
diff --git a/libcr_ipc/CMakeLists.txt b/libcr_ipc/CMakeLists.txt
index 2b13e37..cf8e5fd 100644
--- a/libcr_ipc/CMakeLists.txt
+++ b/libcr_ipc/CMakeLists.txt
@@ -15,9 +15,9 @@ target_link_libraries(libcr_ipc INTERFACE
set(ipc_tests
chan
- #select
mutex
rpc
+ select
sema
)
foreach(test IN LISTS ipc_tests)
diff --git a/libcr_ipc/include/libcr_ipc/select.h b/libcr_ipc/include/libcr_ipc/select.h
index 0e35351..f802348 100644
--- a/libcr_ipc/include/libcr_ipc/select.h
+++ b/libcr_ipc/include/libcr_ipc/select.h
@@ -32,31 +32,31 @@ struct cr_select_arg {
size_t val_siz;
};
-#define CR_SELECT_RECV(CH, VALP) ({ \
- /* The _valp indirection is to get the \
- * compiler to check that the types are \
- * compatible. */ \
- typeof((CH)->vals[0]) *_valp = VALP; \
- ((struct cr_select_arg){ \
- .op = _CR_SELECT_OP_RECV, \
- .ch = &((CH)->core), \
- .val_ptr = _valp, \
- .val_siz = sizeof((CH)->vals[0]), \
- }); \
- })
-#define CR_SELECT_SEND(CH, VAL) ({ \
- typeof((CH)->vals[0]) val_lvalue = VAL; \
- ((struct cr_select_arg){ \
- .op = _CR_SELECT_OP_SEND, \
- .ch = &((CH)->core), \
- .val_ptr = &val_lvalue;, \
- .val_siz = sizeof((CH)->vals[0]), \
- }); \
- })
-#define CR_SELECT_DEFAULT \
- ((struct cr_select_arg){ \
- .op = _CR_SELECT_OP_DEFAULT, \
- })
+#define CR_SELECT_RECV(CH, VALP) \
+ /* The _valp temporary variable is to get the compiler to check that \
+ * the types are compatible. */ \
+ ((struct cr_select_arg){ \
+ .op = _CR_SELECT_OP_RECV, \
+ .ch = &((CH)->core), \
+ .val_ptr = ({ typeof((CH)->vals[0]) *_valp = VALP; _valp; }), \
+ .val_siz = sizeof((CH)->vals[0]), \
+ })
+/* BUG: It's bogus that CR_SELECT_SEND takes VALP instead of VAL, but
+ * since we need an address, taking VAL would introduce uncomfortable
+ * questions about where VAL sits on the stack. */
+#define CR_SELECT_SEND(CH, VALP) \
+ /* The _valp temporary variable is to get the compiler to check that \
+ * the types are compatible. */ \
+ ((struct cr_select_arg){ \
+ .op = _CR_SELECT_OP_SEND, \
+ .ch = &((CH)->core), \
+ .val_ptr = ({ typeof((CH)->vals[0]) *_valp = VALP; _valp; }), \
+ .val_siz = sizeof((CH)->vals[0]), \
+ })
+#define CR_SELECT_DEFAULT \
+ ((struct cr_select_arg){ \
+ .op = _CR_SELECT_OP_DEFAULT, \
+ })
/* cr_select_v(arg_cnt, arg_vec) **********************************************/
diff --git a/libcr_ipc/tests/config.h b/libcr_ipc/tests/config.h
index b00508c..a648589 100644
--- a/libcr_ipc/tests/config.h
+++ b/libcr_ipc/tests/config.h
@@ -7,9 +7,9 @@
#ifndef _CONFIG_H_
#define _CONFIG_H_
-#define CONFIG_COROUTINE_STACK_SIZE_DEFAULT (4*1024)
+#define CONFIG_COROUTINE_STACK_SIZE_DEFAULT (8*1024)
#define CONFIG_COROUTINE_NAME_LEN 16
-#define CONFIG_COROUTINE_NUM 8
+#define CONFIG_COROUTINE_NUM 16
#define CONFIG_COROUTINE_MEASURE_STACK 1
#define CONFIG_COROUTINE_PROTECT_STACK 1
diff --git a/libcr_ipc/tests/test_select.c b/libcr_ipc/tests/test_select.c
new file mode 100644
index 0000000..07ddc09
--- /dev/null
+++ b/libcr_ipc/tests/test_select.c
@@ -0,0 +1,90 @@
+/* libcr_ipc/tests/test_select.c - Tests for <libcr_ipc/select.h>
+ *
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <libcr/coroutine.h>
+#include <libcr_ipc/select.h>
+
+#include "test.h"
+
+CR_CHAN_DECLARE(intchan, int)
+
+intchan_t ch[10] = {0};
+intchan_t fch = {0};
+
+COROUTINE cr_consumer(void *) {
+ cr_begin();
+
+ struct cr_select_arg args[11];
+
+ bool chdone[10] = {0};
+ int arg2ch[10];
+ for (;;) {
+ int ret_ch;
+ int i_arg = 0;
+ for (int i_ch = 0; i_ch < 10; i_ch++) {
+ if (!chdone[i_ch]) {
+ args[i_arg] = CR_SELECT_RECV(&ch[i_ch], &ret_ch);
+ arg2ch[i_arg] = i_ch;
+ i_arg++;
+ }
+ }
+ if (!i_arg)
+ break;
+ args[i_arg] = CR_SELECT_DEFAULT; /* check that default doesn't trigger */
+ test_assert(i_arg <= 10);
+ int ret_arg = cr_select_v(i_arg+1, args);
+ test_assert(ret_arg < i_arg);
+ test_assert(arg2ch[ret_arg] == ret_ch);
+ chdone[ret_ch] = true;
+ }
+ int ret_ch, ret_arg;
+ args[0] = CR_SELECT_RECV(&ch[0], &ret_ch);
+ args[1] = CR_SELECT_DEFAULT;
+ ret_arg = cr_select_v(2, args);
+ test_assert(ret_arg == 1);
+
+ int send = 567;
+ args[0] = CR_SELECT_SEND(&fch, &send);
+ args[1] = CR_SELECT_DEFAULT;
+ ret_arg = cr_select_v(2, args);
+ test_assert(ret_arg == 0);
+
+ send = 890;
+ args[0] = CR_SELECT_SEND(&fch, &send);
+ args[1] = CR_SELECT_DEFAULT;
+ ret_arg = cr_select_v(2, args);
+ test_assert(ret_arg == 1);
+
+ cr_end();
+}
+
+COROUTINE cr_producer(void *_n) {
+ int n = *(int *)_n;
+ cr_begin();
+
+ intchan_send(&ch[n], n);
+
+ cr_end();
+}
+
+COROUTINE cr_final(void *) {
+ cr_begin();
+
+ int ret = intchan_recv(&fch);
+ printf("ret=%d\n", ret);
+ test_assert(ret == 567);
+
+ cr_end();
+}
+
+int main() {
+ for (int i = 0; i < 10; i++)
+ coroutine_add("producer", cr_producer, &i);
+ coroutine_add("consumer", cr_consumer, NULL);
+ coroutine_add("final", cr_final, NULL);
+ coroutine_main();
+ return 0;
+}