Earlier quoted context omitted.
I don’t think this is true. Context managers call special magic “dunder” methods on the instance (I don’t remember the specific ones), and I’m pretty sure those don’t get called during regular garbage collection of those instances. It’s been a few years since I was regularly writing python, so I might be wrong, but I don’t believe that context manager friendly instances are the same as Rust’s Drop trait, and I don’t…
Python is a fun case of "all of the above" (or rather, a layering of styles once it turns out a previous one isn't workable). Originally, they used pure reference counting GC, with finalizers used to clean up when freed. This was "fine", since RC is deterministic. Everything is freed when the last reference is deleted, nice and simple. But reference counting can't detect reference cycles, so eventually they added a s…
Why you might want async in your project
71–80 of 182 posts
Re: Why you might want async in your project
#72The author starts by citing greenspun's tenth rule and goes on to elaborate on the argument that if you are going to have a half implementation of async anyway, why not just pull it in? Yet fails to interrogate the relationship between this argument and the cited "rule". If you should use async because you might need it in the future, shouldn't we all be writing in lisp? If we presuppose that all software eventually…
Re: Why you might want async in your project
#73do any of the async libraries for rust have good visualization tools for inspecting the implicit state machine that is constructed via this type of concurrency primitive?
The first transformation is that every async block / fn compiles to a generator where `future.await` is essentially replaced by `loop { match future.poll() { Ready(value) => break value, Pending => yield } }`. ie either polling the inner future will resolve immediately, or it will return Pending and yield the generator, and the next time the generator is resumed it will go back to the start of the loop to poll the future again.
The second transformation is that every generator compiles to essentially an enum. Every variant of the enum represents one region of code between two `yield`s, and the data of that variant is all the local variables that in the scope of that region.
Putting both together:
async fn foo(i: i32, j: i32) {
sleep(5).await;
i + j
}
... essentially compiles to: fn foo(i: i32, j: i32) -> FooFuture {
FooFuture::Step0 { i, j }
}
enum FooFuture {
Step0 { i: i32, j: i32 }
Step1 { i: i32, j: i32, sleep: SleepFuture }
Step2,
}
impl Future for FooFuture {
fn poll(self) -> Poll {
loop {
match self {
Self::Step0 { i, j } => {
let sleep = sleep(5);
self = Self::Step1 { i, j, sleep };
}
Self::Step1 { i, j, sleep } => {
let () = match sleep.poll() {
Poll::Ready(()) => (),
Poll::Pending => return Poll::Pending,
};
self = Self::Step2;
return Poll::Ready(i + j);
}
Self::Step2 => panic!("already run to completion"),
}
}
}
}Re: Why you might want async in your project
#74Earlier quoted context omitted.
I don’t think this is true. Context managers call special magic “dunder” methods on the instance (I don’t remember the specific ones), and I’m pretty sure those don’t get called during regular garbage collection of those instances. It’s been a few years since I was regularly writing python, so I might be wrong, but I don’t believe that context manager friendly instances are the same as Rust’s Drop trait, and I don’t…
Python is a fun case of "all of the above" (or rather, a layering of styles once it turns out a previous one isn't workable). Originally, they used pure reference counting GC, with finalizers used to clean up when freed. This was "fine", since RC is deterministic. Everything is freed when the last reference is deleted, nice and simple. But reference counting can't detect reference cycles, so eventually they added a s…
Re: Why you might want async in your project
#75Earlier quoted context omitted.
The problem with garbage collection is that it doesn't work for other kinds of resources than memory, so basically every garbage collected runtime ends up with an awkward and kinda-broken version of RAII anyway (Closeable, defer, using/try-with-resources, context managers, etc). Static lifetimes are also a large part of the rest of Rust's safety features (like statically enforced thread-safety). A usable Rust-without…
You make an interesting point. Has any language introduced a generic-resource-collector? You're not supposed to use deconstructors to clean up resources because you're left to the whims of the GC which is only concerned about memory. Has anyone build a collector that tracks multiple types of resources an object might consume? It seems possible.
Re: Why you might want async in your project
#76Re: Why you might want async in your project
#77It's not a problem with tokio either. The author's point is specifically about the multi-threaded tokio runtime that allows tasks to be moved between worker threads, which is why it requires the tasks to be Send + 'static. Alternatively you can either a) create a single-threaded tokio runtime instead which will remove the need for tasks to be Send, or b) use a LocalSet within the current worker that will scope all tasks to that LocalSet's lifetime so they will not need to be Send or 'static.
If you go the single-threaded tokio runtime route, that doesn't mean you're limited to one worker total. You can create your own pseudo-multi-threaded tokio runtime by creating multiple OS threads and running one single-threaded tokio runtime on each. This will be similar to the real multi-threaded tokio runtime except it doesn't support moving tasks between workers, which means it won't require the tasks to be Send. This is also what the author's smol example does. But note that allowing tasks to migrate between workers prevents hotspots, so there are pros and cons to both approaches.
Re: Why you might want async in your project
#78This is only partly true -- if you want to `spawn` a task on another thread then yes it has to be Send and 'static. But if you use `spawn_local`, it spawns on the same thread, and it doesn't have to be Send (still has to be 'static).
Re: Why you might want async in your project
#79Re: Why you might want async in your project
#80Earlier quoted context omitted.
FastAPI docs, case when you don't create an async route > When you declare a path operation function with normal def instead of async def, it is run in an external threadpool that is then awaited, instead of being called directly (as it would block the server). https://fastapi.tiangolo.com/async/#path-operation-functions OP either meant this, or its variation, such as async_to_sync and sync_to_async. https://github.c…
NB: In Python >= 3.9 the idiomatic way to do this is to_thread(), not familiar with these ASGI functions but I would guess they're a polyfill and/or predate 3.9. https://docs.python.org/3/library/asyncio-task.html#asyncio....