Live data from Hacker News

Why you might want async in your project

notgull.net

151–160 of 182 posts

Re: Why you might want async in your project

#151

Earlier quoted context omitted.

The ability to cancel an operation without talking to the operating system requires that your program has yield points. That yielding is what allows another part of the program to take control and say "OK, I'm done with you now, no need to finish". Yes, the problem is that your thread would continue to perform work even if you stopped waiting on it.

Maybe I don't understand the complexity, but in good old Ruby I can easily stop a thread if I don't need result anymore. No async needed and no yield points necessary. Doesn't it apply to Rust too?

I assume that Ruby does in fact have yield points in some form, such as a global lock. Killing a thread is only possible (for a pthread) via the `pthread_cancel` API. That API is very dangerous and is generally not something you'd ever want to use manually - the thread will not clean up any memory or other resources, any shared memory is left in a tricky state.

To gracefully shut a thread down you need yielding of some kind.

Re: Why you might want async in your project

#152

I might need a lot of stuff in my software. Eventually. I might need distributed database, or to to scale it out to run on multiple machines, and then maybe Raft, or reactive architecture, zero-copy IO, or incremental updates or ... or ... the list goes on and on. Thinking too much and in particularly going with over complicated solutions from the very start because "might" is just bad engineering. Also, even if I do…

> But nobody wants to write a library that isn't "web-scale" anymore, so tough luck. It's more like "I want to be able to put timeouts in my code". 99% of why I want async is so that if something takes too long I can just stop that. That is incredibly hard to do without async.

Genuinely curious: looks to me like you can set timeout on sockets (TcpStream) with std. Does that not work? What other kinds of timeouts do you typically need?

Re: Why you might want async in your project

#153
post #144

Earlier quoted context omitted.

They aren't different. They are the same. The only difference is that in Go the `yield` points are implicit (the compiler inserts them) whereas in Rust they are explicit (.await). Otherwise they both schedule a task to be executed with the implicit runtime - in Rust that may be tokio or something else, in Go that would be the Go runtime.

I would say they function similarly. There are quite a few internal differences. The main benefit of Rust is that futures are just regular types and don’t need a stack when idle. And the main benefit of Go is that there is only one type of function, so no coloring. > they both schedule a task to be executed with the implicit runtime To be pedantic, in rust the runtime is referenced with a global or thread-local varia…

Sure, their implementation differs in all sorts of ways. But for the purposes of this conversation, with regards to scheduling a future without having to manually pin it up into a your call graph, they are the same.

> but it’s still explicit.

I guess it just comes down to your definition of explicit. There's a dependency, but from a caller's perspective it's implicit. It doesn't matter though, I think the point is clear enough.

Re: Why you might want async in your project

#154
post #152

Earlier quoted context omitted.

> But nobody wants to write a library that isn't "web-scale" anymore, so tough luck. It's more like "I want to be able to put timeouts in my code". 99% of why I want async is so that if something takes too long I can just stop that. That is incredibly hard to do without async.

Genuinely curious: looks to me like you can set timeout on sockets (TcpStream) with std. Does that not work? What other kinds of timeouts do you typically need?

Relying on the socket APIs is a bit painful. For one thing, what if that socket is shared across tasks? What if I want to do per-request timeouts? What if I don't want to expose the socket APIs to callers?

What if my work isn't related to socket timeouts? For example, downloading a multi-part file? I may want a very long socket timeout but still have a distinct timeout for the individual chunked operations. It might take 30 seconds to grab an entire file but if one chunk takes >3 seconds I may want to time out.

What if my work involves no IO at all?

The ability to cancel work in progress is extremely important to me.

Re: Why you might want async in your project

#155

Earlier quoted context omitted.

> But nobody wants to write a library that isn't "web-scale" anymore, so tough luck. It's more like "I want to be able to put timeouts in my code". 99% of why I want async is so that if something takes too long I can just stop that. That is incredibly hard to do without async.

Not sure about Rust, but in other languages that don't have async: create a queue, spawn a thread with your task, thread with sleep and wait for a message from any of those two. Kill the still running thread when you get the message. Can't say it's incredibly hard (unless it's Javascript or you work in a single-threaded model in general).

> Kill the still running thread when you get the message

This is extremely difficult. I mentioned elsewhere that the only way to kill a thread is through the pthread_cancel API, which is very dangerous.

Languages with larger runtimes can get around this because they have all sorts of things like global locks or implicit cooperative yielding. So they don't ever have to "kill" a thread, they actually just join it when it yields.

Re: Why you might want async in your project

#156

Earlier 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…

> 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). RAII works only for the simplest case: when your cleanup takes no parameters, when the cleanup doesn't perform async operations, etc. Rust has RAII but it's unusable in async because the drop method isn't itself async (and thus may block the w…

I agree but does RAII necessarily imply parameter-free destruction?

Personally I love Rust’s `fn foo(self, …)`, which is just like a regular method but consumes the value.

Deallocate by default is fine, but sometimes you need to run specific destructors (linear type style). I’ve long wished for an opt-out from implicit drop semantics for resource/handle types.

Re: Why you might want async in your project

#157

> Why don’t people like async? That's pretty simple. The primary goal of every software engineer is (or at least should be) ... no, not to learn a new cool technology, but to get the shit done. There are cases where async might be beneficial, but those cases are few and far in between. In all other cases a simple thread model, or even a single thread works just fine without incurring extra mental overhead. As profess…

Show me how to cancel a network requests using only threads, with no access to the underlying socket APIs? Because that's trivial with `async`. That's not "fun", that's table stakes.

> Show me how to cancel a network requests using only threads, with no access to the underlying socket APIs?

It’s been a long time since I did this in Rust. But why do you not have access to the sockets or at least a set_timeout method? Is it a higher level lib that omits such crucial features?

In Go, the super common net.Conn interface has deadline methods. Not everyone knows their importance but generally you have something like it piped through to the higher layers.

EDIT: Oh I see you replied to my other comment. Please disregard.

Re: Why you might want async in your project

#158
post #152

Earlier quoted context omitted.

Genuinely curious: looks to me like you can set timeout on sockets (TcpStream) with std. Does that not work? What other kinds of timeouts do you typically need?

Relying on the socket APIs is a bit painful. For one thing, what if that socket is shared across tasks? What if I want to do per-request timeouts? What if I don't want to expose the socket APIs to callers? What if my work isn't related to socket timeouts? For example, downloading a multi-part file? I may want a very long socket timeout but still have a distinct timeout for the individual chunked operations. It might…

> For one thing, what if that socket is shared across tasks?

Yeah that’s an issue. In Go they sync it (thread safe), which in Rust would translate to interior mutability.

> What if I want to do per-request timeouts?

Ah you’re right in http2 there can be multiple concurrent reqs per conn. Go still allows request based timeouts, but I wonder if that’s possible with the limited primitives in std. It’s also true that this is a case where the inner conn should not be exposed.

> I may want a very long socket timeout but still have a distinct timeout for the individual chunked operations.

Right! That’s typically done by extending the deadline for every chunk. Ie the user/caller needs a way to set timeouts.

> The ability to cancel work in progress is extremely important to me.

Yes for sure. I was just curious. Btw which libs are you referring to for network requests? I’d like to see their APIs.

Re: Why you might want async in your project

#159

Earlier quoted context omitted.

Not sure about Rust, but in other languages that don't have async: create a queue, spawn a thread with your task, thread with sleep and wait for a message from any of those two. Kill the still running thread when you get the message. Can't say it's incredibly hard (unless it's Javascript or you work in a single-threaded model in general).

> Kill the still running thread when you get the message This is extremely difficult. I mentioned elsewhere that the only way to kill a thread is through the pthread_cancel API, which is very dangerous. Languages with larger runtimes can get around this because they have all sorts of things like global locks or implicit cooperative yielding. So they don't ever have to "kill" a thread, they actually just join it when…

As I understand when you are blocked on I/O and sends a signal to the waiting thread, that system call will simply be released and return an error. Ruby (Java etc.) does make it simple because of GC, so I don't need to worry about file descriptor leaks etc. But talking about Rust, shouldn't it be a part of a thread management? Basically if an error happens during normal blocking system call, it goes through the same sequence, no? E.g. you have to release any thread-local allocations no matter by each way system call was terminated. Rust threads are supposed to be memory safe, not sure about file descriptors. I don't quite understand what you mean by "yielding" though.

Re: Why you might want async in your project

#160
post #68

Earlier quoted context omitted.

Most commercial code is running an almost entirely IO workload, acting as a gatekeeper to a database or processing user interactions - places where async shines. Async isn't a lark, it's a workhorse. The goal is not to write sexy code, it's to achieve better utilization (which is to say, save money).

Depends on the nature of commercial code and if it has another level of parallelism (think of web servers and read my comment below). As for DB queries, here's the thing: most commercial code is using DB transactions and there is no way to run transaction across multiple connections, so you are either single-threaded and do things in sequence anyway (why use async then?), or you are multi-threaded and then forget abo…

Most web applications (every one I've ever worked on) use connection pooling to run multiple transactions in parallel. I suppose you could think of that as a sort of network level parallelism, but it's not multithreading.

Connection pooling is of course not without it's hazards, scaling databases can be very difficult and almost all of the production incidents I've dealt with involve a database running out of a resource (often connections). But for your garden variety web app, it certainly isn't a dichotomy between serializing all concurrent updates or losing atomicity.

Post reply on HN