1818#include <stdio.h>
1919#include <string.h>
2020#include <stdlib.h>
21+ #if defined(_WIN32 ) && defined(_MSC_VER )
22+ # include <windows.h>
23+ # define test_sleep_ms (ms ) Sleep((DWORD)(ms))
24+ #else
25+ # include <time.h>
26+ static void test_sleep_ms (long long ms )
27+ {
28+ struct timespec ts ;
29+ ts .tv_sec = (time_t )(ms / 1000 );
30+ ts .tv_nsec = (long )((ms % 1000 ) * 1000000L );
31+ nanosleep (& ts , NULL );
32+ }
33+ #endif
2134
2235static int failures = 0 ;
2336
@@ -547,6 +560,23 @@ static void test_shutdown_agents_self_call_throws(void)
547560 mino_state_free (S );
548561}
549562
563+ /* Sleep for N milliseconds inside an action, so an embed-side test
564+ * can block an agent's in-flight long enough to make await_for
565+ * actually time out. Registered in env as `test-sleep`. */
566+ static mino_val_t * prim_test_sleep (mino_state_t * S , mino_val_t * args ,
567+ mino_env_t * env )
568+ {
569+ long long ms = 0 ;
570+ (void )env ;
571+ if (args != NULL && args -> type == MINO_CONS
572+ && args -> as .cons .car != NULL
573+ && args -> as .cons .car -> type == MINO_INT ) {
574+ ms = args -> as .cons .car -> as .i ;
575+ }
576+ test_sleep_ms (ms );
577+ return mino_nil (S );
578+ }
579+
550580/* Drive every public C-API agent entry from C. Validates that the
551581 * mino_send / mino_send_off / mino_await / mino_await_for /
552582 * mino_agent_error / mino_restart_agent perimeter is wired up the
@@ -592,13 +622,8 @@ static void test_c_api_agents(void)
592622 "agent should have value 101 after one inc send-off" );
593623 }
594624
595- /* mino_await_for timeout: queue an action that sleeps longer
596- * than the timeout. Build a fn that calls (Thread/sleep 200);
597- * mino doesn't expose that directly, but we can simulate via a
598- * busy spin -- actually simpler: just check the no-op timeout
599- * shape with an agent that has zero in-flight, where it
600- * immediately succeeds. The blocking-timeout case is exercised
601- * by the Clojure-level await-for tests. */
625+ /* mino_await_for trivial path: zero in-flight means it returns 1
626+ * immediately without blocking. */
602627 {
603628 mino_val_t * a = mino_agent (S , mino_int (S , 0 ));
604629 mino_val_t * agents [2 ];
@@ -607,6 +632,37 @@ static void test_c_api_agents(void)
607632 "mino_await_for should return 1 when nothing is queued" );
608633 }
609634
635+ /* mino_await_for fires the timeout: register a C-side test-sleep
636+ * primitive, queue an action that sleeps 250ms, then call
637+ * mino_await_for with a 50ms deadline. The first call must time
638+ * out (return 0). A subsequent mino_await with no deadline must
639+ * drain. Asserts both sides of the perimeter -- the timed wait
640+ * actually waits, and the cv broadcast on action completion
641+ * wakes the next await. */
642+ {
643+ mino_val_t * a = mino_agent (S , mino_int (S , 0 ));
644+ mino_val_t * agents [2 ];
645+ mino_val_t * slow_fn ;
646+ mino_register_fn (S , env , "test-sleep" , prim_test_sleep );
647+ slow_fn = mino_eval_string (S ,
648+ "(fn [v] (test-sleep 250) (inc v))" , env );
649+ REQUIRE (slow_fn != NULL , "slow_fn should compile" );
650+ REQUIRE (mino_send (S , a , slow_fn , NULL ) != NULL ,
651+ "mino_send of slow action should enqueue" );
652+ agents [0 ] = a ; agents [1 ] = NULL ;
653+ REQUIRE (mino_await_for (S , 50 , agents ) == 0 ,
654+ "mino_await_for should return 0 when the deadline "
655+ "fires before the action completes" );
656+ /* Action is still running on the worker; await with no
657+ * deadline drains it. */
658+ REQUIRE (mino_await (S , agents ) != NULL ,
659+ "mino_await must drain the action started above" );
660+ REQUIRE (a -> as .agent .val != NULL && a -> as .agent .val -> type == MINO_INT ,
661+ "agent value should be int after the slow action runs" );
662+ REQUIRE (a -> as .agent .val -> as .i == 1 ,
663+ "agent value should be 1 after one slow inc action" );
664+ }
665+
610666 /* mino_agent_error / mino_restart_agent: install a validator
611667 * that rejects negatives, drive the agent into the failed
612668 * state, then restart with a clean value. */
0 commit comments