@@ -17,6 +17,7 @@ use zyntax_compiler::{
1717 zrtl:: DynamicValue ,
1818 tiered_backend:: { TieredBackend , TieredConfig , TieredStatistics , OptimizationTier } ,
1919 lowering:: AstLowering , // For lower_program trait method
20+ runtime:: { Executor , Waker as RuntimeWaker } ,
2021} ;
2122
2223/// Result type for runtime operations
@@ -1598,16 +1599,38 @@ pub struct ZyntaxPromise {
15981599 state : Arc < Mutex < PromiseInner > > ,
15991600}
16001601
1602+ /// Poll result from async state machine
1603+ ///
1604+ /// This matches the Zyntax async ABI where poll functions return a discriminated union.
1605+ #[ repr( C , u8 ) ]
1606+ #[ derive( Clone ) ]
1607+ pub enum AsyncPollResult {
1608+ /// Still pending, needs more polls
1609+ Pending = 0 ,
1610+ /// Completed with a value (the DynamicValue)
1611+ Ready ( DynamicValue ) = 1 ,
1612+ /// Failed with an error message
1613+ Failed ( * const u8 , usize ) = 2 , // (ptr, len) for error string
1614+ }
1615+
16011616struct PromiseInner {
1602- /// Function pointer
1603- func_ptr : * const u8 ,
1617+ /// Function pointer for creating the state machine
1618+ init_fn : * const u8 ,
1619+ /// Poll function pointer (once state machine is created)
1620+ poll_fn : Option < * const u8 > ,
16041621 /// Arguments to pass
16051622 args : Vec < DynamicValue > ,
16061623 /// Current state
16071624 state : PromiseState ,
16081625 /// State machine pointer (for Zyntax async functions)
16091626 state_machine : Option < * mut u8 > ,
1610- /// Waker registration
1627+ /// Ready queue for waker integration
1628+ ready_queue : Arc < Mutex < std:: collections:: VecDeque < usize > > > ,
1629+ /// Task ID for waker
1630+ task_id : usize ,
1631+ /// Poll count for timeout detection
1632+ poll_count : usize ,
1633+ /// Waker for Rust Future integration
16111634 waker : Option < std:: task:: Waker > ,
16121635}
16131636
@@ -1626,15 +1649,23 @@ pub enum PromiseState {
16261649 Failed ( String ) ,
16271650}
16281651
1652+ /// Global task ID counter for promise wakers
1653+ static NEXT_TASK_ID : std:: sync:: atomic:: AtomicUsize = std:: sync:: atomic:: AtomicUsize :: new ( 0 ) ;
1654+
16291655impl ZyntaxPromise {
16301656 /// Create a new promise for an async function call
16311657 fn new ( func_ptr : * const u8 , args : Vec < DynamicValue > ) -> Self {
1658+ let task_id = NEXT_TASK_ID . fetch_add ( 1 , std:: sync:: atomic:: Ordering :: SeqCst ) ;
16321659 Self {
16331660 state : Arc :: new ( Mutex :: new ( PromiseInner {
1634- func_ptr,
1661+ init_fn : func_ptr,
1662+ poll_fn : None ,
16351663 args,
16361664 state : PromiseState :: Pending ,
16371665 state_machine : None ,
1666+ ready_queue : Arc :: new ( Mutex :: new ( std:: collections:: VecDeque :: new ( ) ) ) ,
1667+ task_id,
1668+ poll_count : 0 ,
16381669 waker : None ,
16391670 } ) ) ,
16401671 }
@@ -1643,6 +1674,14 @@ impl ZyntaxPromise {
16431674 /// Poll the promise for completion
16441675 ///
16451676 /// Returns the current state without blocking.
1677+ ///
1678+ /// # Async ABI
1679+ ///
1680+ /// Zyntax async functions follow this ABI:
1681+ /// 1. `init_fn(args...) -> *mut StateMachine` - Creates the state machine
1682+ /// 2. `poll_fn(state_machine: *mut u8, waker_data: *const u8) -> AsyncPollResult`
1683+ ///
1684+ /// The poll function advances the state machine until it yields or completes.
16461685 pub fn poll ( & self ) -> PromiseState {
16471686 let mut inner = self . state . lock ( ) . unwrap ( ) ;
16481687
@@ -1654,29 +1693,106 @@ impl ZyntaxPromise {
16541693 PromiseState :: Pending => { }
16551694 }
16561695
1696+ inner. poll_count += 1 ;
1697+
16571698 // Try to advance the state machine
16581699 if let Some ( state_machine) = inner. state_machine {
1659- unsafe {
1660- // Call the poll function on the state machine
1661- // The state machine follows Zyntax's async ABI:
1662- // poll(state_machine: *mut u8, waker: *const Waker) -> PollResult
1663- // where PollResult = { Pending = 0, Ready(value) = 1, Failed(error) = 2 }
1664-
1665- // For now, we simulate completion after first poll
1666- // A real implementation would call into the Zyntax runtime
1700+ if let Some ( poll_fn) = inner. poll_fn {
1701+ unsafe {
1702+ // Create a waker for this task
1703+ let waker = RuntimeWaker :: new ( inner. task_id , inner. ready_queue . clone ( ) ) ;
1704+ let std_waker = waker. into_std_waker ( ) ;
1705+ let waker_ptr = & std_waker as * const std:: task:: Waker as * const u8 ;
1706+
1707+ // Call the poll function on the state machine
1708+ // poll(state_machine: *mut u8, waker: *const u8) -> AsyncPollResult
1709+ let f: extern "C" fn ( * mut u8 , * const u8 ) -> AsyncPollResult =
1710+ std:: mem:: transmute ( poll_fn) ;
1711+
1712+ let result = f ( state_machine, waker_ptr) ;
1713+
1714+ match result {
1715+ AsyncPollResult :: Pending => {
1716+ // Still pending, state remains unchanged
1717+ }
1718+ AsyncPollResult :: Ready ( dv) => {
1719+ // Completed - convert DynamicValue to ZyntaxValue
1720+ match ZyntaxValue :: from_dynamic ( dv) {
1721+ Ok ( value) => inner. state = PromiseState :: Ready ( value) ,
1722+ Err ( e) => inner. state = PromiseState :: Failed ( e. to_string ( ) ) ,
1723+ }
1724+ }
1725+ AsyncPollResult :: Failed ( ptr, len) => {
1726+ // Extract error message from pointer
1727+ let err_msg = if !ptr. is_null ( ) && len > 0 {
1728+ let slice = std:: slice:: from_raw_parts ( ptr, len) ;
1729+ String :: from_utf8_lossy ( slice) . to_string ( )
1730+ } else {
1731+ "Unknown async error" . to_string ( )
1732+ } ;
1733+ inner. state = PromiseState :: Failed ( err_msg) ;
1734+ }
1735+ }
1736+ }
1737+ } else {
1738+ // No poll function available, mark as complete with void
1739+ // This handles sync functions wrapped as async
16671740 inner. state = PromiseState :: Ready ( ZyntaxValue :: Void ) ;
16681741 }
16691742 } else {
16701743 // Initialize the state machine on first poll
16711744 unsafe {
1672- let f: extern "C" fn ( ) -> * mut u8 = std:: mem:: transmute ( inner. func_ptr ) ;
1673- inner. state_machine = Some ( f ( ) ) ;
1745+ // The init function creates the state machine and optionally returns
1746+ // a poll function pointer. For simple async functions, both are the same.
1747+ //
1748+ // ABI: init_fn() -> (*mut StateMachine, *const PollFn)
1749+ // Or simplified: init_fn() -> *mut StateMachine (poll_fn same as init_fn)
1750+
1751+ if inner. init_fn . is_null ( ) {
1752+ inner. state = PromiseState :: Failed ( "Null async function pointer" . to_string ( ) ) ;
1753+ return inner. state . clone ( ) ;
1754+ }
1755+
1756+ // Simple ABI: init function returns the state machine pointer
1757+ // The poll function is inferred from the async function's structure
1758+ let f: extern "C" fn ( ) -> * mut u8 = std:: mem:: transmute ( inner. init_fn ) ;
1759+ let state_machine = f ( ) ;
1760+
1761+ if state_machine. is_null ( ) {
1762+ inner. state = PromiseState :: Failed ( "Failed to create async state machine" . to_string ( ) ) ;
1763+ return inner. state . clone ( ) ;
1764+ }
1765+
1766+ inner. state_machine = Some ( state_machine) ;
1767+
1768+ // For now, assume the poll function follows right after the state machine
1769+ // In a real implementation, this would be part of the async function's vtable
1770+ // or returned alongside the state machine pointer
1771+ inner. poll_fn = Some ( inner. init_fn ) ; // Use same function for polling
16741772 }
16751773 }
16761774
16771775 inner. state . clone ( )
16781776 }
16791777
1778+ /// Poll with a maximum number of iterations
1779+ ///
1780+ /// This is useful for avoiding infinite loops when the async function
1781+ /// might be stuck or taking too long.
1782+ pub fn poll_with_limit ( & self , max_polls : usize ) -> PromiseState {
1783+ let inner = self . state . lock ( ) . unwrap ( ) ;
1784+ if inner. poll_count >= max_polls {
1785+ drop ( inner) ;
1786+ let mut inner = self . state . lock ( ) . unwrap ( ) ;
1787+ inner. state = PromiseState :: Failed ( format ! (
1788+ "Async operation timed out after {} polls" , max_polls
1789+ ) ) ;
1790+ return inner. state . clone ( ) ;
1791+ }
1792+ drop ( inner) ;
1793+ self . poll ( )
1794+ }
1795+
16801796 /// Block until the promise completes and return the result
16811797 pub fn await_result < T : FromZyntax > ( & self ) -> RuntimeResult < T > {
16821798 loop {
@@ -1729,20 +1845,55 @@ impl ZyntaxPromise {
17291845 self . state . lock ( ) . unwrap ( ) . state . clone ( )
17301846 }
17311847
1848+ /// Block until the promise completes with a timeout
1849+ ///
1850+ /// Returns `Err` if the timeout is exceeded.
1851+ pub fn await_with_timeout ( & self , timeout : std:: time:: Duration ) -> RuntimeResult < ZyntaxValue > {
1852+ let start = std:: time:: Instant :: now ( ) ;
1853+ loop {
1854+ if start. elapsed ( ) > timeout {
1855+ return Err ( RuntimeError :: Promise ( format ! (
1856+ "Async operation timed out after {:?}" , timeout
1857+ ) ) ) ;
1858+ }
1859+ match self . poll ( ) {
1860+ PromiseState :: Pending => {
1861+ std:: thread:: yield_now ( ) ;
1862+ }
1863+ PromiseState :: Ready ( value) => {
1864+ return Ok ( value) ;
1865+ }
1866+ PromiseState :: Failed ( err) => {
1867+ return Err ( RuntimeError :: Promise ( err) ) ;
1868+ }
1869+ }
1870+ }
1871+ }
1872+
1873+ /// Get the number of times this promise has been polled
1874+ pub fn poll_count ( & self ) -> usize {
1875+ self . state . lock ( ) . unwrap ( ) . poll_count
1876+ }
1877+
17321878 /// Chain another operation to run when this promise completes
17331879 pub fn then < F > ( & self , f : F ) -> ZyntaxPromise
17341880 where
17351881 F : FnOnce ( ZyntaxValue ) -> ZyntaxValue + Send + ' static ,
17361882 {
17371883 let source = self . state . clone ( ) ;
1884+ let task_id = NEXT_TASK_ID . fetch_add ( 1 , std:: sync:: atomic:: Ordering :: SeqCst ) ;
17381885
17391886 // Create a new promise that depends on this one
17401887 let new_promise = ZyntaxPromise {
17411888 state : Arc :: new ( Mutex :: new ( PromiseInner {
1742- func_ptr : std:: ptr:: null ( ) ,
1889+ init_fn : std:: ptr:: null ( ) ,
1890+ poll_fn : None ,
17431891 args : vec ! [ ] ,
17441892 state : PromiseState :: Pending ,
17451893 state_machine : None ,
1894+ ready_queue : Arc :: new ( Mutex :: new ( std:: collections:: VecDeque :: new ( ) ) ) ,
1895+ task_id,
1896+ poll_count : 0 ,
17461897 waker : None ,
17471898 } ) ) ,
17481899 } ;
@@ -1779,13 +1930,18 @@ impl ZyntaxPromise {
17791930 F : FnOnce ( String ) -> ZyntaxValue + Send + ' static ,
17801931 {
17811932 let source = self . state . clone ( ) ;
1933+ let task_id = NEXT_TASK_ID . fetch_add ( 1 , std:: sync:: atomic:: Ordering :: SeqCst ) ;
17821934
17831935 let new_promise = ZyntaxPromise {
17841936 state : Arc :: new ( Mutex :: new ( PromiseInner {
1785- func_ptr : std:: ptr:: null ( ) ,
1937+ init_fn : std:: ptr:: null ( ) ,
1938+ poll_fn : None ,
17861939 args : vec ! [ ] ,
17871940 state : PromiseState :: Pending ,
17881941 state_machine : None ,
1942+ ready_queue : Arc :: new ( Mutex :: new ( std:: collections:: VecDeque :: new ( ) ) ) ,
1943+ task_id,
1944+ poll_count : 0 ,
17891945 waker : None ,
17901946 } ) ) ,
17911947 } ;
@@ -1923,10 +2079,14 @@ mod tests {
19232079 fn test_promise_state ( ) {
19242080 let promise = ZyntaxPromise {
19252081 state : Arc :: new ( Mutex :: new ( PromiseInner {
1926- func_ptr : std:: ptr:: null ( ) ,
2082+ init_fn : std:: ptr:: null ( ) ,
2083+ poll_fn : None ,
19272084 args : vec ! [ ] ,
19282085 state : PromiseState :: Ready ( ZyntaxValue :: Int ( 42 ) ) ,
19292086 state_machine : None ,
2087+ ready_queue : Arc :: new ( Mutex :: new ( std:: collections:: VecDeque :: new ( ) ) ) ,
2088+ task_id : 0 ,
2089+ poll_count : 0 ,
19302090 waker : None ,
19312091 } ) ) ,
19322092 } ;
@@ -1944,10 +2104,14 @@ mod tests {
19442104 fn test_promise_then ( ) {
19452105 let promise = ZyntaxPromise {
19462106 state : Arc :: new ( Mutex :: new ( PromiseInner {
1947- func_ptr : std:: ptr:: null ( ) ,
2107+ init_fn : std:: ptr:: null ( ) ,
2108+ poll_fn : None ,
19482109 args : vec ! [ ] ,
19492110 state : PromiseState :: Ready ( ZyntaxValue :: Int ( 10 ) ) ,
19502111 state_machine : None ,
2112+ ready_queue : Arc :: new ( Mutex :: new ( std:: collections:: VecDeque :: new ( ) ) ) ,
2113+ task_id : 0 ,
2114+ poll_count : 0 ,
19512115 waker : None ,
19522116 } ) ) ,
19532117 } ;
0 commit comments