Live data from Hacker News

Green threads explained in 200 lines of Rust

cfsamson.gitbook.io

11–20 of 48 posts

Re: Green threads explained in 200 lines of Rust

#11
post #10
post #7

Earlier quoted context omitted.

Yeah, of course, but how would you work this type of code into something that can be used in production? The goal should be to have a green thread library that you can just use without thinking about registers or assembly.

> how would you work this type of code into something that can be used in production? You wouldn't. There are production-ready solutions to this in many languages. > The goal should be to have a green thread library that you can just use without thinking about registers or assembly. The goal here is very explicitly to explain a concept by example, which is largely incompatible with what you say the goal should be. I…

> I think the author did a very good job.

I wasn't questioning that!

Re: Green threads explained in 200 lines of Rust

#12
post #7
post #6

Earlier quoted context omitted.

> a simple but working example

Yeah, of course, but how would you work this type of code into something that can be used in production? The goal should be to have a green thread library that you can just use without thinking about registers or assembly.

> Yeah, of course, but how would you work this type of code into something that can be used in production?

Why would you?

> The goal should be to have a green thread library that you can just use without thinking about registers or assembly.

No it should not. That's a completely separate (and mostly incompatible) goal than TFA's, which is educational.

Re: Green threads explained in 200 lines of Rust

#13
Part of the code is:

    unsafe {
        std::ptr::write(stack_ptr.offset(SSIZE - 16) as *mut u64, hello as u64);
        ctx.rsp = stack_ptr.offset(SSIZE - 16) as u64;
        gt_switch(&mut ctx, &mut n);
    }
Only the last line should be unsafe — the first line appears to be writing in bounds to a Vec, which is easy (and much more readable) in safe Rust.

Linux used to do its actual context switch in C with inline asm, and it changed to being in straight asm a while back. This was absolutely a win. Rather than trying to make everything work out behind the compiler’s back, it’s much more straightforward to write a function in asm that does the stack switch.

Re: Green threads explained in 200 lines of Rust

#14

Co-routines are very useful and likely underused, but sometimes you are actually better off being able to pass the control to a given thread directly, other than having a scheduler involved. Anecdote: almost a decade ago, I was responsible for an NVMe-like implementation (hard- and software). The 3rd version of the firmware recognized the various components as threads, but there was no need for preemption (which woul…

If you have any knowledge of it, what are your thoughts about the Erlang scheduler & Erlang's concurrency model?

Funny enough we actually started prototyping with Erlang for subsequent project (which was cancelled before it went far). Unfortunately I don't know enough to know what's special about the Erlang scheduler (if anything), but as I understand the Erlang concurrency model, it's mostly about not sharing memory (forces explicit communication). That's obviously going to eliminate a host of bugs, but it would have been way too expensive for the mentioned firmware.

Once we got started with Erlang I was pretty turned off. The pretty examples you see in tutorials aren't what you'll be using. Instead it's framework upon framework, far from elegance IMO. I was happy to not have to deal with that again. Today I'd probably choose Rust for the same task (static types FTW).

(EDIT: typos)

Re: Green threads explained in 200 lines of Rust

#15
post #13

Part of the code is: unsafe { std::ptr::write(stack_ptr.offset(SSIZE - 16) as *mut u64, hello as u64); ctx.rsp = stack_ptr.offset(SSIZE - 16) as u64; gt_switch(&mut ctx, &mut n); } Only the last line should be unsafe — the first line appears to be writing in bounds to a Vec, which is easy (and much more readable) in safe Rust. Linux used to do its actual context switch in C with inline asm, and it changed to being in…

Fantastic (IMHO) read on the last paragraph's history and code movement: https://www.maizure.org/projects/evolution_x86_context_switc...

Re: Green threads explained in 200 lines of Rust

#16
post #4

Isn't one downside that the inline assembly will not be peephole-optimized by the code generator (LLVM)? E.g., you'd be saving and restoring registers that are not even used.

I don't know how practical that optimization would be in this particular use case, wouldn't that mean the context switch code would have to be inlined at every call site? Then when you switch back to that context how would you generate that code correctly? You would need to know which registers were not saved by green thread A, and not touched by green thread B. So the compiler would basically need to know the runtime behavior of your program to optimize pushing and popping context records, unless I misunderstood your point?

If you set a breakpoint in say a win32 fiber switch, and look at the disassembly, it jumps to an internal function that just saves all the registers (and flags) to the active context and restores all the registers from the resumed context every time. Don't know how more optimal that can be for the general case.

Re: Green threads explained in 200 lines of Rust

#17

The explanation of the asm! macro is great, I’ve always been a little confused by it. It does make me thinking about how this could be useful in the context of Futures and async/await.

> It does make me thinking about how this could be useful in the context of Futures and async/await.

At its core, when you pass a Future (really, a chain of futures, but in the end that's still a Future) to an executor, the executor creates a stack for it. An executor with multiple futures will then switch between them, sorta similar to the code seen here. The details depend on the exact executor, of course, but the fundamental idea is the same.

One of the nice things about the futures design is that you can know exactly how big the stack needs to be, so the resizing stuff isn't an issue.

Re: Green threads explained in 200 lines of Rust

#18

Earlier quoted context omitted.

If you have any knowledge of it, what are your thoughts about the Erlang scheduler & Erlang's concurrency model?

Funny enough we actually started prototyping with Erlang for subsequent project (which was cancelled before it went far). Unfortunately I don't know enough to know what's special about the Erlang scheduler (if anything), but as I understand the Erlang concurrency model, it's mostly about not sharing memory (forces explicit communication). That's obviously going to eliminate a host of bugs, but it would have been way…

Given that Erlang is sufficiently different from anything else I've seen, it doesn't surprise me that trying to be productive in Erlang before you knew it well enough was suboptimal experience. I like it, and more specifically Elixir, quite a bit, but the learning curve was steep.

Re: Green threads explained in 200 lines of Rust

#19
post #13

Part of the code is: unsafe { std::ptr::write(stack_ptr.offset(SSIZE - 16) as *mut u64, hello as u64); ctx.rsp = stack_ptr.offset(SSIZE - 16) as u64; gt_switch(&mut ctx, &mut n); } Only the last line should be unsafe — the first line appears to be writing in bounds to a Vec, which is easy (and much more readable) in safe Rust. Linux used to do its actual context switch in C with inline asm, and it changed to being in…

The ptr::write is needed because the stack vector contains bytes and the code wants to do a pointer cast and write a u64. It could be done in safe Rust with u64::to_ne_bytes and a copy_from_slice or something like that, but since all of this code is extravagantly unsafe anyway, I think it's reasonably clear to Just Do It :)

Re: Green threads explained in 200 lines of Rust

#20

Co-routines are very useful and likely underused, but sometimes you are actually better off being able to pass the control to a given thread directly, other than having a scheduler involved. Anecdote: almost a decade ago, I was responsible for an NVMe-like implementation (hard- and software). The 3rd version of the firmware recognized the various components as threads, but there was no need for preemption (which woul…

If you have any knowledge of it, what are your thoughts about the Erlang scheduler & Erlang's concurrency model?

As he replies, Erlang is operating at a significantly higher level than that kind of project.

It's well worth learning about how it works, as it's a great fit for some problems.

Post reply on HN