Activating Action causes SIGSEGV #298

Closed
opened 2025-11-10 14:04:20 +01:00 by Nyeksenn · 10 comments
Nyeksenn commented 2025-11-10 14:04:20 +01:00 (Migrated from github.com)

I have two Actions defined like so:

entries.add(new ActionEntry(
        "play-album",
        (action, parameter) -> {
            exec.submit(() -> {
                var albumId = parameter.getString(null);
                var album = new QAlbumModel().albumId.equalTo(albumId).findOne();
                if (album != null) {
                    var songs = album.getSongs();
                    //songQueue.replace(songs);
                }
            });
        },
        Variant.string("").getTypeString(),
        null,
        null
));
entries.add(new ActionEntry(
        "queue-album",
        (action, parameter) -> {
            exec.submit(() -> {
                var albumId = parameter.getString(null);
                var album = new QAlbumModel().albumId.equalTo(albumId).findOne();
                if (album != null) {
                    var songs = album.getSongs();
                    songQueue.addSongList(songs);
                }
            });
        },
        Variant.string("").getTypeString(),
        null,
        null
));

addActionEntries(entries.toArray(ActionEntry[]::new), null);

If I want to call the play-album action I get a SIGSEGV
It's called like so:

var action = win.lookupAction("play-album");
if (action != null) {
    action.activate(Variant.string(albumId));
}

However, if I call my queue-album action, this doesn't happen.
Logging confirms, that the activation handler isn't even being called.

C  [libgobject-2.0.so.0+0x24aba]
C  [libgobject-2.0.so.0+0x26af6]
C  [libgobject-2.0.so.0+0x26d68]  g_signal_emit_valist+0x38
C  [libgobject-2.0.so.0+0x26e23]  g_signal_emit+0x93
C  [libgio-2.0.so.0+0xbabf5]
C  [libgio-2.0.so.0+0xad43f]  g_action_activate+0x5f
v  ~RuntimeStub::nep_invoker_blob 0x00007eff0bd9bb9d
J 7603 c1 jdk.internal.foreign.abi.DowncallStub+0x00007efe9f63c000.invoke(Ljava/lang/foreign/SegmentAllocator;Ljava/lang/foreign/MemorySegment;Ljava/lang/foreign/MemorySegment;Ljava/lang/foreign/MemorySegment;)V java.base@23 (214 bytes) @ 0x00007eff04ec5aa4 [0x00007eff04ec4b40+0x0000000000000f64]
J 7604 c1 java.lang.invoke.DirectMethodHandle$Holder.invokeStatic(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V java.base@23 (20 bytes) @ 0x00007eff04ec6e94 [0x00007eff04ec6ac0+0x00000000000003d4]
J 7602 c1 java.lang.invoke.LambdaForm$MH+0x00007efe9f63d400.invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V java.base@23 (51 bytes) @ 0x00007eff04ec46ac [0x00007eff04ec4160+0x000000000000054c]
J 7326 c1 java.lang.invoke.LambdaForm$MH+0x00007efe9f7cc000.invokeExact_MT(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V java.base@23 (24 bytes) @ 0x00007eff04e421e4 [0x00007eff04e41b80+0x0000000000000664]
j  org.gnome.gio.Action.activate(Lorg/gnome/glib/Variant;)V+106
j  io.github.Nyeksenn.grooveboat.ui.SongListPage.playAlbumClicked()V+47
I have two Actions defined like so: ```java entries.add(new ActionEntry( "play-album", (action, parameter) -> { exec.submit(() -> { var albumId = parameter.getString(null); var album = new QAlbumModel().albumId.equalTo(albumId).findOne(); if (album != null) { var songs = album.getSongs(); //songQueue.replace(songs); } }); }, Variant.string("").getTypeString(), null, null )); entries.add(new ActionEntry( "queue-album", (action, parameter) -> { exec.submit(() -> { var albumId = parameter.getString(null); var album = new QAlbumModel().albumId.equalTo(albumId).findOne(); if (album != null) { var songs = album.getSongs(); songQueue.addSongList(songs); } }); }, Variant.string("").getTypeString(), null, null )); addActionEntries(entries.toArray(ActionEntry[]::new), null); ``` If I want to call the `play-album` action I get a SIGSEGV It's called like so: ```java var action = win.lookupAction("play-album"); if (action != null) { action.activate(Variant.string(albumId)); } ``` However, if I call my `queue-album` action, this doesn't happen. Logging confirms, that the activation handler isn't even being called. ```bash C [libgobject-2.0.so.0+0x24aba] C [libgobject-2.0.so.0+0x26af6] C [libgobject-2.0.so.0+0x26d68] g_signal_emit_valist+0x38 C [libgobject-2.0.so.0+0x26e23] g_signal_emit+0x93 C [libgio-2.0.so.0+0xbabf5] C [libgio-2.0.so.0+0xad43f] g_action_activate+0x5f v ~RuntimeStub::nep_invoker_blob 0x00007eff0bd9bb9d J 7603 c1 jdk.internal.foreign.abi.DowncallStub+0x00007efe9f63c000.invoke(Ljava/lang/foreign/SegmentAllocator;Ljava/lang/foreign/MemorySegment;Ljava/lang/foreign/MemorySegment;Ljava/lang/foreign/MemorySegment;)V java.base@23 (214 bytes) @ 0x00007eff04ec5aa4 [0x00007eff04ec4b40+0x0000000000000f64] J 7604 c1 java.lang.invoke.DirectMethodHandle$Holder.invokeStatic(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V java.base@23 (20 bytes) @ 0x00007eff04ec6e94 [0x00007eff04ec6ac0+0x00000000000003d4] J 7602 c1 java.lang.invoke.LambdaForm$MH+0x00007efe9f63d400.invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V java.base@23 (51 bytes) @ 0x00007eff04ec46ac [0x00007eff04ec4160+0x000000000000054c] J 7326 c1 java.lang.invoke.LambdaForm$MH+0x00007efe9f7cc000.invokeExact_MT(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V java.base@23 (24 bytes) @ 0x00007eff04e421e4 [0x00007eff04e41b80+0x0000000000000664] j org.gnome.gio.Action.activate(Lorg/gnome/glib/Variant;)V+106 j io.github.Nyeksenn.grooveboat.ui.SongListPage.playAlbumClicked()V+47 ```
jwharm commented 2025-11-12 21:17:09 +01:00 (Migrated from github.com)

Based on the example code, I don't see any reason why "queue-album" would work and "play-album" wouldn't.

I created a simple application with two ActionEntries, and I can activate them without issues. Are you sure that the action entries added to the window and not to the application?

Can you remove the exec.submit() calls just to be completely sure that this isn't a concurrency issue?
For example, would this work?

entries.add(new ActionEntry(
        "play-album",
        (action, parameter) -> {
            System.out.println("play-album");
        },
        Variant.string("").getTypeString(),
        null,
        null
));
entries.add(new ActionEntry(
        "queue-album",
        (action, parameter) -> {
            System.out.println("queue-album");
        },
        Variant.string("").getTypeString(),
        null,
        null
));
Based on the example code, I don't see any reason why "queue-album" would work and "play-album" wouldn't. I created a simple application with two ActionEntries, and I can activate them without issues. Are you sure that the action entries added to the window and not to the application? Can you remove the `exec.submit()` calls just to be completely sure that this isn't a concurrency issue? For example, would this work? ```java entries.add(new ActionEntry( "play-album", (action, parameter) -> { System.out.println("play-album"); }, Variant.string("").getTypeString(), null, null )); entries.add(new ActionEntry( "queue-album", (action, parameter) -> { System.out.println("queue-album"); }, Variant.string("").getTypeString(), null, null )); ```
Nyeksenn commented 2025-11-13 14:37:02 +01:00 (Migrated from github.com)

I am adding them to the window.
I rewrote the code to use the proper VariantTypes.STRING
and got rid of the list.
Now every action triggers a SIGSEGV.

        ActionEntry[] entries = new ActionEntry[4];
        entries[0] = new ActionEntry(
                "play-song",
                (action, parameter) -> {
                    if (parameter != null) {
                        exec.submit(() -> {
                            logger.info("Playing this song:");
                            var songId = parameter.getString(null);
                            var song = new QSongModel().songId.equalTo(songId).findOne();
                            if (song != null) {
                                logger.info("{}", song.getTitle());
                                songQueue.replace(List.of(song));
                                playNew();
                            }
                        });
                    }
                },
                VariantTypes.STRING.dupString(),
                null,
                null
        );
...
        win.addActionEntries(entries, null);

Ignore that I am now directly calling the addActionEntries on win. I moved the
code to a different widget.
If I simply create SimpleAction instances and add them directly, it works without issues.

I am adding them to the window. I rewrote the code to use the proper `VariantTypes.STRING` and got rid of the list. Now every action triggers a SIGSEGV. ```java ActionEntry[] entries = new ActionEntry[4]; entries[0] = new ActionEntry( "play-song", (action, parameter) -> { if (parameter != null) { exec.submit(() -> { logger.info("Playing this song:"); var songId = parameter.getString(null); var song = new QSongModel().songId.equalTo(songId).findOne(); if (song != null) { logger.info("{}", song.getTitle()); songQueue.replace(List.of(song)); playNew(); } }); } }, VariantTypes.STRING.dupString(), null, null ); ... win.addActionEntries(entries, null); ``` Ignore that I am now directly calling the `addActionEntries` on `win`. I moved the code to a different widget. If I simply create `SimpleAction` instances and add them directly, it works without issues.
Nyeksenn commented 2025-11-13 14:52:29 +01:00 (Migrated from github.com)

It crashes here:

_result = (int) MethodHandles.gtk_widget_activate_action_variant.invokeExact(
                        handle(), 
                        (MemorySegment) (name == null ? MemorySegment.NULL : Interop.allocateNativeString(name, _arena)), 
                        (MemorySegment) (args == null ? MemorySegment.NULL : args.handle()));

I don't know whether it is relevant, but the debugger says that args is of type Variant with length 0.

It crashes here: ```java _result = (int) MethodHandles.gtk_widget_activate_action_variant.invokeExact( handle(), (MemorySegment) (name == null ? MemorySegment.NULL : Interop.allocateNativeString(name, _arena)), (MemorySegment) (args == null ? MemorySegment.NULL : args.handle())); ``` I don't know whether it is relevant, but the debugger says that args is of type Variant with length 0.
jwharm commented 2025-11-13 22:21:25 +01:00 (Migrated from github.com)

Thanks for the additional info. However, I still cannot reproduce the segfault. Can you please provide a complete program that I can run? If that's not feasible, then maybe the stack trace that leads to the gtk_widget_activate_action_variant will be helpful, because I have no idea where it's invoked.

Thanks for the additional info. However, I still cannot reproduce the segfault. Can you please provide a complete program that I can run? If that's not feasible, then maybe the stack trace that leads to the `gtk_widget_activate_action_variant` will be helpful, because I have no idea where it's invoked.
Nyeksenn commented 2025-11-14 18:55:39 +01:00 (Migrated from github.com)

Thanks for your help.
I finally got around to putting the code on GitHub.
However, while you could run it, it requires a user on a Navidrome instance to function.
But at least the full code can be inspected.
The following links point to the relevant section:

github.com/Nyeksenn/GrooveBoat@59ac174549/src/main/java/io/github/Nyeksenn/grooveboat/ui/MusicPlayer.java (L213)

github.com/Nyeksenn/GrooveBoat@59ac174549/src/main/java/io/github/Nyeksenn/grooveboat/ui/SongCard.java (L61)

Thanks for your help. I finally got around to putting the code on GitHub. However, while you could run it, it requires a user on a Navidrome instance to function. But at least the full code can be inspected. The following links point to the relevant section: https://github.com/Nyeksenn/GrooveBoat/blob/59ac17454913d95b6cf6bc215303814775af2822/src/main/java/io/github/Nyeksenn/grooveboat/ui/MusicPlayer.java#L213 https://github.com/Nyeksenn/GrooveBoat/blob/59ac17454913d95b6cf6bc215303814775af2822/src/main/java/io/github/Nyeksenn/grooveboat/ui/SongCard.java#L61
Nyeksenn commented 2025-11-14 18:59:03 +01:00 (Migrated from github.com)

This is the stacktrace:

stacktrace.txt

This is the stacktrace: [stacktrace.txt](https://github.com/user-attachments/files/23553112/stacktrace.txt)
jwharm commented 2025-11-15 11:38:35 +01:00 (Migrated from github.com)

Perhaps the arg parameter is disposed too soon.
When you add arg.ref(); directly after this line, does the crash still happen?

Perhaps the `arg` parameter is disposed too soon. When you add `arg.ref();` directly after [this line](https://github.com/Nyeksenn/GrooveBoat/blob/59ac17454913d95b6cf6bc215303814775af2822/src/main/java/io/github/Nyeksenn/grooveboat/ui/SongCard.java#L60), does the crash still happen?
Nyeksenn commented 2025-11-17 17:12:25 +01:00 (Migrated from github.com)

I tried you proposal but sadly it didn't work.
The stack trace remains the same.

I tried you proposal but sadly it didn't work. The stack trace remains the same.
jwharm commented 2025-11-17 19:32:37 +01:00 (Migrated from github.com)

Then perhaps the action entries are disposed too soon.

In the MusicPlayer class, when creating the action entries, specify the global arena so they will stay alive.
So when creating the ActionEntry:

new ActionEntry("queue-song", [callback], VariantTypes.STRING.dupString(), null, null);

add an extra parameter:

new ActionEntry("queue-song", [callback], VariantTypes.STRING.dupString(), null, null, Arena.global());

for all 4 entries.

ActionEntry is a simple C struct, and when you create one, Java-GI allocates memory for it. This is done with an arena. If you don't specifiy a specific arena, then by default Arena.ofAuto() is used. That arena disposes all allocated memory when the Java MemorySegment is garbage-collected. The MemorySegment is an instance field in the entries. But the entries are eligible for GC after the setupActions() method returns.

Then perhaps the action entries are disposed too soon. In the MusicPlayer class, when creating the action entries, specify the global arena so they will stay alive. So when creating the ActionEntry: ```java new ActionEntry("queue-song", [callback], VariantTypes.STRING.dupString(), null, null); ``` add an extra parameter: ```java new ActionEntry("queue-song", [callback], VariantTypes.STRING.dupString(), null, null, Arena.global()); ``` for all 4 entries. ActionEntry is a simple C struct, and when you create one, Java-GI allocates memory for it. This is done with an arena. If you don't specifiy a specific arena, then by default `Arena.ofAuto()` is used. That arena disposes all allocated memory when the Java MemorySegment is garbage-collected. The MemorySegment is an instance field in the entries. But the entries are eligible for GC after the `setupActions()` method returns.
Nyeksenn commented 2025-11-18 09:26:59 +01:00 (Migrated from github.com)

That fixed it. Thank you so much :)

That fixed it. Thank you so much :)
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
java-gi/java-gi#298
No description provided.