harmos_runtime/operations/
invocation.rs1use std::any::Any;
19use std::sync::{Arc, Mutex};
20
21use super::HostContext;
24use harmos_domain::{MemorySize, Message, MessageError, OperationDeclaration};
25use harmos_sdk::__private::ErasedResponse;
26use harmos_sdk::operations::{CallFuture, Caller, InvocationError, Target};
27
28macro_rules! native_payload {
33 ($ty:ty) => {
34 impl Message for $ty {
35 const CODEC: &'static str = "harmos.native";
36 fn encode(&self) -> Vec<u8> {
37 panic!("native payload cannot be encoded")
38 }
39 fn encoded_size(&self) -> usize {
40 0
41 }
42 fn decode(_: &[u8]) -> Result<Self, MessageError> {
43 Err(MessageError::borrowed("native payload cannot be decoded"))
44 }
45 fn decoding_memory(_: &[u8]) -> Result<usize, MessageError> {
46 Err(MessageError::borrowed("native payload cannot be decoded"))
47 }
48 }
49 impl harmos_domain::schema::Described for $ty {
50 const NAME: &'static str = "harmos::native";
51 const FIELDS: &'static [harmos_domain::schema::declaration::Field] = &[];
52 const MARKER: &'static [u8] = &[];
53 }
54 };
55}
56native_payload!(Request);
57native_payload!(Answer);
58
59struct Request {
64 payload: Mutex<Option<Box<dyn Any + Send>>>,
65 memory: usize,
66}
67
68struct Answer(ErasedResponse);
69struct Invocation;
70
71impl Caller for HostContext {
78 fn call(&self, target: Target, request: Box<dyn Any + Send>) -> CallFuture<'_> {
79 Box::pin(async move {
80 let result = async {
81 let context = self.borrow();
82 let origin = context.source.check()?;
83 let entry = context.operations.entry_as(target.id, &origin)?;
84 if entry.declared().fingerprint != (target.definition)().fingerprint {
85 return Err(crate::Error::InvalidInput {
86 field: "declared operation",
87 reason: "callee fingerprint differs",
88 });
89 }
90 let Some(factory) = entry.binding.downcast_ref::<harmos_sdk::operations::OperationFactory>().cloned() else {
91 let payload = (target.encode)(request.as_ref()).ok_or(crate::Error::InvalidInput {
92 field: "declared operation",
93 reason: "callee payload differs",
94 })?;
95 let bytes = context.call_encoded(target.id, payload).await?;
96 return (target.decode)(&bytes).map_err(|source| crate::Error::OperationFailed {
97 operation: target.id.to_owned(),
98 source: Arc::new(source),
99 });
100 };
101 if !factory.accepts_target(&target) {
102 return Err(crate::Error::InvalidInput {
103 field: "declared operation",
104 reason: "callee payload types differ",
105 });
106 }
107 let grant = (target.grant)(request.as_ref());
108 let key = (target.key)(request.as_ref());
109 let memory = (target.memory)(request.as_ref());
110 let binding = super::authored::factory_projected::<Invocation, ()>(
111 &context.operations,
112 move |request, ()| {
113 let payload = request
114 .payload
115 .into_inner()
116 .map_err(|_| harmos_sdk::ServingError::Catalog)?
117 .ok_or(harmos_sdk::ServingError::Catalog)?;
118 factory.prepare_native(payload, Box::new(()))
119 },
120 |response| Some(harmos_sdk::Response::new(Answer(response))),
121 |response| response.0.clone(),
122 );
123 let mut input = super::binding::Request::from(Request {
124 payload: Mutex::new(Some(request)),
125 memory,
126 });
127 input.binding = Some(Arc::new(binding));
128 input.grant = grant;
129 input.key = key;
130 let running = context.operations.admit_at::<Invocation, ()>(
131 target.id,
132 (input, ()),
133 Some(&context.scope),
134 context.admission.held(),
135 &context.source,
136 None,
137 )?;
138 let response = running.await?;
139 Ok(response.0.clone())
140 }
141 .await;
142 result.map_err(|error: crate::Error| InvocationError::failed(target.id, error))
143 })
144 }
145}
146
147impl OperationDeclaration for Invocation {
148 type Request = Request;
149 type Response = Answer;
150 type Input = harmos_domain::operations::NoStream;
151 type Output = harmos_domain::operations::NoStream;
152 const ID: &'static str = "native.invocation";
153 const POLICY: harmos_domain::OperationPolicy = harmos_operations::host::POLICY;
154}
155
156impl MemorySize for Request {
157 fn memory_size(&self) -> usize {
158 self.memory
159 }
160}
161impl MemorySize for Answer {
162 fn memory_size(&self) -> usize {
163 std::mem::size_of::<Self>()
164 }
165}
166
167#[cfg(test)]
174mod tests {
175 use super::*;
176 use crate::RegistrationExt;
177 use harmos_sdk::{Context, Operation, Response};
178
179 #[crate::lanes]
180 enum Lanes {
181 #[default]
182 Open,
183 }
184 struct Model;
185 impl crate::Application for Model {
186 type Lanes = Lanes;
187 type State = ();
188 type Transaction = crate::state::Never;
189 type Record = ();
190 type Streams = ();
191 type Resources = ();
192 }
193
194 const POLICY: crate::OperationPolicy = crate::OperationPolicy {
195 timeout: std::time::Duration::from_secs(2),
196 attempt_timeout: std::time::Duration::from_secs(2),
197 retries: crate::RetryPolicy::Never,
198 };
199
200 #[crate::operation(harmos_path = crate, resources = [u64], id = "test.leaf", response = u64, policy = POLICY,
201 key = |request: &Leaf| crate::operations::CacheKey::new(request.value.to_le_bytes()).ok())]
202 struct Leaf {
203 value: u64,
204 }
205 impl Operation for Leaf {
206 type Error = std::convert::Infallible;
207 async fn run(self, ctx: Context<'_, Self>) -> Result<Response<u64>, Self::Error> {
208 Ok((self.value + *ctx.resource::<u64>()).into())
209 }
210 }
211
212 #[crate::operation(harmos_path = crate, id = "test.parent", response = u64, policy = POLICY, operations = [Leaf])]
213 struct Parent {
214 value: u64,
215 }
216 impl Operation for Parent {
217 type Error = InvocationError;
218 async fn run(self, ctx: Context<'_, Self>) -> Result<Response<u64>, Self::Error> {
219 ctx.operation(Leaf { value: self.value }).await
220 }
221 }
222
223 #[tokio::test]
224 async fn declared_calls_verify_activation_and_run_with_resources() {
225 let runtime = crate::Runtime::<Model>::builder((), ()).register(Lanes::catalog()).runtime().await.unwrap();
226 let missing = harmos_sdk::RegistrationBatch::new(harmos_sdk::Dependencies::new()).operation::<Parent>();
227 assert!(runtime.register(missing.defaults()).await.is_err());
228 let missing = harmos_sdk::RegistrationBatch::new(harmos_sdk::Dependencies::new()).operation::<Leaf>();
229 assert!(runtime.register(missing.defaults()).await.is_err());
230 let mut dependencies = harmos_sdk::Dependencies::new();
231 dependencies.insert(Arc::new(7_u64)).unwrap();
232 let batch = harmos_sdk::RegistrationBatch::new(dependencies).operation::<Parent>().operation::<Leaf>();
233 runtime.register(batch.defaults()).await.unwrap();
234 runtime.operations.set_cache_capacity(2);
235 let direct = runtime.operation(Leaf { value: 5 }).run().await.unwrap();
236 let child = runtime.operation(Parent { value: 5 }).run().await.unwrap();
237 assert_eq!(*child, 12);
238 assert!(direct.ptr_eq(&child));
239 runtime.stop().await.unwrap();
240 }
241}