Skip to content

Commit 1b89f45

Browse files
committed
feat: Add async/await support to C SDK (zrtl.h)
Add complete async support to match Rust SDK capabilities: - ZrtlAsyncState enum for state machine states - ZrtlStateMachineHeader struct for async state machines - ZrtlPromise struct with poll function pointer - Poll result macros (ZRTL_POLL_PENDING, ZRTL_POLL_IS_PENDING, etc.) - Helper functions: zrtl_promise_poll, zrtl_promise_block_on, zrtl_promise_free - State machine helpers: zrtl_state_is_finished, zrtl_state_advance, zrtl_state_complete - Promise combinators: ZrtlPromiseGroup, zrtl_promise_all_poll, zrtl_promise_race_poll
1 parent 3661c87 commit 1b89f45

1 file changed

Lines changed: 246 additions & 0 deletions

File tree

sdk/zrtl.h

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1044,6 +1044,252 @@ static inline int32_t zrtl_array_get_i32(ZrtlArrayConstPtr arr, int32_t index) {
10441044
return ZRTL_ARRAY_DATA(arr, int32_t)[index];
10451045
}
10461046

1047+
/* ============================================================
1048+
* Async/Await Support
1049+
* ============================================================
1050+
*
1051+
* This section provides the C ABI for async functions that can be
1052+
* called from Zyntax-based languages. Native async functions return
1053+
* a ZrtlPromise which can be polled until completion.
1054+
*
1055+
* # Promise ABI Convention
1056+
*
1057+
* Async functions return a pointer to ZrtlPromise:
1058+
* async fn foo(a: i32) -> i64 => ZrtlPromise* foo(int32_t a)
1059+
*
1060+
* The poll function returns an i64:
1061+
* - 0 = Pending (operation not complete, poll again)
1062+
* - positive = Ready(value) - operation completed successfully
1063+
* - negative = Failed(error_code) - operation failed
1064+
*
1065+
* # Usage Example (C)
1066+
*
1067+
* // Define async state machine
1068+
* typedef struct {
1069+
* ZrtlStateMachineHeader header;
1070+
* int32_t counter;
1071+
* int32_t target;
1072+
* } CounterState;
1073+
*
1074+
* // Poll function
1075+
* int64_t counter_poll(void* state) {
1076+
* CounterState* s = (CounterState*)state;
1077+
* if (s->counter >= s->target) {
1078+
* s->header.state = ZRTL_ASYNC_COMPLETED;
1079+
* return s->counter; // Ready(counter)
1080+
* }
1081+
* s->counter++;
1082+
* return 0; // Pending
1083+
* }
1084+
*
1085+
* // Create async function
1086+
* ZrtlPromise* async_count_to(int32_t target) {
1087+
* CounterState* state = malloc(sizeof(CounterState));
1088+
* state->header = ZRTL_STATE_MACHINE_INIT;
1089+
* state->counter = 0;
1090+
* state->target = target;
1091+
*
1092+
* ZrtlPromise* promise = malloc(sizeof(ZrtlPromise));
1093+
* promise->state_machine = state;
1094+
* promise->poll_fn = counter_poll;
1095+
* return promise;
1096+
* }
1097+
*
1098+
* // Await (blocking)
1099+
* int64_t result = zrtl_promise_block_on(async_count_to(100));
1100+
*/
1101+
1102+
/* Async state values */
1103+
typedef enum {
1104+
ZRTL_ASYNC_INITIAL = 0, /* Initial state, not started */
1105+
ZRTL_ASYNC_RESUME1 = 1, /* Resumed after first await */
1106+
ZRTL_ASYNC_RESUME2 = 2, /* Resumed after second await */
1107+
ZRTL_ASYNC_RESUME3 = 3,
1108+
ZRTL_ASYNC_RESUME4 = 4,
1109+
ZRTL_ASYNC_RESUME5 = 5,
1110+
ZRTL_ASYNC_RESUME6 = 6,
1111+
ZRTL_ASYNC_RESUME7 = 7,
1112+
ZRTL_ASYNC_COMPLETED = 0xFFFFFFFE, /* Completed successfully */
1113+
ZRTL_ASYNC_FAILED = 0xFFFFFFFF, /* Failed with error */
1114+
} ZrtlAsyncState;
1115+
1116+
/* State machine header - must be first field in async state structs */
1117+
typedef struct {
1118+
uint32_t state; /* Current async state (ZrtlAsyncState) */
1119+
uint32_t _reserved; /* Reserved for alignment */
1120+
} ZrtlStateMachineHeader;
1121+
1122+
/* Initial value for state machine header */
1123+
#define ZRTL_STATE_MACHINE_INIT { ZRTL_ASYNC_INITIAL, 0 }
1124+
1125+
/* Poll function signature: takes state pointer, returns i64 ABI result */
1126+
typedef int64_t (*ZrtlPollFn)(void* state_machine);
1127+
1128+
/* Promise structure - returned by async functions */
1129+
typedef struct {
1130+
void* state_machine; /* Pointer to the state machine */
1131+
ZrtlPollFn poll_fn; /* Poll function */
1132+
} ZrtlPromise;
1133+
1134+
/* Poll result values */
1135+
#define ZRTL_POLL_PENDING 0
1136+
1137+
/* Check if poll result is pending */
1138+
#define ZRTL_POLL_IS_PENDING(result) ((result) == 0)
1139+
1140+
/* Check if poll result is ready (positive = success) */
1141+
#define ZRTL_POLL_IS_READY(result) ((result) > 0)
1142+
1143+
/* Check if poll result is failed (negative = error) */
1144+
#define ZRTL_POLL_IS_FAILED(result) ((result) < 0)
1145+
1146+
/* Extract error code from failed result */
1147+
#define ZRTL_POLL_ERROR_CODE(result) ((int32_t)(result))
1148+
1149+
/* Poll a promise once */
1150+
static inline int64_t zrtl_promise_poll(ZrtlPromise* promise) {
1151+
if (!promise || !promise->poll_fn) return -1;
1152+
return promise->poll_fn(promise->state_machine);
1153+
}
1154+
1155+
/* Block until promise completes (busy-wait) */
1156+
static inline int64_t zrtl_promise_block_on(ZrtlPromise* promise) {
1157+
if (!promise) return -1;
1158+
int64_t result;
1159+
while ((result = zrtl_promise_poll(promise)) == ZRTL_POLL_PENDING) {
1160+
/* Spin - could add yield here for better CPU usage */
1161+
}
1162+
return result;
1163+
}
1164+
1165+
/* Free a promise and its state machine */
1166+
static inline void zrtl_promise_free(ZrtlPromise* promise) {
1167+
if (promise) {
1168+
if (promise->state_machine) {
1169+
zrtl_free(promise->state_machine);
1170+
}
1171+
zrtl_free(promise);
1172+
}
1173+
}
1174+
1175+
/* Check if state machine is finished */
1176+
static inline int zrtl_state_is_finished(const ZrtlStateMachineHeader* header) {
1177+
return header->state >= ZRTL_ASYNC_COMPLETED;
1178+
}
1179+
1180+
/* Advance state machine to next resume point */
1181+
static inline void zrtl_state_advance(ZrtlStateMachineHeader* header) {
1182+
if (header->state < ZRTL_ASYNC_COMPLETED) {
1183+
header->state++;
1184+
}
1185+
}
1186+
1187+
/* Mark state machine as completed */
1188+
static inline void zrtl_state_complete(ZrtlStateMachineHeader* header) {
1189+
header->state = ZRTL_ASYNC_COMPLETED;
1190+
}
1191+
1192+
/* Mark state machine as failed */
1193+
static inline void zrtl_state_fail(ZrtlStateMachineHeader* header) {
1194+
header->state = ZRTL_ASYNC_FAILED;
1195+
}
1196+
1197+
/* ============================================================
1198+
* Promise Combinators
1199+
* ============================================================
1200+
*
1201+
* These helpers allow combining multiple promises.
1202+
*
1203+
* - zrtl_promise_all: Wait for all promises to complete
1204+
* - zrtl_promise_race: Wait for first promise to complete
1205+
*/
1206+
1207+
/* Promise array for combinators */
1208+
typedef struct {
1209+
ZrtlPromise** promises; /* Array of promise pointers */
1210+
uint32_t count; /* Number of promises */
1211+
int64_t* results; /* Results array (for all) */
1212+
uint32_t completed; /* Number completed (for all) */
1213+
int32_t winner; /* Index of first completed (for race), -1 if none */
1214+
} ZrtlPromiseGroup;
1215+
1216+
/* Create a promise group for Promise.all */
1217+
static inline ZrtlPromiseGroup* zrtl_promise_group_new(uint32_t count) {
1218+
ZrtlPromiseGroup* group = (ZrtlPromiseGroup*)zrtl_alloc(sizeof(ZrtlPromiseGroup));
1219+
if (!group) return NULL;
1220+
1221+
group->promises = (ZrtlPromise**)zrtl_alloc(sizeof(ZrtlPromise*) * count);
1222+
group->results = (int64_t*)zrtl_alloc(sizeof(int64_t) * count);
1223+
group->count = count;
1224+
group->completed = 0;
1225+
group->winner = -1;
1226+
1227+
if (!group->promises || !group->results) {
1228+
if (group->promises) zrtl_free(group->promises);
1229+
if (group->results) zrtl_free(group->results);
1230+
zrtl_free(group);
1231+
return NULL;
1232+
}
1233+
1234+
for (uint32_t i = 0; i < count; i++) {
1235+
group->promises[i] = NULL;
1236+
group->results[i] = 0;
1237+
}
1238+
1239+
return group;
1240+
}
1241+
1242+
/* Add a promise to the group */
1243+
static inline void zrtl_promise_group_add(ZrtlPromiseGroup* group, uint32_t index, ZrtlPromise* promise) {
1244+
if (group && index < group->count) {
1245+
group->promises[index] = promise;
1246+
}
1247+
}
1248+
1249+
/* Poll all promises (Promise.all pattern) - returns 1 when all complete */
1250+
static inline int zrtl_promise_all_poll(ZrtlPromiseGroup* group) {
1251+
if (!group) return 1;
1252+
1253+
for (uint32_t i = 0; i < group->count; i++) {
1254+
if (group->results[i] == ZRTL_POLL_PENDING && group->promises[i]) {
1255+
int64_t result = zrtl_promise_poll(group->promises[i]);
1256+
if (result != ZRTL_POLL_PENDING) {
1257+
group->results[i] = result;
1258+
group->completed++;
1259+
}
1260+
}
1261+
}
1262+
1263+
return group->completed >= group->count;
1264+
}
1265+
1266+
/* Poll for first completion (Promise.race pattern) - returns winner index or -1 */
1267+
static inline int32_t zrtl_promise_race_poll(ZrtlPromiseGroup* group) {
1268+
if (!group || group->winner >= 0) return group ? group->winner : -1;
1269+
1270+
for (uint32_t i = 0; i < group->count; i++) {
1271+
if (group->promises[i]) {
1272+
int64_t result = zrtl_promise_poll(group->promises[i]);
1273+
if (result != ZRTL_POLL_PENDING) {
1274+
group->results[i] = result;
1275+
group->winner = (int32_t)i;
1276+
return group->winner;
1277+
}
1278+
}
1279+
}
1280+
1281+
return -1; /* None completed yet */
1282+
}
1283+
1284+
/* Free a promise group (does NOT free individual promises) */
1285+
static inline void zrtl_promise_group_free(ZrtlPromiseGroup* group) {
1286+
if (group) {
1287+
if (group->promises) zrtl_free(group->promises);
1288+
if (group->results) zrtl_free(group->results);
1289+
zrtl_free(group);
1290+
}
1291+
}
1292+
10471293
/* ============================================================
10481294
* Test Harness Macros
10491295
* ============================================================

0 commit comments

Comments
 (0)