we did some pre task in blog: https://liamy.clovy.top/e771385866b0465990dcd7e17870b591
1.The TaskHeader
There are some comments in the code above. it maybe useful to understand the following parts of the article, for the
TaskHeader
almost contains all information about a task.Here, we set the
poll_fn
, a func pointer to TaskStorage::<F>::
poll
instead of our main task’s poll, which is generated by compiler. When we set the value, we won’t call the func poll
. This func will be mentioned below.
Then we set the
future
to our main future. And create a TaskRef
, a pointer to the TaskStorage
. Finally, we wrap the task ptr to a
SpawnToken
so that the task will be scheduled by the executor.2. How to make my function like async function with OSTimeDly as await
2.1 first need to understand the macro task in embassy
from file embassy-executor-macros/src/lib.rs:27
it’s easy to understand the args and item in the function signature. And the args and f in the body corresponding to the input args and item. So the important part that do the practical thing is
task::run
part.2.2 the task run part
from embassy-executor-macros/src/macros/task.rs:15
this is really long to say, I beak it down.
2.2.1 check the arg input
from this part, we get the args pool_size. Below is the default value setting for unwrap_or
it construct a literal expression which has no attributes and it’s literal value is a literal int with value 1. The
Span::call_site()
(part of proc_macro2), where the Span object is to report the src location when error occurs and so this means it will point to the use/call of macro to help developer debug.2.2.2 check the function input
So the function should without: generic, `where` clauses, ABI qualifier and variadic. It's return should either not return a value, return `()` or return `!`. And most importantly, it should be async.
2.2.3 tackle the input arguments of function
note the check above allow no self arguments and only allow ident pattern.
2.2.4 construct the task’s signature
this clone the ident into
task_ident
and give a new ident with __{task_ident}_task into the task_inner_ident
this Clone
f
into task_inner
and Clone the visibility of task_inner
then Set task_inner
visibility to Inherited
. Visibility::Inherited
means that the visibility is not specified in the source code and should be inherited from the surrounding context. This is typically used for items within a module or a trait. finally Set task_inner
identifier to task_inner_ident
2.2.5 assemble the original input arguments
Generate tokens with
quote!
macro: Inside the loop, the quote!
macro is used to generate tokens. The quote!
macro is a powerful tool in procedural macros for generating Rust code. The syntax #(#cfgs)*
uses repetition inside the quote!
macro to iterate over cfgs
and include each element in the output. The #arg
syntax injects the value of arg
into the generated tokens.this process is simple to understand if you take a closer look.
it define the origin func(which is task_ident), the body of this function is different from origin:
it define the pool_size and also new the TaskPool with the defined pool_size as pool. Finally it use
spawn_async_fn
of type Taskpool
to set our origin task function as a future function(this future just means this function is FutFn: FnOnce() -> F
and F is F: Future + 'static
, which is exactly what async
most does) Conclusion
According to the research on task macro of embassy, we can find that the implementation of async in RTOS is mainly depending on the rust language itself, as we need the concept of future. The keywords async mostly convert our original plain function to the closure that is
FnOnce() -> F
, where F is F: Future + 'static
. There seems no practical solution to support other language.But I still have some ideas to support what we may implement in further future:
Thoughts
we can write in rust with dynamic/fixed number of async tasks which is very light and switch fast to perform what an OS do to schedule and the tasks’s job is like a tiny OS, which can perform execute multiple(also one) threads, with which it can support other language to use the async like a thread but with better performance.
But now, I think we can first do jobs based on embassy to support priority preempt and only support rust to verify our understanding and make necessary prerequisites for our next level improvements.