@bugaevc@floss.social avatar

bugaevc

@bugaevc@floss.social

Unix hacker. I do obscure and cursed things.

I hack on Darling, SerenityOS / Ladybird, GNU Hurd / glibc, wl-clipboard, Owl, etc.

I use GNOME, and contribute to freedesktop / GNOME projects sometimes (systemd, PipeWire, GLib, GTK, etc).

I like Rust and dislike Docker.

This profile is from a federated server and may be incomplete. Browse more on the original instance.

tbernard, to random
@tbernard@mastodon.social avatar

Trying something for this app: https://flathub.org/apps/com.jeffser.Alpaca

Side note: the app works surprisingly well on my fairly old laptop, I'm kind of impressed.

bugaevc,
@bugaevc@floss.social avatar

@tbernard LLM? 😟

bugaevc, to random
@bugaevc@floss.social avatar

Remember to hydrate, fellow humans 💧

zhuowei, to random

Taylor Swift, known for showing vulnerability in her lyrics, announces that each of her songs will receive its own CVE

bugaevc,
@bugaevc@floss.social avatar

@zhuowei CVE-2024-3401 (Taylor's version)

bugaevc, to random
@bugaevc@floss.social avatar
nnethercote, to random

This video has 3 million views. It deserves more.

ENTIRE FREEBIRD SOLO PLAYED ON HARMONICA!!! (Lynyrd Skynyrd)

https://www.youtube.com/watch?v=UtXP2-5IC2E

bugaevc,
@bugaevc@floss.social avatar

@nnethercote I misread FREEBIRD as FREEBSD 😁

brooke, to random
@brooke@bikeshed.vibber.net avatar

cat /dev/zero | gzip > strategic_zero_reserve.gz

bugaevc,
@bugaevc@floss.social avatar

@brooke useless use of cat!

alice, to random

So, CSS variable support has landed in GTK, and libadwaita now uses it. Note that it's super basic for now, it's just replacements for the old @colors. There will be more drastic changes in 2.0, but for now I need to keep backwards compatibility.

So, apps can now override them per widget, like on the screenshot (well, once the sdk updates, anyway)

And because why not, there are a few changes that variables enabled:

.error, .warning and .success style classes now also change the accent color respectively. For example, this means that a selectable label with .error style class will have a red selection as well, instead of blue.

The .opaque style class for buttons has been deprecated - instead, apps can simply use .suggested-action and override the accent color on it (or add .error/.warning/.success - that works too). This will also change the focus ring, and similarly, .destructive-action buttons have red focus rings now.

Apps are encouraged to migrate, but the old colors will keep working until 2.0. For example, @accent_color can be replaced with var(--accent-color) and so on, note the dashes in the names instead of underscores. See the docs for more info: https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/css-variables.html

bugaevc,
@bugaevc@floss.social avatar

@alice I'm guessing there's no public API to get a color variable's value for a widget programmatically, is there? Basically I'd want something like the existing Widget::get_color, but with a name argument.

Private
bugaevc,
@bugaevc@floss.social avatar

The perfect cover does not exi...

https://youtu.be/-gR9CpmEU7c

bugaevc, to random
@bugaevc@floss.social avatar

Let's look into C++ 20 coroutines 🧵

I assume you're already familiar with general ideas of coroutines, async/await, and CPS / state machine transformation from other languages.

bugaevc,
@bugaevc@floss.social avatar

The final suspend should really suspend the coroutine (so std::suspend_never won't do), perhaps notify someone that the coroutine is done, maybe post its return value somewhere (perhaps resume another coroutine that's co_await'ing...), and finally destroy the coroutine frame by calling handle.destroy(). It then returns, just like any other suspension point, to the last resumer (or the original caller, if the coroutine never suspended on any other suspension points).

bugaevc,
@bugaevc@floss.social avatar

Finally, it calls .final_suspend() on the promise (which must be noexcept, since it's too late to throw exceptions at this point) and co_await's the returned value. This mirrors the .initial_suspend() at the start.

bugaevc,
@bugaevc@floss.social avatar

When the coroutine completes, it either:

  • calls promise.return_value(value), for 'co_return expr();',
  • calls promise.return_void(), for 'co_return;' (or simply reaching the end of body),
  • calls promise.unhandled_exception() if it terminates by throwing an exception that it doesn't itself catch (notably, the exception doesn't fall out of the coroutine to the resumer).

In the implementation of these, you'd want to save the return value (or the exception info) somewhere, to be retrieved later.

bugaevc,
@bugaevc@floss.social avatar

From that first suspension on, the coroutine can be resumed via its handle. Whether you're expecting that to be done by the caller (and the return object holds a copy of the coroutine handle) or by whatever the coroutine is suspended on co_await'ing, is up to your design; both options are possible.

As said, std::coroutine_handle<> is a non-owning pointer; also resuming a completed or already running coroutine is UB. This is naturally a ripe area for ownership / UaF / memory unsafety issues.

bugaevc,
@bugaevc@floss.social avatar

Whether the coroutine suspends immediately on .initial_suspend(), or runs to its first real suspension point and suspends there, this is where it returns to the caller, and the value that it returns is the object created earlier with .get_return_object(). Once again, the caller may not even know that it's a coroutine that it has invoked, from its perspective it's just another function with a return value.

bugaevc,
@bugaevc@floss.social avatar

So, we have figured out what the promise type for a coroutine is, based on its signature. The first thing a coroutine does when invoked is it allocates (almost certainly, on the heap) its frame, which includes among other things an instance of the promise type. It then initializes the instance of the promise type, either with its default constructor, or with a constructor taking (a copy of) all of the coroutine's arguments.

bugaevc,
@bugaevc@floss.social avatar

The default implementation provides one specialization, which has promise_type = Ret::promise_type, meaning that if your coroutine's return type has a nested typename promise_type, that will be the promise type of the coroutine, unless you define your own specialization. This is meant for types like MyTask<T> or MyFuture<T> that are intended to be used as coroutines' return types.

bugaevc,
@bugaevc@floss.social avatar

The "promise type" is not explicit in the signature or implementation of a coroutine. It is determined from the signature: for a coroutine with signature Ret(Arg1, Arg2, Arg3), the "promise type" used is whatever typename std::coroutine_traits<Ret, Arg1, Arg2, Arg3>::promise_type is; you can define your own specializations to make it be your intended promise type.

bugaevc,
@bugaevc@floss.social avatar

When a function is implemented as a coroutine (because it contains co_ statements in its body), there is an important type that determines things about how it behaves, called the "promise type". Now, this is an example of very unfortunate naming, since this type has nothing to do with the notion of promise objects (aka futures or deferreds), nor with the existing std::promise<> type. A "coroutine policy type" or just "coroutine type" would have been a much better name, but it is what it is.

bugaevc,
@bugaevc@floss.social avatar

Now that we looked at suspending and resuming, let's see how a coroutine gets created in the first place.

You cannot tell from a signature alone whether a function is implemented as a coroutine or not (i.e. whether it has any co_ statements in its body). An external library could flip a function implementation between coroutine / not coroutine without breaking API or ABI. The code to invoke it on the caller side is the same, and you get back an instance of the return type in both cases.

bugaevc,
@bugaevc@floss.social avatar

There are a few knobs to this mechanism: you must also define bool .await_ready() that says whether the coroutine should suspend at all, and .await_suspend() can return a boolean or another coroutine handle to tail-resume in stead of void; also, you can override things to transform the operand of co_await, first into an "awaitable", and then into an "awaiter", and that's what the .await_*() methods would actually get called on (this is somewhat like IntoFuture, if you're keeping track).

bugaevc,
@bugaevc@floss.social avatar

A coroutine suspends by co_await'ing an awaitable value.

Specifically, it calls the .await_suspend(std::coroutine_handle<>) method on it, passing a handle to itself. The method should store the handle somewhere and arrange for something to call .resume() on it later, when the awaited value "is ready". For example, it could arrange for handle.resume() to be called by another thread, or in a later iteration of an event loop.

bugaevc,
@bugaevc@floss.social avatar

First thing: any function using any of co_return/co_await/co_yield in its body is a coroutine.

A coroutine can suspend its execution, saving its state into a "coroutine frame" (that's almost definitely heap-allocated), to be resumed later. std::coroutine_handle<> is a small wrapper around a pointer to a coroutine frame. It doesn't imply any ownership, can be cheaply copied around, and can be converted to a raw pointer and back using .address()/::from_address().

bugaevc,
@bugaevc@floss.social avatar
  1. You cannot just write code with it! For instance you cannot write something like this:

int async_callee() {
co_return 42;
}
int async_caller() {
int a = co_await async_callee();
co_return a + 35;
}

No, you need a bunch of library code/abstractions (that the standard library does not even provide!) to make something like that expressible.

bugaevc,
@bugaevc@floss.social avatar
  1. C++ 20 coroutines are just way overdesigned.

People say that the Rust async/await design is complicated (futures, tasks, polling, wakers, pinning, oh my!), but compared to C++ 20 coroutines, Rust design is quite straightforward.

bugaevc,
@bugaevc@floss.social avatar

Let me start by criticizing the C++ 20 design, compared to, say, async/await in Rust:

  1. Contrary to the famous C++ "zero overhead" principle, using coroutines pretty much requires pervasive heap allocations and indirect function calls.
  • All
  • Subscribed
  • Moderated
  • Favorites
  • JUstTest
  • mdbf
  • ngwrru68w68
  • tester
  • magazineikmin
  • thenastyranch
  • rosin
  • khanakhh
  • InstantRegret
  • Youngstown
  • slotface
  • Durango
  • kavyap
  • DreamBathrooms
  • megavids
  • tacticalgear
  • osvaldo12
  • normalnudes
  • cubers
  • cisconetworking
  • everett
  • GTA5RPClips
  • ethstaker
  • Leos
  • provamag3
  • anitta
  • modclub
  • lostlight
  • All magazines