It's high time for y'all to get reminded

Nevkontakte shared 9 days ago is one of the weirdest butterfly effect bugs I got to debug.

The effect that starts the chain is simple enough, the hash/maphash.TestHashHighBytes test introduced in Go 1.19 is flaky under GopherJS. What does the test do? It checks that the high 32 bits of the hash are actually sort of random.

The 64-bit hash is computed using a 64-bit seed and a 32-bit low-level hash function, here's an abbreviated version:

func rthash(b []byte, seed uint64) uint64 {
	lo := memhash(b, uint32(seed))
	hi := memhash(b, uint32(seed>>32))
	return uint64(hi)<<32 | uint64(lo)

Somehow, half the time the higher 32 bits of the seed are exactly FFFFFFFF. Why would that be?

Well, the seed is generated by this function, which looks innocent enough:

func fastrand64() uint64 {
	return uint64(fastrand())<<32 | uint64(fastrand())

Liberal use of print-debugging reveals that the high 32 bits of the right uint64(fastrand()) operand are FFFFFFFF. So when or-ed with the other side or basically stomps whatever randomness would have been there. What is fastrand then?

func fastrand() uint32 {
	return uint32(js.Global.Get("Math").Call("random").Float() * (1<<32 - 1))

Wat? How could upsizing a uint32 to uint64 possibly yield FFFFFFFF in the high bits?!

WARNING, we are now exiting vanilla Go lands and delving into JavaScript madness that makes GopherJS work.

Because JavaScript is what it is, GopherJS has little choice but use the same number type to represent all non-64-bit integers, throwing a modulo operation here and there to keep up the appearances. Similarly, float64 and float32 are actually also the same number under the hood. That said, JavaScript does give us a tool to pretend there are signed and unsigned integers: >> and >>> respectively. In JS, the following are true: (4294967295 >> 0) === -1 and (4294967295 >>> 0) === 4294967295 (yeah, the infamous === has a little >>> brother, don't ask what happened to <<<).

Going back to GopherJS, it uses this trick to maintain signed and unsigned integers. We almost have our smoking gun. The last piece of the puzzle has to do with the unit32 โ†’ uint64 conversion step. When GopherJS tries to interpret a negative number as uint64, it assumes the number is signed, and sets the high 32 bits to FFFFFFFF as it should. Let's look at fastrand again:

func fastrand() uint32 {
	return uint32(js.Global.Get("Math").Call("random").Float() * (1&lt;&lt;32 - 1))

The js.Global.Get("Math").Call("random").Float() * (1<<32 - 1) is not very interesting, it just gives us a float64 in the range of [0, 2^32). But then it gets converted to unit32. The correct way of doing it is using the >>> 0 trick, but the compiler was emitting >> 0. Which meant any value in the [2^31, 2^32) range would be converted to its two's complement negative number. Boom! The gun fires. When our negative supposedly uint32 value is converted to uint64 the high bits get set to FFFFFFFF; when it gets |'ed with another number, it overrides whatever higher bits the other side had. Bad things happen then.

Today I've heard a wonderful term: cringineer. Now I'm thinking how do I put it into my Linkedin profile.

What would you call renting a room to a sketchy kind of dude?

Sustenance ๐Ÿฅ

Nevkontakte shared 17 days ago

Another case of a 6-line bug fix accompanies by 160 lines of unit tests.

Nevkontakte shared 23 days ago

If I were Satan, righteousness would have been my favorite invention. Nothing like it to set people against each other, or blind them to the real consequences of their actions.

Somewhat impulsively, I accomplished two things I've been procrastinating on for months:

  • Upgraded my main PC from 16 to 64 GiB RAM. As neat SFX PCs are, a trivial upgrade like that required taking half of it apart, including the CPU cooler to get to the RAM slots.
  • Added a shim to my main monitor arm to raise it by about 5cm. It gave me a bit more clearance to store junk under it, and also a lot more comfortable level to look at it while leaning back.

Holy Inquisition had it right when they burned books. Every time I open a book, any hope of a productive evening is gone. Worse than alcohol! Think of all the free open source labor I could have provided if not for books!

Right, what page was I on...

It occurred to me that a lot of what I've been doing at work amounts to collecting and redistributing proverbial fishing rods just so that people stop bugging me about the same thing over and over again. And if I ever want to have people stop asking me for fishing rods, I need to move into the business of teaching people to make fishing rods from materials at hand. And all I wanted was just to be left alone and write some software.

(Ignore the sound of the metaphor cracking on its seams)

Nevkontakte shared a month ago

A counter-intuitive observation: the more I get done at work in a given day, the more energy I have for some pet project in the evening or weekend.

Interesting... 6 years ago or so I tried the Microsoft Natural Ergonomic Keyboard 4000 (one their split models) at work and after some struggle abandoned it for a more traditional mechanical keyboard. Today I pulled it out of the drawer it was sitting in and gave it a go just because I was bored. Weirdly, I find it a lot more comfortable than I remember and the layout doesn't give me as much of a headache. I don't like how the key press feels, but that's kind of minor.

Maybe I should look into ergonomic keyboards again ๐Ÿค”

Candidate requirements for a job in a ministry of foreign affairs. Wrong answers only.

Caffeine is just energy borrowed from your tomorrow self. Like any loan, could be a useful tool, but also a way to dig yourself into a pit โ˜•

Nevkontakte shared a month ago

If you live in the same online bubble as me, you must have heard about the xz/liblzma backdoor, and if you haven't you can read a TL;DR here.

In its wake, a lot of folks are discussing critical open source infrastructure, sustainability, expectations that can or can not be placed on hobbyist maintainers, and whether they need to payed for their contribution. People point out various foundations that have employed high-profile maintainers so that they could work on the project full-time, and others reasonably assert that such approach can't scale to cover all of the open source.

So, here's my hot take: they only way to sustainably develop free software is to not need to be payed for it. And the only way this could be achieved at a scale software industry needs is for people not to need a paid job to have a decent, comfortable and safe living. Not just for open source maintainers, every human being on this planet. Don't want to work? That's okay, enjoy days with your family and friends. Getting bored? That's cool, do something that's interesting to you, and if your work is beneficial to the society at large - enjoy our heartfelt thank yous.

If you ask me "but who would want to work as a trash truck driver then?", my answer is "go check out how many people play Farming Simulator and similar games". Damn, I myself spent days driving virtual trucks in Euro Truck Simulator, for negative amount of money - I had to pay for the game after all. Then there's also AI, which wouldn't really mind the stink of trash cans that need to be taken out.

And while what I'm saying may sound outlandish or communist, it's actually neither. The objective is to overcome material scarcity, and historically the need for human labor has been the main obstacle for that. I firmly believe that today's technological advancement level is sufficient to achieve that at least as far as physical goods go. It'll take much longer to get to the stage where all services can be comfortably provided on volunteer basis, but it no longer seems implausible to me. We don't even have to abandon capitalism (something I have mixed feelings about, but that's a rant for another day), it'll just shift into the realm of non-material or unique goods.


I didn't realize Windows now supports Bonjour by default, this is nice. I ought to begin using it more actively then.