maxdamantus
Joined 8 years ago
Joined 8 years ago
fwiw, Xorg already had this, since you can set the DPI for each display through RandR/xrandr. In both X11 and Wayland it's up to the toolkit to actually detect the setting and rasterise accordingly.
Wayland actually went backwards in this respect by using "integer scales" (eg, 1, 2, 3) instead of fine-grained DPIs (eg, 96, 192, 288), so using a scale of 1.5 would result in downscale blur (toolkit sees scale as 2, then the compositor scales it down to 75%), whereas in Xorg you could just set the DPI to 144, and the toolkit could theoretically render at the correct resolution. As far as I know Qt was the only toolkit to actually do this automatically, but that's not X11's fault.
Wayland has at least since fixed this in the form of "fractional scaling" [1], but here's [0] an old thread on HN where I complained about it and provided screenshots of the resulting blur.
[0] https://news.ycombinator.com/item?id=32021261
[1] Doing some quick searching it seems like this is still unsupported in Gtk3/Gtk4, maybe planned for Gtk5? Apparently Firefox has only just added support (December 2025), 3 years after the fractional scaling protocol was released. Seems ridiculous to me that Wayland failed to get this right from the start.
This.
> What I can imagine is a purpose-built CPU that would make the JIT's job a lot easier and faster than compiling for x86 or ARM. Such a machine wouldn't execute raw Java bytecode, rather, something a tiny bit more low-level.
My prediction is that eventually a lot of software will be written in such a way that it runs in "kernel mode" using a memory-safe VM to avoid context switches, so reading/writing to pipes, and accessing pages corresponding to files reduces down to function calls, which easily happen billions of times per second, as opposed to "system calls" or page faults which only happen 10 or 20 million times per second due to context switching.
This is basically what eBPF is used for today. I don't know if it will expand to be the VM that I'm predicting, or if kernel WASM [1] or something else will take over.
From there, it seems logical that CPU manufacturers would provide compilers ("CPU drivers"?) that turn bytecode into "microcode" or whatever the CPU circuitry expects to be in the CPU during execution, skipping the ISA. This compilation could be done in the form of JIT, though it could also be done AOT, either during installation (I believe ART in Android already does something similar [0], though it currently emits standard ISA code such as aarch64) or at the start of execution when it finds that there's no compilation cache entry for the bytecode blob (the cache could be in memory or on disk, managed by the OS).
Doing some of the compilation to "microcode" in regular software before execution rather than using special CPU code during execution should allow for more advanced optimisations. If there are patterns where this is not the case (eg, where branch prediction depends on runtime feedback), the compilation output can still emit something analogous to what the ISAs represent today. The other advantage is of course that CPU manufacturers are more free to perform hardware-specific optimisations, because the compiler isn't targeting a common ISA.
Anyway, these are my crazy predictions.
[0] https://source.android.com/docs/core/runtime/jit-compiler
[1] https://github.com/wasmerio/kernel-wasm (outdated)
As far as I can tell this is a transliterator, not a translator. It's just turning latin letters into hieroglyphs as you type them. I don't know how accurate the transliteration is.
It would be like coming up with a sequence of Chinese characters that sounds like an English sentence when pronounced by a Mandarin speaker. Nothing really to do with translation.
I guess Chinese characters work similarly, where eg, each character has a particular sound in Mandarin (with some characters having the same sound), but you spell words using certain characters based on the (sometimes historical) semantic association of components (radicals) within each character.
I'll admit I'm not an expert in either system, so sorry if either description seems like an oversimplification (I'm pretty sure there are exceptions in both cases).
This also leads to one of my favourite tables on Wikipedia [0], showing correspondences between various scripts, including Egyptian hieroglyphs and Arabic/Hebrew. Not all hieroglyphs are included, but you can see that each letter in Arabic/Hebrew ultimately derives from some hieroglyph which would have had a similar sound. The name of the Arabic letter ع sounds the same as the Arabic word for "eye" (ʿayn, عين) and the corresponding hieroglyph also looks like an eye.
[0] https://en.wikipedia.org/wiki/Phoenician_alphabet#Table_of_l...
Handling can be added to change the transaction to be read-only if the inner code throws a particular exception, but the consistency should still be checked through a `commit` phase (at least in an OCC setting), so the `continue` in `finally` is still the correct way to do it.
> could be expressed in a clearer manner by having it within the `try` block as described by IntelliJ here[0].
> 0 - https://www.jetbrains.com/help/inspectopedia/ContinueOrBreak...
Wrong link? The only solution I see there is to add a comment to suppress the warning, which sounds fine to me (eg, analogous to having a `// fallthrough` comment when intentionally omitting `break` statements within `switch`, since I can agree that both of these things are uncommon, but sometimes desirable).
Setting a transaction to read-only on error is possible using the code (using a rethrowing catch within the transaction), but this is not universally desirable.
If you're using transactions to run fairly arbitrary code atomically (assuming no static state outside of the transaction), the expected behaviour would be that modifications leading up to an exception (in a non-anomalous transaction) are still persisted. Eg, imagine the code within the transaction is updating a persisted retry counter before performing a fallible operation. In this case you want the counter to be updated so that the transaction doesn't just fail an infinite number of times, since each time you roll back on error you're just restoring the state that leads to the error.
Another case would be where the exception is due to something that was written within the transaction. If the exception were raised but the writes were not persisted, it would at least be confusing seeing the exception, and possibly logically incorrect depending on how the irrelevant exception is handled (since it's due to data that theoretically doesn't exist).
if (!tx.commit()) { continue; }
you do: try { conn.commit(); } catch (SQLTransactionRollbackException e) { continue; }
and the principle still applies. The simplest solution still involves a `continue` within the `finally` block.Whether it's a good idea to actually do this directly using SQL connections is another question .. SQL databases usually use pessimistic locking, where the transaction failures are actually "deadlocks" that are preferably avoided through careful ordering of operations within the transaction (or more commonly, YOLOing it using an isolation level that allows read anomalies). Without going into all the details, this probably has a large influence over the design of the SQL APIs you're referring to.
That's why it's denoted by a non-exceptional return value from `tx.commit()` in my sample code. When I've talked about exceptions here, I'm talking about exceptions raised within the transaction. If the transaction succeeds, those exceptions should be propagated to the calling code.
> Exceptions are for unexpected events such as the node running out of memory, or a process failing to write to disk.
Discussing valid uses of exceptions seems orthogonal to this (should OOM lead to a catchable exception [0], or should it crash the process?). In any case, if the process is still alive and the transaction code determines without error that "yes, this transaction was invalid due to other contending transactions", it should retry the transaction. If something threw due to lack of memory or disk space, chances are it will throw again within a successful transaction and the error will be propagated.
[0] As alluded to in my first post, you might want to add some special cases for exceptions/errors that you want to immediately propagate instead of retrying. Eg, you might treat `Error` subtypes differently, which includes `OutOfMemoryError` and other cases that suggest the program is in a potentially unusable state, but this still isn't required according to the intent of the transactional logic.
You can add extra logging to show results or exceptions within the transaction if you want (for the exception this would simply be a `catch` just before the `finally` that logs and rethrows).
I've omitted these extra things because it's orthogonal to the point that the simplest way to express this logic is by having the `continue` control flow unconditional on whether the code was successful .. which is what you use `finally` for.
If you did this in Rust noone would complain, since the overall result is expressed as a first-class `Result<T, E>` value that can naturally be discarded. This is why Rust doesn't have `finally`.
Rust is also a lot more permissive about use of control flow, since you can write things like `foo(if x { y } else { continue }, bar)`.
Personally, I prefer when the language gives a bit more flexibility here. Of course you can write things that are difficult to understand, but my stance is still that my example code above is the simplest way to write the intended logic, until someone demonstrates otherwise.
I don't think this is a restriction that generally helps with code quality. If anything I've probably seen more bad code due to a lack of finding the simplest way to express control flow of an algorithm.
I'm sure there's some train of thought that says that continue/break/return from a loop is bad (see proponents of `Array.prototype.forEach` in JS), but I disagree with it.
> The finally block always executes when the try block exits. This ensures that the finally block is executed even if an unexpected exception occurs.
The intent of the transaction code is that the consistency is checked (using `tx.commit()`) "even if an unexpected exception occurs".
I'm not sure how else to interpret that to be honest. If you've got a clearer way of expressing this, feel free to explain.
EDIT: actually, the PEP points out that they intend for it to only be a warning in CPython, to avoid the breaking change
> The projects examined contained a total of 120,964,221 lines of Python code, and among them the script found 203 instances of control flow instructions in a finally block. Most were return, a handful were break, and none were continue.
I don't really write a lot of Python, but I do write a lot of Java, and `continue` is the main control flow statement that makes sense to me within a finally block.
I think it makes sense when implementing a generic transaction loop, something along the lines of:
<T> T executeTransaction(Function<Transaction, T> fn) {
for (int tries = 0;; tries++) {
var tx = newTransaction();
try {
return fn.apply(tx);
} finally {
if (!tx.commit()) {
// TODO: potentially log number of tries, maybe include a backoff, maybe fail after a certain number
continue;
}
}
}
}
In these cases "swallowing" the exception is often intentional, since the exception could be due to some logic failing as a result of inconsistent reads, so the transaction should be retried.The alternative ways of writing this seem more awkward to me. Either you need to store the result (returned value or thrown exception) in one or two variables, or you need to duplicate the condition and the `continue;` behaviour. Having the retry logic within the `finally` block seems like the best way of denoting the intention to me, since the intention is to swallow the result, whether that was a return or a throw.
If there are particular exceptions that should not be retried, these would need to be caught/rethrown and a boolean set to disable the condition in the `finally` block, though to me this still seems easier to reason about than the alternatives.
I guess the "third" program would be something like xrandr, so the Wayland analogue to that would be wlr-randr (for wlroots compositors), or some other DE-specific tool for configuring screen sizes. Again there's no fundamental difference here.
[0] https://wayland.app/protocols/fractional-scale-v1#wp_fractio...