Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

restarting i3 forgets i3bar as a zombie process #5756

Open
Virv12 opened this issue Nov 4, 2023 · 4 comments · May be fixed by #5909
Open

restarting i3 forgets i3bar as a zombie process #5756

Virv12 opened this issue Nov 4, 2023 · 4 comments · May be fixed by #5909
Labels
4.23 bug missing-log Read the CONTRIBUTING.md file for instructions

Comments

@Virv12
Copy link

Virv12 commented Nov 4, 2023

I'm submitting a…

[x] Bug
[ ] Feature Request
[ ] Documentation Request
[ ] Other (Please describe in detail)

Current Behavior

When restarting i3, a new i3bar process is spawned and the old one is left alive as a zombie.

Expected Behavior

The old i3bar process is correctly closed.

Reproduction Instructions

Press $mod+Shift+r to restart i3.

Environment

Output of i3 --moreversion 2>&-:

$ i3 --moreversion 2>&-
Binary i3 version:  4.23 (2023-10-29) © 2009 Michael Stapelberg and contributors
Running i3 version: 4.23 (2023-10-29) (pid 1156)
Loaded i3 config:
  /home/filippo/.config/i3/config (main) (last modified: Mon 23 Oct 2023 10:38:31 PM CEST, 1030139 seconds ago)

The i3 binary you just called: /usr/bin/i3
The i3 binary you are running: i3
Config file
# Clipboard
bindsym $mod+c exec --no-startup-id clipmenu -p 'Select clip:'
bindsym $mod+x exec --no-startup-id clipdel -d ".*" && echo -n | xclip -selection c && echo -n | xclip -selection b && notify-send "Clipboard deleted."

Wallpaper

exec --no-startup-id feh --bg-max --no-fehbg $(ls wallpapers/* | shuf -n 1)
bindsym $mod+p exec --no-startup-id feh --bg-max --no-fehbg $(ls wallpapers/* | shuf -n 1)

Audio

bindsym $mod+Ctrl+m exec pavucontrol
bindsym XF86AudioRaiseVolume exec --no-startup-id volumectl +5%
bindsym XF86AudioLowerVolume exec --no-startup-id volumectl -5%
bindsym Shift+XF86AudioRaiseVolume exec --no-startup-id volumectl +1%"
bindsym Shift+XF86AudioLowerVolume exec --no-startup-id volumectl -1%
bindsym XF86AudioMute exec --no-startup-id volumectl mute

Open applications on specific workspaces

assign [class="TelegramDesktop"] $ws4
assign [class="discord"] $ws4

i3 config file (v4)

Please see http://i3wm.org/docs/userguide.html for a complete reference!

Set mod key (Mod1=, Mod4=)

set $mod Mod4

set default desktop layout (default is tiling)

workspace_layout tabbed <stacking|tabbed>

Configure border style <normal|1pixel|pixel xx|none|pixel>

new_window pixel 1
new_float normal

Hide borders

hide_edge_borders none

change borders

#bindsym $mod+u border none
#bindsym $mod+y border pixel 1
#bindsym $mod+n border normal

Font for window titles. Will also be used by the bar unless a different font

is used in the bar {} block below.

font xft:URWGothic-Book 13
#font TamzenForPowerline 13

Use Mouse+$mod to drag floating windows

floating_modifier $mod

start a terminal

bindsym $mod+Return exec --no-startup-id st
bindsym $mod+Shift+Return exec --no-startup-id st -T st-floating
for_window [title="st-floating"] floating enable

kill focused window

bindsym $mod+Shift+q kill

start program launcher

bindsym $mod+d exec --no-startup-id rofi -show

Screen brightness controls

bindsym XF86MonBrightnessUp exec --no-startup-id "xbacklight -inc 5; notify-send 'brightness up'"
bindsym XF86MonBrightnessDown exec --no-startup-id "xbacklight -dec 5; notify-send 'brightness down'"
bindsym Shift+XF86MonBrightnessUp exec --no-startup-id "xbacklight -inc 1; notify-send 'brightness up'"
bindsym Shift+XF86MonBrightnessDown exec --no-startup-id "xbacklight -dec 1; notify-send 'brightness down'"

Start Applications

bindsym $mod+F2 exec firefox
bindsym $mod+F3 exec pcmanfm
bindsym Print exec --no-startup-id i3-scrot -d

bindsym F9 exec --no-startup-id i3-scrot -w

bindsym $mod+Shift+Print --release exec --no-startup-id i3-scrot -s

focus_follows_mouse no

change focus

bindsym $mod+h focus left
bindsym $mod+j focus down
bindsym $mod+k focus up
bindsym $mod+l focus right

alternatively, you can use the cursor keys:

bindsym $mod+Left focus left
bindsym $mod+Down focus down
bindsym $mod+Up focus up
bindsym $mod+Right focus right

move focused window

bindsym $mod+Shift+h move left
bindsym $mod+Shift+j move down
bindsym $mod+Shift+k move up
bindsym $mod+Shift+l move right

alternatively, you can use the cursor keys:

bindsym $mod+Shift+Left move left
bindsym $mod+Shift+Down move down
bindsym $mod+Shift+Up move up
bindsym $mod+Shift+Right move right

workspace back and forth (with/without active container)

workspace_auto_back_and_forth yes
bindsym $mod+b workspace back_and_forth
bindsym $mod+Shift+b move container to workspace back_and_forth; workspace back_and_forth

split orientation

bindsym $mod+semicolon split h;exec notify-send 'tile horizontally'
#bindsym $mod+v split v;exec notify-send 'tile vertically'
bindsym $mod+q split toggle

toggle fullscreen mode for the focused container

bindsym $mod+f fullscreen toggle

change container layout (stacked, tabbed, toggle split)

bindsym $mod+s layout stacking
bindsym $mod+w layout tabbed
bindsym $mod+e layout toggle split

toggle tiling / floating

bindsym $mod+Shift+space floating toggle

change focus between tiling / floating windows

bindsym $mod+space focus mode_toggle

toggle sticky

bindsym $mod+Shift+s sticky toggle

focus the parent container

bindsym $mod+a focus parent

move the currently focused window to the scratchpad

bindsym $mod+Shift+minus move scratchpad

Show the next scratchpad window or hide the focused scratchpad window.

If there are multiple scratchpad windows, this command cycles through them.

bindsym $mod+minus scratchpad show

Workspace names

to display names or symbols instead of plain workspace numbers you can use

something like: set $ws1 1:mail

set $ws2 2:

set $ws1 1
set $ws2 2
set $ws3 3
set $ws4 4
set $ws5 5
set $ws6 6
set $ws7 7
set $ws8 8
set $ws9 9

switch to workspace

bindsym $mod+1 workspace $ws1
bindsym $mod+2 workspace $ws2
bindsym $mod+3 workspace $ws3
bindsym $mod+4 workspace $ws4
bindsym $mod+5 workspace $ws5
bindsym $mod+6 workspace $ws6
bindsym $mod+7 workspace $ws7
bindsym $mod+8 workspace $ws8
bindsym $mod+9 workspace $ws9

Move focused container to workspace

bindsym $mod+Ctrl+1 move container to workspace $ws1
bindsym $mod+Ctrl+2 move container to workspace $ws2
bindsym $mod+Ctrl+3 move container to workspace $ws3
bindsym $mod+Ctrl+4 move container to workspace $ws4
bindsym $mod+Ctrl+5 move container to workspace $ws5
bindsym $mod+Ctrl+6 move container to workspace $ws6
bindsym $mod+Ctrl+7 move container to workspace $ws7
bindsym $mod+Ctrl+8 move container to workspace $ws8
bindsym $mod+Ctrl+9 move container to workspace $ws9

Move to workspace with focused container

bindsym $mod+Shift+1 move container to workspace $ws1; workspace $ws1
bindsym $mod+Shift+2 move container to workspace $ws2; workspace $ws2
bindsym $mod+Shift+3 move container to workspace $ws3; workspace $ws3
bindsym $mod+Shift+4 move container to workspace $ws4; workspace $ws4
bindsym $mod+Shift+5 move container to workspace $ws5; workspace $ws5
bindsym $mod+Shift+6 move container to workspace $ws6; workspace $ws6
bindsym $mod+Shift+7 move container to workspace $ws7; workspace $ws7
bindsym $mod+Shift+8 move container to workspace $ws8; workspace $ws8
bindsym $mod+Shift+9 move container to workspace $ws9; workspace $ws9

Open specific applications in floating mode

for_window [title="alsamixer"] floating enable border pixel 1
for_window [class="Pavucontrol"] floating enable
for_window [class="(?i)virtualbox"] floating enable border normal
for_window [class="Xfburn"] floating enable

switch to workspace with urgent window automatically

#for_window [urgent=latest] focus

reload the configuration file

bindsym $mod+Shift+c reload

restart i3 inplace (preserves your layout/session, can be used to upgrade i3)

bindsym $mod+Shift+r restart

Set shut down, restart and locking features

bindsym $mod+0 mode "$mode_system"
set $mode_system (l)ock, (e)xit, switch_(u)ser, (s)uspend, (h)ibernate, (r)eboot, (Shift+s)hutdown
mode "$mode_system" {
bindsym l exec --no-startup-id i3exit lock, mode "default"
bindsym s exec --no-startup-id i3exit suspend, mode "default"
bindsym u exec --no-startup-id i3exit switch_user, mode "default"
bindsym e exec --no-startup-id i3exit logout, mode "default"
bindsym h exec --no-startup-id i3exit hibernate, mode "default"
bindsym r exec --no-startup-id i3exit reboot, mode "default"
bindsym Shift+s exec --no-startup-id i3exit shutdown, mode "default"

# exit system mode: "Enter" or "Escape"
bindsym Return mode "default"
bindsym Escape mode "default"

}

Resize window (you can also use the mouse for that)

bindsym $mod+r mode "resize"
mode "resize" {

These bindings trigger as soon as you enter the resize mode

Pressing left will shrink the window’s width.

Pressing right will grow the window’s width.

Pressing up will shrink the window’s height.

Pressing down will grow the window’s height.

bindsym h resize shrink width 5 px or 5 ppt
bindsym j resize grow height 5 px or 5 ppt
bindsym k resize shrink height 5 px or 5 ppt
bindsym l resize grow width 5 px or 5 ppt

same bindings, but for the arrow keys

bindsym Left resize shrink width 10 px or 10 ppt
bindsym Down resize grow height 10 px or 10 ppt
bindsym Up resize shrink height 10 px or 10 ppt
bindsym Right resize grow width 10 px or 10 ppt

exit resize mode: Enter or Escape

bindsym Return mode "default"
bindsym Escape mode "default"

}

Autostart applications

#exec --no-startup-id nm-applet
exec --no-startup-id clipmenud

Color palette used for the terminal ( ~/.Xresources file )

Colors are gathered based on the documentation:

https://i3wm.org/docs/userguide.html#xresources

Change the variable name at the place you want to match the color

of your terminal like this:

[example]

If you want your bar to have the same background color as your

terminal background change the line 362 from:

background #14191D

to:

background $term_background

Same logic applied to everything else.

set_from_resource $term_background background
set_from_resource $term_foreground foreground
set_from_resource $term_color0 color0
set_from_resource $term_color1 color1
set_from_resource $term_color2 color2
set_from_resource $term_color3 color3
set_from_resource $term_color4 color4
set_from_resource $term_color5 color5
set_from_resource $term_color6 color6
set_from_resource $term_color7 color7
set_from_resource $term_color8 color8
set_from_resource $term_color9 color9
set_from_resource $term_color10 color10
set_from_resource $term_color11 color11
set_from_resource $term_color12 color12
set_from_resource $term_color13 color13
set_from_resource $term_color14 color14
set_from_resource $term_color15 color15

Start i3bar to display a workspace bar (plus the system information i3status if available)

bar {
i3bar_command i3bar
status_command i3status
position bottom

mode hide

please set your primary output first. Example: 'xrandr --output eDP1 --primary'

tray_output primary

tray_output eDP1

bindsym button4 nop
bindsym button5 nop

font xft:URWGothic-Book 11

strip_workspace_numbers yes

colors {
	background #222D31
	statusline #F9FAF9
	separator  #454947

border backgr. text

	focused_workspace  #F9FAF9 #16a085 #292F34
	active_workspace   #595B5B #353836 #FDF6E3
	inactive_workspace #595B5B #222D31 #EEE8D5
	binding_mode       #16a085 #2C2C2C #F9FAF9
	urgent_workspace   #16a085 #FDF6E3 #E5201D
}

}

hide/unhide i3status bar

bindsym $mod+m bar mode toggle

Theme colors

class border backgr. text indic. child_border

client.focused #556064 #556064 #80FFF9 #FDF6E3
client.focused_inactive #2F3D44 #2F3D44 #1ABC9C #454948
client.unfocused #2F3D44 #2F3D44 #1ABC9C #454948
client.urgent #CB4B16 #FDF6E3 #1ABC9C #268BD2
client.placeholder #000000 #0c0c0c #ffffff #000000

client.background #2B2C2B

#############################

settings for i3-gaps:

#############################

Set inner/outer gaps

#gaps inner 0
#gaps outer 0

Additionally, you can issue commands with the following syntax. This is useful to bind keys to changing the gap size.

gaps inner|outer current|all set|plus|minus

gaps inner all set 10

gaps outer all plus 5

Smart gaps (gaps used if only more than one container on the workspace)

smart_gaps on

Smart borders (draw borders around container only if it is not the only container on this workspace)

on|no_gaps (on=always activate and no_gaps=only activate if the gap size to the edge of the screen is 0)

smart_borders on

Press $mod+Shift+g to enter the gap mode. Choose o or i for modifying outer/inner gaps. Press one of + / - (in-/decrement for current workspace) or 0 (remove gaps for current workspace). If you also press Shift with these keys, the change will be global for all workspaces.

set $mode_gaps Gaps: (o) outer, (i) inner
set $mode_gaps_outer Outer Gaps: +|-|0 (local), Shift + +|-|0 (global)
set $mode_gaps_inner Inner Gaps: +|-|0 (local), Shift + +|-|0 (global)
bindsym $mod+Shift+g mode "$mode_gaps"

mode "$mode_gaps" {
bindsym o mode "$mode_gaps_outer"
bindsym i mode "$mode_gaps_inner"
bindsym Return mode "default"
bindsym Escape mode "default"
}
mode "$mode_gaps_inner" {
bindsym plus gaps inner current plus 5
bindsym minus gaps inner current minus 5
bindsym 0 gaps inner current set 0

bindsym Shift+plus  gaps inner all plus 5
bindsym Shift+minus gaps inner all minus 5
bindsym Shift+0     gaps inner all set 0

bindsym Return mode "default"
bindsym Escape mode "default"

}
mode "$mode_gaps_outer" {
bindsym plus gaps outer current plus 5
bindsym minus gaps outer current minus 5
bindsym 0 gaps outer current set 0

bindsym Shift+plus  gaps outer all plus 5
bindsym Shift+minus gaps outer all minus 5
bindsym Shift+0     gaps outer all set 0

bindsym Return mode "default"
bindsym Escape mode "default"

}

- Linux Distribution & Version: Archlinux
- Are you using a compositor (e.g., xcompmgr or compton): No

Tomorrow I will try to provide a log file.
Thanks for your help!

@i3bot i3bot added bug missing-log Read the CONTRIBUTING.md file for instructions labels Nov 4, 2023
@i3bot
Copy link

i3bot commented Nov 4, 2023

I don’t see a link to logs.i3wm.org. Did you follow https://i3wm.org/docs/debugging.html? (In case you actually provided a link to a logfile, please ignore me.)

@i3bot i3bot added the 4.23 label Nov 4, 2023
@orestisfl
Copy link
Member

The zombie processes clean themselves up after a while for me, however in 4.22 there were no zombie processes at all.

I ran a git bisect for this and it seems that: 3ae5f31 is the first bad commit

@kolayne can you take a look?

@kolayne
Copy link
Contributor

kolayne commented Nov 5, 2023

I can reproduce the issue. As far as I can tell, i3bar gets stuck in the Zombie state until any of i3's children terminates (I reckon this triggers that libev collects all the zombies).

Why

Before 3ae5f31, when the double forking logic was used, every child process of i3 was immediately reparented, so taking care of that process's zombieness was never i3's responsibility. After double forking was removed, child processes of i3 started dying, becoming zombies, and getting reaped by libev, regardless of how they were spawned and even regardless of whether they were spawned by this current i3 or one of its previous incarnations (previous processes which execed into the current one). That's why, normally, zombie processes don't appear even if one restarts i3 (and the old i3 is replaced with its new incarnation).

The problem now is a race condition: if a child of i3 is still alive when i3 is restarting but dies before i3 creates the libev event loop, the event of that child's termination is lost. This is what seems to be happening with i3bar.

How to fix

I see a couple of possible solutions, I am not sure which one fits best.

  1. Ostrich algorithm. The event of zombie processes left behind is unlikely (it only happens with processes that terminate at the very short period of time while the new (incarnation of) i3 is initializing, which, in a common setup, is only i3bar), and these zombies are not around for a long time, as they'll be reaped anyway as soon as any new child dies. In an individual case this can be worked around with a config directive such as

    exec_always --no-startup-id killall -SIGCHLD i3
    

    which cleans zombies up.

    What I dislike about this solution is that overall the behavior of i3 has degraded, although it's not very noticeable.

  2. Make sure all zombies are reaped when i3 starts. This can be achieved very easily and naturally by doing a raise(SIGCHLD) in the main of i3 before entering the event loop, which will make libev reap all previous zombies. Note that by design of libev, there is no race condition between raise(SIGCHLD) and entering the event loop. We don't have to enter the event loop before raising the signal but, rather, create the event loop. From ev(3):

    Libev grabs "SIGCHLD" as soon as the default event loop is initialised. This is necessary to guarantee proper behaviour even if the first child watcher is started after the child exits. The occurrence of "SIGCHLD" is recorded asynchronously, but child reaping is done synchronously as part of the event loop processing. Libev always reaps all children, even ones not watched.

    A flaw I see in this solution is that it changes the current behavior of i3: if I write my own wrapper for i3 that creates a child but doesn't reap it (so that my main process ends up with a zombie child) and then execs the current version of i3, then that zombie child wouldn't get reaped (not until another child of i3 died). On the one hand, it kind of makes sense, as that child is not i3's responsibility, on the other hand, there's no one else to reap that zombie anyway. Could there be anyone out there relying on the current behavior?..

  3. Finally, another option is to, on i3 restart, block the SIGCHLD signal first, then exec the new incarnation, then unblock the signal, which would cause the pending SIGCHLDs (if any) to be delivered to the new process. This maintains the behavior described in the previous point compatible with the current version of i3 but seems like an overcomplication.

@kolayne
Copy link
Contributor

kolayne commented Nov 5, 2023

Okay, after writing it all out, I now come to a conclusion that, perhaps, the second option makes more sense than the other two.

The following patch fixes it for me:

diff --git a/src/main.c b/src/main.c
index 6fae7e41..e294f50e 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1202,6 +1202,17 @@ int main(int argc, char *argv[]) {
      * when calling exit() */
     atexit(i3_exit);
 
+    /* There might be children who died before we initialized the event loop,
+     * e.g., when restarting i3 (see #5756).
+     * To not carry zombie children around, raise the signal to invite libev to
+     * reap them.
+     *
+     * Note that there is no race condition between raising the signal and
+     * entering the event loop below: the signal is just to notify libev that
+     * zombies might already be there. The child reaping will happen in the
+     * event loop anyway. */
+    (void)raise(SIGCHLD);
+    
     sd_notify(1, "READY=1");
     ev_loop(main_loop, 0);

kolayne added a commit to kolayne/i3 that referenced this issue Feb 7, 2024
One case when this might be useful is when i3 is restarted and there are
children that terminate after the previous i3 instance shut down but
before the new one set things up.

Fixes i3#5756
@kolayne kolayne linked a pull request Feb 7, 2024 that will close this issue
kolayne added a commit to kolayne/i3 that referenced this issue May 7, 2024
One case when this might be useful is when i3 is restarted and there are
children that terminate after the previous i3 instance shut down but
before the new one set things up.

Fixes i3#5756
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
4.23 bug missing-log Read the CONTRIBUTING.md file for instructions
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants