Live data from Hacker News

A four year plan for async Rust

without.boats

161–170 of 236 posts

Re: A four year plan for async Rust

#161

Earlier quoted context omitted.

I'm not sure "percentage of usage" is a metric you can use to fully decide how "useful" something is. Take macros in Clojure for example, usually you just have a few of those, but the ones you have are really useful, they give a lot of functionality and practicality to the language. Maybe the same thing is happening with sync/async generators?

That's a good observation about a lot of language features, yes. Macros are a particularly good example. But for a feature like generators which is, I think, essentially user-facing syntactic sugar, I do think a count of usages is a pretty good measure of usefulness. It's probably also worth noting that my data here is from published packages, which tends to skew towards reusable libraries and away from application c…

> I think, essentially user-facing syntactic sugar,

One thing to remember is that Rust is a bit weird here; often times, features like this are "sugar" in a sense, but that sugar lets you write safe code, whereas the non-sugar version would force you to write unsafe. This means this kind of thing is a larger win in Rust than it would be in other languages.

Re: A four year plan for async Rust

#162

I think std needs a default runtime, and we might as well make it tokio, but maybe make the single-threaded executor the default instead and tweak the API where appropriate to align with this change. Swapping the executors out should absolutely be a feature, and the traits should be portable, but a way to start fixing the situation beyond the great suggestions in this proposal is to acknowledge that std and no-std us…

I would not think it would be that good. Having multiple runtimes is great for specific use cases. I would also fear that having "one standard endorsed runtime" would lead to either the death of the alternative ones, or to the freeze of it once it's in the stdlib and fall to oblivion (like many packages in the python standard library). What the stdlib actually needs is the proper set of traits/facades/whatever to int…

Agree on both points. While I don't find async rust hard to use as a library consumer, trying to write a runtime-agnostic library is (IMO) more difficult than it needs to be. I would like to see most of `futures` either be incorporated into the stdlib or used more consistently throughout the various runtimes.

Re: A four year plan for async Rust

#163

> AsyncIterator and async generators For what it's worth, Dart has had synchronous and asynchronous generators (including `await for` statements) for as long as its had async/await. They are neat features. I've definitely written code using synchronous generators that would be hard to manually transform into a custom Iterable implementation. But they add a large amount of complexity to the language implementations an…

I've just used an async generator when upgrading to aws-sdk v3, the function lists all the items in a s3 bucket, if its truncated then it yeilds a recursive generator. Stops when it's got all the keys. First time in 5 years writing typescript I've actually used a generator.

Generators (either variety) in JavaScript are flawed in their implementation in that they only went halfway with it. They also needed to make all primitive objects iterable as well, and/or they should have introduced iterable helpers for all data structures. The fact that you can't use an object in a `for` loop without implementing Symbol method(s) is a glaring hole in their every day utility. It makes it non-obvious to reach for in many cases

The other to me is some of the semantics of generators are not well thought out, for instance, you have to call `.next()` _twice_ in order to get the first `yield` value, and how arguments to `.next()` should be used correctly is opaque. This combined with the fact `yield` doesn't follow the same lexcial scope as `this` does (IE, you can't have an arrow function yield if it was enclosed by a generator, unlike `this` which can be used inside an arrow function as reference to its enclosing scope. This would make generators far more useful IMO).

Combine all this with the fact that most frameworks don't support generators natively for things like components (but they are starting to accept Promises / async functions) you end up with a relatively niche feature

Re: A four year plan for async Rust

#164
post #56

I was a huge opponent of Rust, but finally decided to give it a try in anger once again. I began writing a large application, and noticed that many of my libraries only offered async versions, and the promise was quite appealing -- not having to worry about threads or concurrency as long as I followed certain rules. What I ended up with was an incredibly slow application because of the limitations of Rust async, and…

> All of my I/O got pushed through one thread (with tokio)

In all my use of Tokio in the last few years, I never heard of such a thing.

In tokio::fs::File [1] it calls spawn_mandatory_blocking to do file writes, I assume this is similar to spawn_blocking [2] which sends a task to Tokio's blocking thread pool. That thread pool is supposed to max out at 512 blocking threads [3], unrelated to CPU core count.

Tokio's TcpStream appears to be built on mio's TcpStream. I didn't dig deep into the code for this, but it doesn't just call spawn_blocking, and I'm assuming on Unix it ultimately registers the socket with epoll or equivalent, so it never blocks a thread to do socket reads or writes.

Could you share more about your application?

[1] https://docs.rs/tokio/latest/src/tokio/fs/file.rs.html#682

[2] https://docs.rs/tokio/latest/tokio/task/fn.spawn_blocking.ht...

[3] https://docs.rs/tokio/latest/tokio/runtime/struct.Builder.ht...

Re: A four year plan for async Rust

#165
post #133

Earlier quoted context omitted.

As far as I know, the proposal to add one was determined to need an RFC: https://github.com/rust-lang/rust/pull/65875

Another instance of a trivial and obvious feature that can only really be designed one way that is not implemented 4 years after initial proposal.

I am not fully sure I agree with the first part of your assessment, but I share the frustration that a feature like this, which would be useful and is not particularly large, is basically ignored by the working group, while larger, more controversial, and less useful features, like keyword generics, are pursued while the more useful things languish.

Re: A four year plan for async Rust

#166

Earlier quoted context omitted.

Why didn't you use the multithreaded executor from Tokio?

Tokio does sync I/O in the background with dedicated I/O threads if memory serves. (it is sync in the sense it does syscalls, which are pushed to a dedicated thread).

Even then I'm pretty sure it's not _a_ dedicated thread, but a thread pool that defaults to a maximum of 512 blocking threads. There's no reason it would bottleneck unrelated writes on unrelated Files through the same thread. I tried to dig into the code here https://news.ycombinator.com/item?id=38180860

Re: A four year plan for async Rust

#167

Earlier quoted context omitted.

Just because it's a network request doesn't mean it benefits from using async. EDIT: to add a specific example, postgres. I understand from sfackler's perspective why it makes sense to maintain just an async library and a sync wrapper around it. But from the user's perspective there'a absolutely no reason that all of tokio should be required to talk to their db.

As a user who has maintained network services that communicate with Postgres, I'm very glad that sfackler's postgres library doesn't perform blocking IO calls, as that would make it unfit for purpose for me.

"async" and threads are not the only options for concurrent IO.

I've never saw anyone discuss using poll(2) style concurrency here which is interesting but also kind of sad.

With the mio crate for example, it uses the operating systems "select/poll" interface. On Linix this is epoll(7), but other OSes have their own interface.

"poll" style concurrency is a lot less complicated for me. The general idea is to register IO handles such as sockets into a data structure and pass it into the kernel. The kernel will emit events whenever a handle is ready to do something. There is the option to go to sleep and block until one of the handles are ready but it's not required.

"async" is an abstraction on top of this. The async runtime is handling the event loop and other stuff for you which is very useful but also a little bit complicated.

I am not in any way against "async" style concurrency but I think a lot of libraries could avoid pulling in a runtime and still be concurrent.

Re: A four year plan for async Rust

#168

Earlier quoted context omitted.

What Rust libraries use async that you think should not? I can think of one, but otherwise every library I've encountered uses async because its intended for the kind of networking service that benefits from using non-blocking IO.

Example from the recent past: I wanted a quick Rust code to stream s3 object, uncompress with zstd and untar the content to a directory. aws-sdk-s3 supports only async (tokio), tar crate only blocking, async-tar only async-std, async-compression only tokio. Hard to paper over it with `block_on` because of the streaming part. I don't remember what I actually did, but it doesn't matter - having to come up with a magic…

> And using Rust ecosystem was easier just a few years ago than it is now for these use-cases

I used to write Rust web services (professionally) before async and it was definitely not easier for me. It was way harder. I'd end up with accidental hanging because some socket didn't have a timeout set on it properly, it was extremely leaky (why the hell am I talking to raw socket APIs just so that I can read from S3?), and it sucked compared to async. We've had radically different experiences, somehow.

Re: A four year plan for async Rust

#169

Earlier quoted context omitted.

That's a good observation about a lot of language features, yes. Macros are a particularly good example. But for a feature like generators which is, I think, essentially user-facing syntactic sugar, I do think a count of usages is a pretty good measure of usefulness. It's probably also worth noting that my data here is from published packages, which tends to skew towards reusable libraries and away from application c…

> I think, essentially user-facing syntactic sugar, One thing to remember is that Rust is a bit weird here; often times, features like this are "sugar" in a sense, but that sugar lets you write safe code, whereas the non-sugar version would force you to write unsafe. This means this kind of thing is a larger win in Rust than it would be in other languages.

Yeah, that's a good point.

Re: A four year plan for async Rust

#170

I think std needs a default runtime, and we might as well make it tokio, but maybe make the single-threaded executor the default instead and tweak the API where appropriate to align with this change. Swapping the executors out should absolutely be a feature, and the traits should be portable, but a way to start fixing the situation beyond the great suggestions in this proposal is to acknowledge that std and no-std us…

I would not think it would be that good. Having multiple runtimes is great for specific use cases. I would also fear that having "one standard endorsed runtime" would lead to either the death of the alternative ones, or to the freeze of it once it's in the stdlib and fall to oblivion (like many packages in the python standard library). What the stdlib actually needs is the proper set of traits/facades/whatever to int…

In reality there aren't multiple runtimes in a real, pluggable, generic sense. There's tokio and then some niche things that are hard to build on top of because they're not tokio (despite being possibly technically superior in some way).

Every async-focused dependency that is relevant effectively mandates Tokio. And I've been ridiculed in public and private forums for suggesting that there should be a way to write libraries generically so that they don't have a runtime dependency. (Right now, services as basic as locking and task spawning are coupled to tokio, at least, and people use them all over.). Major new things are being made, all coupled to tokio.

And I think this is a shitty situation for a whole bunch of reasons. And others (looks like you, too) agree, but it feels like there's just no way this is going to get fixed.

Like, this article here, it doesn't even mention this as a concern? In the context of a big picture discussion of async over the next 4 years.

So I don't foresee any progress on this front, and it makes me want to just rip async out of my code entirely.

Post reply on HN