Hacker Newsnew | past | comments | ask | show | jobs | submit | smarks's commentslogin

In my earlier message [1] I described the evolution of the PEM Encodings API in a way that prevents applications from being exposed to the risk of runtime exceptions arising from future additions to the list of permitted subclasses.

[1]: https://news.ycombinator.com/item?id=50148756

The discussions around the PEM Encodings API also led my colleagues on the langtools team to write an article "Preparing for Change: Safe Switching over Sealed APIs" that discusses these issues in more detail. [2]

[2]: https://openjdk.org/projects/amber/guides/exhaustiveness-gui...

The article focuses on pattern matching and exhaustive checking, because this is an important feature for a large class of applications.

As a library designer, though, I'm primarily interested in the use of sealed classes to control subclassing. The old idiom for controlling subclassing of concrete and abstract classes was to ensure that all constructors had package access or less. This mostly prohibits subclasses outside the package. (Strictly speaking, it prohibits such subclasses from being compiled or instantiated.) For interfaces, it was formerly impossible to control subclassing at all. The addition of sealed classes to the language solves the problem completely.

Indeed, in the JDK we've converted all the uses of the older idioms to use sealed classes. [3] (This bug covers most of the changes; there are a handful of others that were converted individually.) Of the several dozen examples here, it doesn't look to me like ANY of them are candidates for pattern matching. This probably reflects the long-standing issue of subclassing control in object-oriented systems. That issue has existed in Java much longer than pattern matching. As time goes on, I expect the JDK class libraries to introduce more new APIs that make use of pattern matching and sealed classes.

[3]: https://bugs.openjdk.org/browse/JDK-8283414


The article (which seems to be from 2026-04-24 based on its URL) refers to the JDK 26 version of the PEM Encodings API. [1]

[1]: https://openjdk.org/jeps/524

The article points out a potential pitfall with how this version of the API exposes a sealed class. If the application has an exhaustive switch expression that lists all permitted subclasses, and a later JDK release were to add a new permitted subclass, this would potentially expose the application to a MatchException at runtime.

In the JDK 27 version of the PEM Encodings API, JEP 538 [2], we’ve adjusted the permitted subclasses of the sealed class in question to include a non-public permitted subclass.

[2]: https://openjdk.org/jeps/538

This prevents applications from using an exhaustive switch expression by enumerating all permitted subclasses. This allows the JDK to extend set of permitted subclasses in the future without exposing applications to the risk of unexpected exceptions.


It's a nice workaround but the necessity of such a non-obvious fix also shows that the design of sealed classes is really flawed ..


Not really.

Defaulting to exhaustive pattern matches is correct, it’s by far the most frequent case (which is why essentially every modern language does that); being able to prevent exhaustive matching is useful for libraries; and using a private subclass is a completely obvious way to implement that in Java.

See private extensible variants for the same capability in OCaml. Or the non_exhaustive attribute in Rust. Or the nonexhaustive attribute in Swift (or alternatively opting into library-evolution which reverses the defaults).


I disagree - the problem lies with the intention of exhaustively listing all available values in a switch expression with no backup plan. Switch expressions should always have a default/fallback branch, even if it's theoretically impossible and only logs the attempt/picks a default for future proofing the code and its uses.


> Switch expressions should always have a default/fallback branch,

This will nullify the advantage of being able to treat sealed classes as algebraic datatypes by using the exhaustiveness check.


Yes and no. You either accept that your code and the environment is final (in which case there's no problem and you can just blindly use the exhaustiveness check) or you accept that your code is a living thing that can change in part (including your edits, its dependencies, and its standard library), in which case you must be prepared for changes but can still make use of the exhaustiveness check by temporarily removing the default case.

I feel this is fundamentally a perspective issue. If this is a problem, also consider why people want sealed classes in the first place. It's always a tradeoff and you can also always fork or propose your own improved version of the concept and see if it has enough merit to gain traction. There's a million other footguns in the language that needs similar scrutiny, I'm just happy java isn't C/C++.


We could have both by introducing another keyword than "default" here.

In some cases you want "default" handling, in other cases it's a runtime detection of a code issue. E.g. you could add a "fallback" case which is the same as "default" yet if your code doesn't exhaustively handle all sealed class cases won't compile. This would still create a fallback, if your sealed class is widened later on, but still allow you to error out if it's already the case right now.


> You either accept

That is a false dichotomy. Some enumerations have reasons to likely change in the future, others (most) either have no such thing or their changing would be so impactful to the system it needs to be signaled loudly.

That’s really no different than any other type information.

> It's always a tradeoff and you can also always fork or propose your own improved version of the concept

You are the person asserting that the concept as defined is incorrect.


No, I assert that the concept as defined has a reasoning behind it that accepts certain tradeoffs for certain benefits. I think its perfectly valid to disagree with them, but I am curious about improvements to the current solution to the problem of extending types that really should not get extended (e.g. from the standard lib)


I’m glad you’ve fixed that issue. Hopefully you can fix the other design issues, including security weaknesses, I reported at the time [1]. I found the response rather dismissive and unhelpful, I have to say.

[1]: https://marc.info/?l=openjdk-security-dev&m=177435495515512&...


I saw this title and I immediately thought “SCCS”. Indeed, the article is a deep dive into the SCCS data structures. Cool!

I actually still use SCCS… sort of. I have some old code bases that are stored in SCCS. They’re actually Sun CodeManager/Teamware repositories, which used SCCS for file storage. I don’t actually update any files in SCCS; I use it only for browsing history. These old repos have been converted to git, but somehow I find it harder to trace through the history in the converted repo.

Instead, I use the open-sourced version of SCCS commands that the late Jörg Schilling ported to various OSes from the OpenSolaris/SVR4 source base. The guy was a real fan of SCCS. He even gave a conference talk on it once. Kind of retro computing, but for software instead of hardware.

https://news.ycombinator.com/item?id=28832682


Warming up a 2019-era (Intel) MacBook Pro was never my problem. Quite the opposite. Those machines ran notoriously hot. The later macOS releases, combined with company-mandated crapware, made it worse. Doing an ordinary build or starting a videoconferencing session was enough to cause the fans to run. On a warm day the fans couldn’t shed enough heat and so the system would go into thermal throttling. The OS would occupy a core with a 100% kernel_task that didn’t do any work but which would serve to prevent actual work from being scheduled onto that core. When four or five out of the six cores were occupied by kernel_task, I knew I was in for a bag of hurt (to steal a phrase from Steve Jobs). Responsiveness went completely to hell. The machine became effectively unusable.

After a while my normal procedure was to run with the thing sitting on top of an ice pack. That would let me run a 60-90 minute video conference without troubles.

The only redeeming feature of these machines is that they could emulate old x86 hardware at speed. That allowed me to run old apps on old OSes without having to keep old hardware running.


I had Windows and Mac laptops back then, and the HN snobbishness around the superiority of the Mac was genuinely baffling.

My i9 2019 MBP with discrete graphics was probably the worst laptop purchase I ever made. Docking it to an external monitor would enable the GPU, so even when idling it would run the fans and drain the battery.

I’d read cautionary tales about Windows laptops being pulled out of backpacks scorching hot as they failed to shut down. But that happened to my Mac all the time, too.

The M series though is incredible. I can’t imagine buying a Windows laptop now.


The i9 was notorious. Would thermally throttle almost instantly & for any sizeable build job would end up slower than the i7 IIRC.

Intel really made themselves unpopular with Apple during that period.


> Intel really made themselves unpopular with Apple during that period.

You can't tell me that this wasn't known by Apple before shipping the product. Why did they not provide adequate cooling for the CPU?


This was a laptop, so cooling was very constrained. The fans can only be so big & you can only shift so much air in & out of a MacBook.

I presume Apple knew perfectly well but wanted the halo product to sell to those people who will always pay extra for the perceived “top of the line” product. Once Intel branding had created an i9 that was a bigger number than an i7, then Apple was going to sell it.

It was faster than the i7 after all: just not for very long!

My entirely speculative theory is that the poor thermal characteristics of that era of Intel CPUs didn’t really become apparent until quite late in the development process & by that point Apple had probably committed to buying a fair chunk of Intel’s output.


> Intel really made themselves unpopular with Apple during that period.

Intel just reenacted IBM's history with Apple, particularly the G5 era. That CPU was instantly a no-go for anything mobile. In workstations it was cranked ever higher with very poor power-frequency scaling, needing water cooling for the beastly 200W idle power consumption and close to 1kW full throttle.

That went well so was a perfect role model for Intel's i9.


Oh yeah, I forgot about the graphics.

I had (and still have) a 4k external monitor. Naturally I wanted the MBP to drive the monitor with a resolution that took advantage of all the pixels. Unfortunately with most monitor settings the GPU power consumption would produce enough heat to run the fans even if the rest of the system was completely idle! If I set the output to full HD the GPU would cool down and the fans would turn off. But full HD on a 4k monitor is a waste.

It was very strange. I could drive the monitor at 4k but with the image upside down, and the power consumption would be low. But flip the image right side up and it would run hot and turn on the fans.

It took a couple weeks of fiddling, but I finally found a combination of refresh rate, resolution, image orientation (right side up!), and cabling that let me drive the monitor at high resolution without running the fans. What a pain.

(I used iStat menus to monitor GPU power consumption. At “good” settings it consumed about 5w. At “bad” settings it would consume 17w. At a bad setting you could immediately see the various temperatures go up and the fans spinning up to compensate.)


How could orientation and cabling affect heat??


I don't know, but I did observe differences in power consumption caused by all sorts of unexpected things.

At some point during all my fiddling with this stuff, I discovered a correlation between GPU power consumption (as indicated by iStat Menus) and fan speed. For example, if I switched to high resolution, the power would increase, and at low resolution, power would decrease. And of course more power means more heat, which causes the fans to run. That kind of made sense: moving more pixels consumes more power.

But I also observed an effect caused by refresh rate. Oddly, I seem to recall 30Hz would cause increased power draw but 60Hz the power draw was reduced. Yes, this seems counterintuitive. Since I didn't have a good model for what caused the increased power draw, I decided to try all combinations of the settings. On a lark I tried inverting the image and it actually affected the power draw! Probably has something to do with the access order of memory, but I don't really know. And indeed it seems odd that running the image upside down actually pulled less power than right side up.

The cabling issue was a bit more complicated. My initial configuration was to run the video through a dock, with the dock's video output going to the monitor. That always resulted in high power draw. Connecting the computer directly to the monitor did have the effect of reducing power draw... but only in certain configurations.


Changing orientation affects which parts of the display processor are used. In general, the low power states on GPUs are very capricious.

eg. from personal experience, on older Nvidia cards (1080 Ti), just adding +1Hz to the refresh rate (or using a slightly higher resolution, or 10-bit color, or no DSC etc. etc.) caused it to never enter low power mode, changing power consumption permanently from 10W -> 50W.

AMD GPUs on even the most modern Linux kernels don't enter low power states properly. If you do change the power profile to do it more often, browsers become a laggy mess.


Apple hardware quality on the laptops was bottom tier during the 2016-2019 "butterfly" era. There's no denying.


It was a truly ridiculous idea to put an i9 in any laptop. That generation of i9 is difficult to cool even with liquid cooler systems in big ATX cases.


Just to remind, the mobile + desktop lines are not the same thing even if they both get the same branding.

The top end chip in the 2019 MBP was the i9-9980HK, which had a TDP of 45W.

Would have been reasonable in a chunkier workstation/gaming type of laptop with the kinds of cooling solutions those usually get, it's not something that needed an actual desktop liquid cooling solution to run well.

But obviously the 2019 MBP design/cooling was not up to that task.


My Intel MBP would noticeably raise the whole room's temperature, while the fans ran so loud. We had some corporate security software that would occasionally go haywire and lock up 100% of a core until you rebooted. If you got that at the same time as a video call it would become too physically painful to touch any part of the metal body with bare skin.


i decided to do an experiment and try to run an LLM in my old 2013 MBP. i7, 16 gb mem, 1 tb hd.

Installed Linux mint Xfce Edition for lightness, installed ollama, start to test different models. Gemma4 e4b runs perfectly fine, exposed it to the network, connected to it with my current notebook and use vs code codex to start to run inference.

For about 30 minutes of bliss, this setup work at a reasonable speed... then the MBP shut it self down. It was so hot that it trigger the safety mechanism, the fans sounded like the laptop was about to take off.

I though on leaving it on inside the fridge, but then the WIFI wouldn't reach.

On the other hand, my wife saw all this and offer to buy me an M5... the experiment didn't work as intended, but it did work.


On a laptop that old it might be worth opening it up to blow all the dust out with a compressor or air duster. I’ve often found that to work wonders on old MacBook Pros.

The other issue is that unless the battery has been replaced relatively recently its charging efficiency may not be that great and the high load being placed on it might be causing it to get hotter than it would have done when new.


I have personally replaced the battery a couple of times already... but a good clean up is a good idea


Give the fans a really good going over: tend to find loads of dust and fluff hiding inside them.


Could be in need of a thermal paste refresh etc.?


and redo the thermal paste


> On the other hand, my wife saw all this and offer to buy me an M5...

Hold my beer. I'm going to run Qwen on this 3rd-generation iPod... somewhere my partner can see


i can sell you my MBP, but no refunds if it doesn't work

hahaha


Those were John Ive era laptops where form ruled function. Poor thermal, less ports, less keys were all features.


Form always ruled function with Jony Ive, but he always had a good eye for the way compromises shook out. During that era, Ive was creatively checked out but Cook kept him on to maintain the stock price.


Maybe the same type. Each time I call the LLM api the fan starts to work and make big noise. The temperature in the room is going up noticeably for 1-2 degrees.


> Each time I call the LLM api the fan starts to work and make big noise

So every time you do HTTP calls? Nothing there should spin up your fans, unless you use an agent with an horribly broken TUI, I've heard there is a few of those out there. But remotely calling LLM APIs really shouldn't be taxing on your local device, something somewhere is wrong/bad if that's what you're seeing.


If the horribly broken TUI you mentioned is OpenCode, I’d say yes. That’s exactly what I am experiencing.


Sure, if that's what you're using, then that's definitively buggy, unless it's doing compilation or something actually using your resources, just making HTTP calls shouldn't be heavy for your computer. Claude Code was mainly what I was thinking about, as it similarly broken, but I'm sure there are more out there as most of them seem vibe-coded at best.


Yes. Whatever *code, the same when they are working. The node.js backend is awful.


Is it a local LLM? Sibling seems to be assuming remote, but I have trouble imagining a TUI that inefficient.


No. Simply the rest api call in opencode tui. I don’t know why maybe the mbp is too old, at least it served 6 years +.


Not counting compilation passes, the rest of OpenCode is trivial enough that it should work on a 1980s PC.


It’s just so weird.


Part 2 shows this comment from the Linux TCP code:

    /* As outlined in RFC 2525, section 2.17, we send a RST here because
     * data was lost. To witness the awful effects of the old behavior of
     * always doing a FIN, run an older 2.1.x kernel or 2.0.x, start a bulk
     * GET in an FTP client, suspend the process, wait for the client to
     * advertise a zero window, then kill -9 the FTP client, wheee...
     * Note: timeout is always zero in such a case.
     */
Ok, so the RST is explained and well justified by the literature. But what are the “awful effects” of sending FIN instead? Can someone explain?


> But what are the “awful effects” of sending FIN instead? Can someone explain?

The RFC explains.

The other side - the server - will be stuck on `send(sock_fd, more_bytes...)`. If it's the 90s and your FTP server is single-threaded, that means the server will appear completely stuck. This won't resolve for several minutes, or possibly forever if the server-side TCP stack is buggy or lacks timeouts.

The client's connection, even after the process is gone, will still be alive in the kernel. It will be in FIN-WAIT-2, waiting for the server to send its FIN. The remote sender will be stuck waiting for the zero-window state to pass, sending probes to figure out when the receive window opens (which will never happen). Things will be stuck in this state until one of the sides times out, which could be several minutes. Or if the implementations are buggy, it could be permanent.

From RFC 2525, 2.17

      Failure to reset the connection can lead to permanently hung
      connections, in which the remote endpoint takes no further action
      to tear down the connection because it is waiting on the local TCP
      to first take some action.  This is particularly the case if the
      local TCP also allows the advertised window to go to zero, and
      fails to tear down the connection when the remote TCP engages in
      "persist" probes (see example below).

https://datatracker.ietf.org/doc/html/rfc2525#page-50


Thanks for your explanation; it’s very helpful.


The difference between RST and FIN shows up in read() as an ECONNRESET vs end-of-file reading 0.

In some protocols, end-of-file has semantic meaning that all data has been transferred, and TCP is set up such that you should be able to rely on that - if you can't rely on that difference, it is a bug in a TCP stack along the way.

FIN also has a sequence number, so you can wait to ACK it until you get the corresponding data if it is dropped or out of order.

TCP RST says the other side won't be resending if not ACKed, it is reset. Further, the downloading client usually cannot even read any packets in the receive window either once an RST has been received - that might be hundreds of KB of missed data.

RST and FIN are very semantically meaningfully different.

Reading the post, if gunicorn is e.g. sending a 404 after seeing a POST to a path it doesn't know about before reading the body, the client will never get the 404 because gunicorn hasn't read the message body.

This case is partly why "Expect: 100-continue" exists, so it will be properly handled, even if it does introduce an extra round-trip lag in the POST.

It might be dangerous to have your protocol rely on a piece of TCP that is often incorrectly implemented.


Client sees a clean disconnect and I guess assumes thats the entire file?


The client has been SIGKILLed, so it’s not assuming anything. I wonder whether the comment is a typo and they meant to kill -9 the server instead.


Perhaps a permanently hung connection because timeout is zero (=disabled?)?


Seems plausible since FIN only means “I’m done sending” also called a “half close”.

FTP has different data and command connections so the server may not have an outstanding read to detect the data connection break.

But.. it should still clean up both when the command connection dies


Hypothetically if this was HTTP without a Content-length (like it used to be in the olden days), you could have a proxy server assume this is the entire file.


I rewatched this recently; I think it held up well. It’s probably re-entered the zeitgeist given the recent developments in “AI” and “agents”. So far accidents seem to have destroyed only data, but it’s only a matter of time before some fool hooks up an “AI agent” to a missile.

Much of the film was shot at the Lawrence Hall of Science in the hills above Berkeley, California. This building was probably chosen because of its unique brutalist hexagonal architecture. I spent a bunch of time there as a kid.

Eric Braeden (Forbin) is still alive, but his house in Pacific Palisades burned down in the 2025 fires. :-(


I took another look at L.H.S. and the buildings have rather more octagons than hexagons. There’s even a decagon-shaped building. Still, the symmetric geometry of the architecture is quite striking.


Oh! This is a great explanation, thanks. I remember your original exchange (and I found it baffling and uncharacteristic), and I remember the William Shatner SNL Trek convention sketch, but I never made the connection between them.


I believe the definitive analysis of the Therac-25 incident was written by Nancy Leveson, first in IEEE Computer,[1] and later as an appendix of her book.[2] The appendix is freely available as a PDF on the web [3][4] and probably other places. Many people here are asking questions about what happened and how it came about. The answers to many of these questions can be found there. I strongly recommend that anyone who is serious about safety and wants to learn more about this incident read Leveson’s analysis.

[1]: N. G. Leveson and C. S. Turner, "An investigation of the Therac-25 accidents," in Computer, vol. 26, no. 7, pp. 18-41, July 1993.

[2]: Nancy Leveson. Safeware: System Safety and Computers. Addison-Wesley, 1995.

[3]: http://sunnyday.mit.edu/papers/therac.pdf

[4]: https://web.mit.edu/6.033/2014/wwwdocs/papers/therac.pdf


Thanks for posting this. The Leveson article is IMO the definitive study on this incident. I’m going to repost at top level for emphasis.


You encountered this book in 01996? Is that around the time of the Norman Conquest?

I'm assuming you're using octal here. Myself, I haven't used octal since 03677.

:-)

I see you mentioned https://interlisp.org/ ; while it's not a Lisp machine, the Medley Interlisp Project aims to recreate the Interlisp environment that ran on Xerox D-machines up through the 1980s or so. Still very interesting.


kragen should be applauded for getting in early when it comes to the Y10K problem. (-:

* https://cr.yp.to/y2k.html


I'm older than I look.


Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: