1pub mod dynclib_abi;
36pub mod vtable;
37
38pub fn peer_status_from_v1(status: u32) -> Option<crate::WebRtcPeerStatus> {
42 use crate::guest::dynclib_abi::webrtc_peer_status as st;
43 match status {
44 st::IDLE => Some(crate::WebRtcPeerStatus::Idle),
45 st::CONNECTING => Some(crate::WebRtcPeerStatus::Connecting),
46 st::CONNECTED => Some(crate::WebRtcPeerStatus::Connected),
47 st::RECOVERING => Some(crate::WebRtcPeerStatus::Recovering),
48 other => {
49 tracing::warn!(
50 discriminant = other,
51 "unknown dynclib WebRtcPeerStatus discriminant; dropping peer status"
52 );
53 None
54 }
55 }
56}
57
58#[cfg(test)]
59mod tests {
60 use super::*;
61
62 #[test]
63 fn peer_status_from_v1_drops_unknown_discriminants() {
64 use crate::guest::dynclib_abi::webrtc_peer_status as st;
65
66 assert_eq!(
67 peer_status_from_v1(st::IDLE),
68 Some(crate::WebRtcPeerStatus::Idle)
69 );
70 assert_eq!(
71 peer_status_from_v1(st::CONNECTING),
72 Some(crate::WebRtcPeerStatus::Connecting)
73 );
74 assert_eq!(
75 peer_status_from_v1(st::CONNECTED),
76 Some(crate::WebRtcPeerStatus::Connected)
77 );
78 assert_eq!(
79 peer_status_from_v1(st::RECOVERING),
80 Some(crate::WebRtcPeerStatus::Recovering)
81 );
82 assert_eq!(peer_status_from_v1(u32::MAX), None);
83 }
84}
85
86#[cfg(all(target_arch = "wasm32", not(feature = "web")))]
91pub mod wasm;
92
93#[cfg(feature = "cdylib")]
94pub mod dynclib;
95
96#[cfg(all(target_arch = "wasm32", not(feature = "web")))]
100#[doc(hidden)]
101pub mod __wasm_macro_support {
102 pub use super::wasm::adapter::{
108 WorkloadCell, run_dispatch, run_on_credential_expiring, run_on_credential_renewed,
109 run_on_data_stream, run_on_error, run_on_mailbox_backpressure, run_on_ready,
110 run_on_signaling_connected, run_on_signaling_connecting, run_on_signaling_disconnected,
111 run_on_start, run_on_stop, run_on_webrtc_connected, run_on_webrtc_connecting,
112 run_on_webrtc_disconnected, run_on_websocket_connected, run_on_websocket_connecting,
113 run_on_websocket_disconnected,
114 };
115 pub use super::wasm::generated::actr::workload::types::{
116 ActrError as WitActrError, ActrId as WitActrId, BackpressureEvent as WitBackpressureEvent,
117 CredentialEvent as WitCredentialEvent, DataStream as WitDataStream,
118 ErrorEvent as WitErrorEvent, PeerEvent as WitPeerEvent, RpcEnvelope as WitRpcEnvelope,
119 };
120 pub use super::wasm::generated::exports::actr::workload::workload::Guest;
121}
122
123#[macro_export]
164macro_rules! entry {
165 ($workload_type:ty) => {
167 $crate::entry!($workload_type, <$workload_type as ::core::default::Default>::default());
168 };
169
170 ($workload_type:ty, $init_expr:expr) => {
172 #[cfg(all(target_arch = "wasm32", not(feature = "web")))]
183 const _: () = {
184 static __ACTR_WORKLOAD: $crate::guest::__wasm_macro_support::WorkloadCell<$workload_type> =
187 $crate::guest::__wasm_macro_support::WorkloadCell::new();
188
189 fn __actr_workload() -> &'static $workload_type {
190 __ACTR_WORKLOAD.get_or_init(|| -> $workload_type { $init_expr })
191 }
192
193 struct __ActrEntryAdapter;
194
195 impl $crate::guest::__wasm_macro_support::Guest for __ActrEntryAdapter {
196 async fn dispatch(
197 envelope: $crate::guest::__wasm_macro_support::WitRpcEnvelope,
198 ) -> ::core::result::Result<
199 ::std::vec::Vec<u8>,
200 $crate::guest::__wasm_macro_support::WitActrError,
201 > {
202 $crate::guest::__wasm_macro_support::run_dispatch(
203 __actr_workload(),
204 envelope,
205 )
206 .await
207 }
208
209 async fn on_start() -> ::core::result::Result<
210 (),
211 $crate::guest::__wasm_macro_support::WitActrError,
212 > {
213 $crate::guest::__wasm_macro_support::run_on_start(__actr_workload()).await
214 }
215
216 async fn on_ready() -> ::core::result::Result<
217 (),
218 $crate::guest::__wasm_macro_support::WitActrError,
219 > {
220 $crate::guest::__wasm_macro_support::run_on_ready(__actr_workload()).await
221 }
222
223 async fn on_stop() -> ::core::result::Result<
224 (),
225 $crate::guest::__wasm_macro_support::WitActrError,
226 > {
227 $crate::guest::__wasm_macro_support::run_on_stop(__actr_workload()).await
228 }
229
230 async fn on_error(
231 event: $crate::guest::__wasm_macro_support::WitErrorEvent,
232 ) -> ::core::result::Result<
233 (),
234 $crate::guest::__wasm_macro_support::WitActrError,
235 > {
236 $crate::guest::__wasm_macro_support::run_on_error(__actr_workload(), event)
237 .await
238 }
239
240 async fn on_signaling_connecting() {
241 $crate::guest::__wasm_macro_support::run_on_signaling_connecting(
242 __actr_workload(),
243 )
244 .await
245 }
246
247 async fn on_signaling_connected() {
248 $crate::guest::__wasm_macro_support::run_on_signaling_connected(
249 __actr_workload(),
250 )
251 .await
252 }
253
254 async fn on_signaling_disconnected() {
255 $crate::guest::__wasm_macro_support::run_on_signaling_disconnected(
256 __actr_workload(),
257 )
258 .await
259 }
260
261 async fn on_websocket_connecting(
262 event: $crate::guest::__wasm_macro_support::WitPeerEvent,
263 ) {
264 $crate::guest::__wasm_macro_support::run_on_websocket_connecting(
265 __actr_workload(),
266 event,
267 )
268 .await
269 }
270
271 async fn on_websocket_connected(
272 event: $crate::guest::__wasm_macro_support::WitPeerEvent,
273 ) {
274 $crate::guest::__wasm_macro_support::run_on_websocket_connected(
275 __actr_workload(),
276 event,
277 )
278 .await
279 }
280
281 async fn on_websocket_disconnected(
282 event: $crate::guest::__wasm_macro_support::WitPeerEvent,
283 ) {
284 $crate::guest::__wasm_macro_support::run_on_websocket_disconnected(
285 __actr_workload(),
286 event,
287 )
288 .await
289 }
290
291 async fn on_webrtc_connecting(
292 event: $crate::guest::__wasm_macro_support::WitPeerEvent,
293 ) {
294 $crate::guest::__wasm_macro_support::run_on_webrtc_connecting(
295 __actr_workload(),
296 event,
297 )
298 .await
299 }
300
301 async fn on_webrtc_connected(
302 event: $crate::guest::__wasm_macro_support::WitPeerEvent,
303 ) {
304 $crate::guest::__wasm_macro_support::run_on_webrtc_connected(
305 __actr_workload(),
306 event,
307 )
308 .await
309 }
310
311 async fn on_webrtc_disconnected(
312 event: $crate::guest::__wasm_macro_support::WitPeerEvent,
313 ) {
314 $crate::guest::__wasm_macro_support::run_on_webrtc_disconnected(
315 __actr_workload(),
316 event,
317 )
318 .await
319 }
320
321 async fn on_credential_renewed(
322 event: $crate::guest::__wasm_macro_support::WitCredentialEvent,
323 ) {
324 $crate::guest::__wasm_macro_support::run_on_credential_renewed(
325 __actr_workload(),
326 event,
327 )
328 .await
329 }
330
331 async fn on_credential_expiring(
332 event: $crate::guest::__wasm_macro_support::WitCredentialEvent,
333 ) {
334 $crate::guest::__wasm_macro_support::run_on_credential_expiring(
335 __actr_workload(),
336 event,
337 )
338 .await
339 }
340
341 async fn on_mailbox_backpressure(
342 event: $crate::guest::__wasm_macro_support::WitBackpressureEvent,
343 ) {
344 $crate::guest::__wasm_macro_support::run_on_mailbox_backpressure(
345 __actr_workload(),
346 event,
347 )
348 .await
349 }
350
351 async fn on_data_stream(
352 chunk: $crate::guest::__wasm_macro_support::WitDataStream,
353 sender: $crate::guest::__wasm_macro_support::WitActrId,
354 ) -> ::core::result::Result<
355 (),
356 $crate::guest::__wasm_macro_support::WitActrError,
357 > {
358 $crate::guest::__wasm_macro_support::run_on_data_stream(chunk, sender).await
359 }
360 }
361
362 $crate::guest::wasm::generated::export!(__ActrEntryAdapter with_types_in $crate::guest::wasm::generated);
363 };
364
365 #[cfg(all(target_arch = "wasm32", feature = "web"))]
375 const _: () = {
376 #[$crate::web::__web_macro_support::wasm_bindgen(start)]
386 fn __actr_web_bootstrap() {
387 let workload: $workload_type = $init_expr;
388 let adapter =
389 $crate::web::__web_macro_support::WebWorkloadAdapter::new(workload);
390 $crate::web::__web_macro_support::register_workload(adapter);
391 }
392 };
393
394 #[cfg(feature = "cdylib")]
399 const _: () = {
400 static mut __ACTR_WORKLOAD: Option<$workload_type> = None;
401 static mut __ACTR_VTABLE: Option<*const $crate::guest::vtable::HostVTable> = None;
402
403 #[unsafe(no_mangle)]
409 pub unsafe extern "C" fn actr_init(
410 vtable: *const $crate::guest::vtable::HostVTable,
411 init_ptr: *const u8,
412 init_len: usize,
413 ) -> i32 {
414 if vtable.is_null() {
415 return $crate::guest::dynclib_abi::code::INIT_FAILED;
416 }
417
418 let init_bytes = if init_ptr.is_null() || init_len == 0 {
419 &[][..]
420 } else {
421 unsafe { std::slice::from_raw_parts(init_ptr, init_len) }
422 };
423
424 if $crate::guest::dynclib_abi::decode_message::<$crate::guest::dynclib_abi::InitPayloadV1>(
429 init_bytes,
430 )
431 .is_err()
432 {
433 return $crate::guest::dynclib_abi::code::PROTOCOL_ERROR;
434 }
435
436 let workload: $workload_type = $init_expr;
437 unsafe {
438 if __ACTR_WORKLOAD.is_some() {
439 return $crate::guest::dynclib_abi::code::INIT_FAILED;
440 }
441 __ACTR_VTABLE = Some(vtable);
442 __ACTR_WORKLOAD = Some(workload);
443 }
444 $crate::guest::dynclib_abi::code::SUCCESS
445 }
446
447 #[unsafe(no_mangle)]
449 pub unsafe extern "C" fn actr_handle(
450 req_ptr: *const u8,
451 req_len: usize,
452 resp_out: *mut *mut u8,
453 resp_len_out: *mut usize,
454 ) -> i32 {
455 use actr_protocol::prost::Message as ProstMessage;
456 use $crate::{MessageDispatcher, Workload};
457
458 let vtable = match unsafe { __ACTR_VTABLE } {
460 Some(vt) => vt,
461 None => return $crate::guest::dynclib_abi::code::INIT_FAILED,
462 };
463
464 if req_ptr.is_null() {
466 return $crate::guest::dynclib_abi::code::PROTOCOL_ERROR;
467 }
468 let req_bytes = unsafe { std::slice::from_raw_parts(req_ptr, req_len) };
469
470 let frame = match $crate::guest::dynclib_abi::decode_message::<
471 $crate::guest::dynclib_abi::AbiFrame,
472 >(req_bytes) {
473 Ok(f) => f,
474 Err(_) => return $crate::guest::dynclib_abi::code::PROTOCOL_ERROR,
475 };
476
477 if frame.op == $crate::guest::dynclib_abi::op::GUEST_LIFECYCLE {
478 let payload = match <$crate::guest::dynclib_abi::GuestLifecycleV1 as $crate::guest::dynclib_abi::AbiPayload>::decode_payload(&frame.payload) {
479 Ok(payload) => payload,
480 Err(_) => return $crate::guest::dynclib_abi::code::PROTOCOL_ERROR,
481 };
482
483 let ctx = match unsafe {
484 $crate::guest::dynclib::context::DynclibContext::from_invocation(vtable, payload.ctx)
485 } {
486 Ok(c) => c,
487 Err(_) => return $crate::guest::dynclib_abi::code::HANDLE_FAILED,
488 };
489
490 let workload = unsafe {
491 match __ACTR_WORKLOAD.as_ref() {
492 Some(w) => w,
493 None => return $crate::guest::dynclib_abi::code::INIT_FAILED,
494 }
495 };
496
497 let lifecycle_result = match payload.hook {
498 $crate::guest::dynclib_abi::lifecycle_hook::ON_START => {
499 let fut = workload.on_start(&ctx);
500 let waker = std::task::Waker::noop();
501 let mut cx = std::task::Context::from_waker(waker);
502 let mut pinned = std::pin::pin!(fut);
503 match pinned.as_mut().poll(&mut cx) {
504 std::task::Poll::Ready(v) => v,
505 std::task::Poll::Pending => {
506 return $crate::guest::dynclib_abi::code::HANDLE_FAILED;
507 }
508 }
509 }
510 $crate::guest::dynclib_abi::lifecycle_hook::ON_READY => {
511 let fut = workload.on_ready(&ctx);
512 let waker = std::task::Waker::noop();
513 let mut cx = std::task::Context::from_waker(waker);
514 let mut pinned = std::pin::pin!(fut);
515 match pinned.as_mut().poll(&mut cx) {
516 std::task::Poll::Ready(v) => v,
517 std::task::Poll::Pending => {
518 return $crate::guest::dynclib_abi::code::HANDLE_FAILED;
519 }
520 }
521 }
522 $crate::guest::dynclib_abi::lifecycle_hook::ON_STOP => {
523 let fut = workload.on_stop(&ctx);
524 let waker = std::task::Waker::noop();
525 let mut cx = std::task::Context::from_waker(waker);
526 let mut pinned = std::pin::pin!(fut);
527 match pinned.as_mut().poll(&mut cx) {
528 std::task::Poll::Ready(v) => v,
529 std::task::Poll::Pending => {
530 return $crate::guest::dynclib_abi::code::HANDLE_FAILED;
531 }
532 }
533 }
534 _ => return $crate::guest::dynclib_abi::code::UNSUPPORTED_OP,
535 };
536
537 let resp_bytes = match lifecycle_result {
538 Ok(()) => match $crate::guest::dynclib_abi::success_reply(::std::vec::Vec::new()) {
539 Ok(bytes) => bytes,
540 Err(code) => return code,
541 },
542 Err(err) => match $crate::guest::dynclib_abi::error_reply(
543 $crate::guest::dynclib_abi::code::HANDLE_FAILED,
544 err.to_string().into_bytes(),
545 ) {
546 Ok(bytes) => bytes,
547 Err(code) => return code,
548 },
549 };
550
551 let resp_len = resp_bytes.len();
552 let layout = match std::alloc::Layout::from_size_align(resp_len.max(1), 1) {
553 Ok(l) => l,
554 Err(_) => return $crate::guest::dynclib_abi::code::GENERIC_ERROR,
555 };
556 let ptr = unsafe { std::alloc::alloc(layout) };
557 if ptr.is_null() {
558 return $crate::guest::dynclib_abi::code::GENERIC_ERROR;
559 }
560
561 unsafe {
562 std::ptr::copy_nonoverlapping(resp_bytes.as_ptr(), ptr, resp_len);
563 *resp_out = ptr;
564 *resp_len_out = resp_len;
565 }
566
567 return $crate::guest::dynclib_abi::code::SUCCESS;
568 }
569
570 if frame.op == $crate::guest::dynclib_abi::op::GUEST_HOOK {
571 let payload = match <$crate::guest::dynclib_abi::GuestHookV1 as $crate::guest::dynclib_abi::AbiPayload>::decode_payload(&frame.payload) {
572 Ok(payload) => payload,
573 Err(_) => return $crate::guest::dynclib_abi::code::PROTOCOL_ERROR,
574 };
575
576 let ctx = match unsafe {
577 $crate::guest::dynclib::context::DynclibContext::from_invocation(vtable, payload.ctx)
578 } {
579 Ok(c) => c,
580 Err(_) => return $crate::guest::dynclib_abi::code::HANDLE_FAILED,
581 };
582
583 let workload = unsafe {
584 match __ACTR_WORKLOAD.as_ref() {
585 Some(w) => w,
586 None => return $crate::guest::dynclib_abi::code::INIT_FAILED,
587 }
588 };
589
590 macro_rules! __actr_poll_unit {
591 ($future:expr) => {{
592 let fut = $future;
593 let waker = std::task::Waker::noop();
594 let mut cx = std::task::Context::from_waker(waker);
595 let mut pinned = std::pin::pin!(fut);
596 match pinned.as_mut().poll(&mut cx) {
597 std::task::Poll::Ready(()) => {}
598 std::task::Poll::Pending => {
599 return $crate::guest::dynclib_abi::code::HANDLE_FAILED;
600 }
601 }
602 }};
603 }
604
605 let peer_event = |peer: $crate::guest::dynclib_abi::PeerEventV1| {
606 $crate::PeerEvent {
607 peer: peer.peer,
608 relayed: peer.relayed,
609 status: peer
610 .status
611 .and_then($crate::guest::peer_status_from_v1),
612 }
613 };
614
615 let timestamp = |ts: $crate::guest::dynclib_abi::TimestampV1| {
616 std::time::UNIX_EPOCH
617 + std::time::Duration::new(ts.seconds, ts.nanoseconds)
618 };
619
620 match payload.hook {
621 $crate::guest::dynclib_abi::runtime_hook::ON_SIGNALING_CONNECTING => {
622 __actr_poll_unit!(workload.on_signaling_connecting(Some(&ctx)));
623 }
624 $crate::guest::dynclib_abi::runtime_hook::ON_SIGNALING_CONNECTED => {
625 __actr_poll_unit!(workload.on_signaling_connected(Some(&ctx)));
626 }
627 $crate::guest::dynclib_abi::runtime_hook::ON_SIGNALING_DISCONNECTED => {
628 __actr_poll_unit!(workload.on_signaling_disconnected(&ctx));
629 }
630 $crate::guest::dynclib_abi::runtime_hook::ON_WEBSOCKET_CONNECTING => {
631 let event = match payload.peer {
632 Some(peer) => peer_event(peer),
633 None => return $crate::guest::dynclib_abi::code::PROTOCOL_ERROR,
634 };
635 __actr_poll_unit!(workload.on_websocket_connecting(&ctx, &event));
636 }
637 $crate::guest::dynclib_abi::runtime_hook::ON_WEBSOCKET_CONNECTED => {
638 let event = match payload.peer {
639 Some(peer) => peer_event(peer),
640 None => return $crate::guest::dynclib_abi::code::PROTOCOL_ERROR,
641 };
642 __actr_poll_unit!(workload.on_websocket_connected(&ctx, &event));
643 }
644 $crate::guest::dynclib_abi::runtime_hook::ON_WEBSOCKET_DISCONNECTED => {
645 let event = match payload.peer {
646 Some(peer) => peer_event(peer),
647 None => return $crate::guest::dynclib_abi::code::PROTOCOL_ERROR,
648 };
649 __actr_poll_unit!(workload.on_websocket_disconnected(&ctx, &event));
650 }
651 $crate::guest::dynclib_abi::runtime_hook::ON_WEBRTC_CONNECTING => {
652 let event = match payload.peer {
653 Some(peer) => peer_event(peer),
654 None => return $crate::guest::dynclib_abi::code::PROTOCOL_ERROR,
655 };
656 __actr_poll_unit!(workload.on_webrtc_connecting(&ctx, &event));
657 }
658 $crate::guest::dynclib_abi::runtime_hook::ON_WEBRTC_CONNECTED => {
659 let event = match payload.peer {
660 Some(peer) => peer_event(peer),
661 None => return $crate::guest::dynclib_abi::code::PROTOCOL_ERROR,
662 };
663 __actr_poll_unit!(workload.on_webrtc_connected(&ctx, &event));
664 }
665 $crate::guest::dynclib_abi::runtime_hook::ON_WEBRTC_DISCONNECTED => {
666 let event = match payload.peer {
667 Some(peer) => peer_event(peer),
668 None => return $crate::guest::dynclib_abi::code::PROTOCOL_ERROR,
669 };
670 __actr_poll_unit!(workload.on_webrtc_disconnected(&ctx, &event));
671 }
672 $crate::guest::dynclib_abi::runtime_hook::ON_CREDENTIAL_RENEWED => {
673 let event = match payload.credential {
674 Some(credential) => $crate::CredentialEvent {
675 new_expiry: timestamp(credential.new_expiry),
676 },
677 None => return $crate::guest::dynclib_abi::code::PROTOCOL_ERROR,
678 };
679 __actr_poll_unit!(workload.on_credential_renewed(&ctx, &event));
680 }
681 $crate::guest::dynclib_abi::runtime_hook::ON_CREDENTIAL_EXPIRING => {
682 let event = match payload.credential {
683 Some(credential) => $crate::CredentialEvent {
684 new_expiry: timestamp(credential.new_expiry),
685 },
686 None => return $crate::guest::dynclib_abi::code::PROTOCOL_ERROR,
687 };
688 __actr_poll_unit!(workload.on_credential_expiring(&ctx, &event));
689 }
690 $crate::guest::dynclib_abi::runtime_hook::ON_MAILBOX_BACKPRESSURE => {
691 let event = match payload.backpressure {
692 Some(backpressure) => $crate::BackpressureEvent {
693 queue_len: backpressure.queue_len as usize,
694 threshold: backpressure.threshold as usize,
695 },
696 None => return $crate::guest::dynclib_abi::code::PROTOCOL_ERROR,
697 };
698 __actr_poll_unit!(workload.on_mailbox_backpressure(&ctx, &event));
699 }
700 _ => return $crate::guest::dynclib_abi::code::UNSUPPORTED_OP,
701 }
702
703 let resp_bytes = match $crate::guest::dynclib_abi::success_reply(::std::vec::Vec::new()) {
704 Ok(bytes) => bytes,
705 Err(code) => return code,
706 };
707
708 let resp_len = resp_bytes.len();
709 let layout = match std::alloc::Layout::from_size_align(resp_len.max(1), 1) {
710 Ok(l) => l,
711 Err(_) => return $crate::guest::dynclib_abi::code::GENERIC_ERROR,
712 };
713 let ptr = unsafe { std::alloc::alloc(layout) };
714 if ptr.is_null() {
715 return $crate::guest::dynclib_abi::code::GENERIC_ERROR;
716 }
717
718 unsafe {
719 std::ptr::copy_nonoverlapping(resp_bytes.as_ptr(), ptr, resp_len);
720 *resp_out = ptr;
721 *resp_len_out = resp_len;
722 }
723
724 return $crate::guest::dynclib_abi::code::SUCCESS;
725 }
726
727 if frame.op == $crate::guest::dynclib_abi::op::GUEST_DATA_STREAM {
728 let payload = match <$crate::guest::dynclib_abi::GuestDataStreamV1 as $crate::guest::dynclib_abi::AbiPayload>::decode_payload(&frame.payload) {
729 Ok(payload) => payload,
730 Err(_) => return $crate::guest::dynclib_abi::code::PROTOCOL_ERROR,
731 };
732
733 let resp_bytes = match $crate::guest::dynclib::context::dispatch_registered_stream(payload) {
734 Ok(()) => match $crate::guest::dynclib_abi::success_reply(::std::vec::Vec::new()) {
735 Ok(bytes) => bytes,
736 Err(code) => return code,
737 },
738 Err(err) => match $crate::guest::dynclib_abi::error_reply(
739 $crate::guest::dynclib_abi::code::HANDLE_FAILED,
740 err.to_string().into_bytes(),
741 ) {
742 Ok(bytes) => bytes,
743 Err(code) => return code,
744 },
745 };
746
747 let resp_len = resp_bytes.len();
748 let layout = match std::alloc::Layout::from_size_align(resp_len.max(1), 1) {
749 Ok(l) => l,
750 Err(_) => return $crate::guest::dynclib_abi::code::GENERIC_ERROR,
751 };
752 let ptr = unsafe { std::alloc::alloc(layout) };
753 if ptr.is_null() {
754 return $crate::guest::dynclib_abi::code::GENERIC_ERROR;
755 }
756
757 unsafe {
758 std::ptr::copy_nonoverlapping(resp_bytes.as_ptr(), ptr, resp_len);
759 *resp_out = ptr;
760 *resp_len_out = resp_len;
761 }
762
763 return $crate::guest::dynclib_abi::code::SUCCESS;
764 }
765
766 if frame.op != $crate::guest::dynclib_abi::op::GUEST_HANDLE {
767 return $crate::guest::dynclib_abi::code::UNSUPPORTED_OP;
768 }
769
770 let handle = match <$crate::guest::dynclib_abi::GuestHandleV1 as $crate::guest::dynclib_abi::AbiPayload>::decode_payload(&frame.payload) {
771 Ok(handle) => handle,
772 Err(_) => return $crate::guest::dynclib_abi::code::PROTOCOL_ERROR,
773 };
774
775 let envelope = match actr_protocol::RpcEnvelope::decode(handle.rpc_envelope.as_slice()) {
776 Ok(e) => e,
777 Err(_) => return $crate::guest::dynclib_abi::code::PROTOCOL_ERROR,
778 };
779
780 let ctx = match unsafe {
781 $crate::guest::dynclib::context::DynclibContext::from_invocation(vtable, handle.ctx)
782 } {
783 Ok(c) => c,
784 Err(_) => return $crate::guest::dynclib_abi::code::HANDLE_FAILED,
785 };
786
787 let workload = unsafe {
789 match __ACTR_WORKLOAD.as_ref() {
790 Some(w) => w,
791 None => return $crate::guest::dynclib_abi::code::INIT_FAILED,
792 }
793 };
794
795 type Dispatcher = <$workload_type as Workload>::Dispatcher;
797
798 let resp_result = {
803 let fut = Dispatcher::dispatch(workload, envelope, &ctx);
804 let waker = std::task::Waker::noop();
805 let mut cx = std::task::Context::from_waker(waker);
806 let mut pinned = std::pin::pin!(fut);
807 match pinned.as_mut().poll(&mut cx) {
808 std::task::Poll::Ready(v) => v,
809 std::task::Poll::Pending => {
810 return $crate::guest::dynclib_abi::code::HANDLE_FAILED;
811 }
812 }
813 };
814
815 let resp_bytes = match resp_result {
816 Ok(b) => match $crate::guest::dynclib_abi::success_reply(b.to_vec()) {
817 Ok(bytes) => bytes,
818 Err(code) => return code,
819 },
820 Err(err) => match $crate::guest::dynclib_abi::error_reply(
821 $crate::guest::dynclib_abi::code::HANDLE_FAILED,
822 err.to_string().into_bytes(),
823 ) {
824 Ok(bytes) => bytes,
825 Err(code) => return code,
826 },
827 };
828
829 let resp_len = resp_bytes.len();
831 let layout = match std::alloc::Layout::from_size_align(resp_len.max(1), 1) {
832 Ok(l) => l,
833 Err(_) => return $crate::guest::dynclib_abi::code::GENERIC_ERROR,
834 };
835 let ptr = unsafe { std::alloc::alloc(layout) };
836 if ptr.is_null() {
837 return $crate::guest::dynclib_abi::code::GENERIC_ERROR;
838 }
839
840 unsafe {
841 std::ptr::copy_nonoverlapping(resp_bytes.as_ptr(), ptr, resp_len);
842 *resp_out = ptr;
843 *resp_len_out = resp_len;
844 }
845
846 $crate::guest::dynclib_abi::code::SUCCESS
847 }
848
849 #[unsafe(no_mangle)]
853 pub unsafe extern "C" fn actr_free_response(ptr: *mut u8, len: usize) {
854 if ptr.is_null() || len == 0 {
855 return;
856 }
857 let layout = match std::alloc::Layout::from_size_align(len, 1) {
858 Ok(l) => l,
859 Err(_) => return,
860 };
861 unsafe { std::alloc::dealloc(ptr, layout) };
862 }
863 };
864 };
865}