Live data from Hacker News

How I turned Zig into my favorite language to write network programs in

lalinsky.com

111–120 of 150 posts

Re: How I turned Zig into my favorite language to write network programs in

#111

Mostly out of curiosity, a read on a TCP connection could easily block for a month - how does the I/O timeout interface look like ? e.g. if you want to send an application level heartbeat when a read has blocked for 30 seconds.

You can set read and write timeouts on TCP sockets:

https://linux.die.net/man/3/setsockopt

Zig has a posix API layer.

Re: How I turned Zig into my favorite language to write network programs in

#112
post #43

Earlier quoted context omitted.

Nobody is denying that? Andrew Kelly and the Zig team have been extremely clear that they are okay making break changes. So if you're choosing to use it in large projects, as some have, you're taking that risk. I think it speaks volumes that these projects chose to use it, and speak very highly of it, despite the fact that it's still pre 1.0.

"Nobody is denying that?" seems like you missing a lot of comment on this tree

Care to point out a few?

Re: How I turned Zig into my favorite language to write network programs in

#113
post #11

Move Zig, for great justice.

One of the very first internet memes. The zig team should adopt it as the slogan. https://en.wikipedia.org/wiki/All_your_base_are_belong_to_us

It has in a way! See:

https://github.com/ziglang/zig/blob/master/lib/init/src/main...

Re: How I turned Zig into my favorite language to write network programs in

#114
post #109

Stackful coroutines make sense when you have the RAM for it. I've been using Zig for embedded (ARM Cortex-M4, 256KB RAM) mainly for memory safety with C interop. The explicitness around calling conventions catches ABI mismatches at compile-time instead of runtime crashes. I actually prefer colored async (like Rust) over this approach. The "illusion of synchronous code" feels magical, but magic becomes a gotcha in lar…

All synchronous code is an illusion created in software, as is the very notion of "blocking". The CPU doesn't block for IO. An OS thread is a (scheduled) "stackful coroutine" implemented in the OS that gives the illusion of blocking where there is none. The only problem is that the OS implements that illusion in a way that's rather costly, allowing only a relatively small number of threads (typically, you have no mor…

Unfortunately, the illusion of an OS thread relies on keeping a single consistent stack. Stackful coroutines (implemented on top of kernel threads) break this model in a way that has many detrimental effects; stackless ones do not.

Re: How I turned Zig into my favorite language to write network programs in

#115
post #109

Earlier quoted context omitted.

All synchronous code is an illusion created in software, as is the very notion of "blocking". The CPU doesn't block for IO. An OS thread is a (scheduled) "stackful coroutine" implemented in the OS that gives the illusion of blocking where there is none. The only problem is that the OS implements that illusion in a way that's rather costly, allowing only a relatively small number of threads (typically, you have no mor…

Unfortunately, the illusion of an OS thread relies on keeping a single consistent stack. Stackful coroutines (implemented on top of kernel threads) break this model in a way that has many detrimental effects; stackless ones do not.

The OS allocates your thread stack in a very similar way that a coroutine runtime allocates the coroutine stack. The OS will swap the stack pointer and a bunch more things in each context switch, the coroutine runtime will also swap the stack pointer and some other things. It's really the same thing. The only difference is that the runtime in a compiled language knows more about your code than the OS does, so it can make assumptions that the OS can't and that's what makes user-space coroutines lighter. The mechanisms are the same.

Re: How I turned Zig into my favorite language to write network programs in

#116

Earlier quoted context omitted.

Unfortunately, the illusion of an OS thread relies on keeping a single consistent stack. Stackful coroutines (implemented on top of kernel threads) break this model in a way that has many detrimental effects; stackless ones do not.

The OS allocates your thread stack in a very similar way that a coroutine runtime allocates the coroutine stack. The OS will swap the stack pointer and a bunch more things in each context switch, the coroutine runtime will also swap the stack pointer and some other things. It's really the same thing. The only difference is that the runtime in a compiled language knows more about your code than the OS does, so it can…

And the stackless runtime will use some other register than the stack pointer to access the coroutine's activation frame, leaving the stack pointer register free for OS and library use, and avoiding the many drawbacks of fiddling with the system stack as stackful coroutines do. It's the same thing.

Re: How I turned Zig into my favorite language to write network programs in

#118
post #30

I am still mystified as to why callback-based async seems to have become the standard. What this and e.g. libtask[1] do seems so much cleaner to me. The Rust folks adopted async with callbacks, and they were essentially starting from scratch so had no need to do it that way, and they are smarter than I (both individually and collectively) so I'm sure they have a reason; I just don't know what it is. 1: https://swtch.…

See also: https://man.9front.org/2/thread

The history of this concurrency model is here: https://seh.dev/go-legacy/

Re: How I turned Zig into my favorite language to write network programs in

#119

Earlier quoted context omitted.

One thing I would consider "unclean" about the zio approach (and e.g. libtask) is that you pass it an arbitrary expected stack size (or, as in the example, assume the default) and practically just kind of hope it's big enough not to blow up and small enough to be able to spawn as many tasks as you need. Meanwhile, how much stack actually ends up being needed by the function is a platform specific implementation detai…

Couldn’t you use the Go approach of starting with a tiny stack that is big enough for 90% of cases, then grow it as needed?

8kB is enough for 90% of use cases. But then you invoke getaddrinfo() once and now your stack is 128kB+.

Re: How I turned Zig into my favorite language to write network programs in

#120

Earlier quoted context omitted.

Couldn’t you use the Go approach of starting with a tiny stack that is big enough for 90% of cases, then grow it as needed?

Go depends on the fact that it can track all pointers, and when it needs to resize stacks, it can update them. Previous versions of Go used segmented stacks, which are theoretically possible, if Zig really wanted (would need compiler support), but they have nasty performance side-effects, see https://www.youtube.com/watch?v=-K11rY57K7k

Resizing stacks on use does not depend on any of these properties of Go. You can do it like this in C, too. It does not require segmentation.
Post reply on HN