hyprwm / Hyprland

Hyprland is an independent, highly customizable, dynamic tiling Wayland compositor that doesn't sacrifice on its looks.
https://hyprland.org
BSD 3-Clause "New" or "Revised" License
21.91k stars 912 forks source link

CPU usage 100% attaching bash script to socket2 with exec-once #1564

Open ToppDev opened 1 year ago

ToppDev commented 1 year ago

Describe the bug

When attaching a bash script to the socket2 with the exec-once functionality, the CPU usage of Hyprland goes to 100% afterwards till the attached script is killed.

Running the bash script over the terminal is not causing any CPU problems.

Steps to reproduce

Steps to reproduce the behavior:

  1. Add to the hyprland.conf
    exec-once = ~/.config/hypr/attach
  2. Create the ~/.config/hypr/attach script and make it executable. The script is from the wiki but socat -t 1000 was added according to #1089 to survive the startup phase of Hyprland.

    #!/bin/sh
    
    function handle {
      if [[ ${1:0:10} == "focusedmon" ]]; then
        if [[ ${1:12:4} == "DP-1" ]]; then
          hyprctl keyword general:gaps_out 20
        else
          hyprctl keyword general:gaps_out 30
        fi
      fi
    }
    
    socat -t 1000 - UNIX-CONNECT:/tmp/hypr/$(echo $HYPRLAND_INSTANCE_SIGNATURE)/.socket2.sock | while read line; do handle $line; done
  3. Log out and in again (restarting Hyprland)
  4. CPU usage is at 100% for the process Hyprland till you kill the attached script, e.g. pkill attach

Expected outcome

CPU usage should not be affected.

Relevant logs and/or screenshots

CPU usage

                      MemB   Cpu%
─ Hyprland            221M    100
  └─ dbus-update-act    0B    0.0
─ attach (sh)         3.7M    0.0
  ├─ attach (sh)      2.2M    0.0
  └─ socat            2.6M    0.0

sudo strace -p $(pgrep Hyprland)

Extract from output (repeats forever) ``` epoll_wait(3, [{events=EPOLLIN, data={u32=2147487280, u64=140391743491632}}], 32, -1) = 1 recvmsg(61, {msg_namelen=0}, 0) = -1 EAGAIN (Resource temporarily unavailable) epoll_wait(3, [{events=EPOLLIN, data={u32=2147487280, u64=140391743491632}}], 32, -1) = 1 recvmsg(61, {msg_namelen=0}, 0) = -1 EAGAIN (Resource temporarily unavailable) epoll_wait(3, [{events=EPOLLIN, data={u32=2147487280, u64=140391743491632}}], 32, -1) = 1 recvmsg(61, {msg_namelen=0}, 0) = -1 EAGAIN (Resource temporarily unavailable) epoll_wait(3, [{events=EPOLLIN, data={u32=2147487280, u64=140391743491632}}], 32, -1) = 1 recvmsg(61, {msg_namelen=0}, 0) = -1 EAGAIN (Resource temporarily unavailable) epoll_wait(3, [{events=EPOLLIN, data={u32=2147487280, u64=140391743491632}}], 32, -1) = 1 recvmsg(61, {msg_namelen=0}, 0) = -1 EAGAIN (Resource temporarily unavailable) epoll_wait(3, [{events=EPOLLIN, data={u32=2147487280, u64=140391743491632}}], 32, -1) = 1 recvmsg(61, {msg_namelen=0}, 0) = -1 EAGAIN (Resource temporarily unavailable) epoll_wait(3, [{events=EPOLLIN, data={u32=2147487280, u64=140391743491632}}], 32, -1) = 1 recvmsg(61, {msg_namelen=0}, 0) = -1 EAGAIN (Resource temporarily unavailable) epoll_wait(3, [{events=EPOLLIN, data={u32=2147487280, u64=140391743491632}}], 32, -1) = 1 recvmsg(61, {msg_namelen=0}, 0) = -1 EAGAIN (Resource temporarily unavailable) epoll_wait(3, [{events=EPOLLIN, data={u32=2147487280, u64=140391743491632}}], 32, -1) = 1 recvmsg(61, {msg_namelen=0}, 0) = -1 EAGAIN (Resource temporarily unavailable) epoll_wait(3, [{events=EPOLLIN, data={u32=2147487280, u64=140391743491632}}], 32, -1) = 1 recvmsg(61, {msg_namelen=0}, 0) = -1 EAGAIN (Resource temporarily unavailable) epoll_wait(3, [{events=EPOLLIN, data={u32=2147487280, u64=140391743491632}}], 32, -1) = 1 recvmsg(61, {msg_namelen=0}, 0) = -1 EAGAIN (Resource temporarily unavailable) epoll_wait(3, [{events=EPOLLIN, data={u32=2147487280, u64=140391743491632}}], 32, -1) = 1 recvmsg(61, {msg_namelen=0}, 0) = -1 EAGAIN (Resource temporarily unavailable) epoll_wait(3, [{events=EPOLLIN, data={u32=2147487280, u64=140391743491632}}], 32, -1) = 1 recvmsg(61, {msg_namelen=0}, 0) = -1 EAGAIN (Resource temporarily unavailable) epoll_wait(3, [{events=EPOLLIN, data={u32=2147487280, u64=140391743491632}}], 32, -1) = 1 recvmsg(61, {msg_namelen=0}, 0) = -1 EAGAIN (Resource temporarily unavailable) epoll_wait(3, [{events=EPOLLIN, data={u32=2147487280, u64=140391743491632}}, {events=EPOLLIN, data={u32=1790239472, u64=942 9142517488}}], 32, -1) = 2 read(12, "\2\0\0\0 \0\0\0\20\2\265j\272U\0\0\30\7\0\0\36\267\t\0\0\0\0\0D\0\0\0"..., 1024) = 64 getpid() = 10791 getpid() = 10791 getpid() = 10791 getpid() = 10791 getpid() = 10791 getpid() = 10791 getpid() = 10791 getpid() = 10791 sched_yield() = 0 sched_yield() = 0 sched_yield() = 0 sched_yield() = 0 sched_yield() = 0 sched_yield() = 0 sched_yield() = 0 sched_yield() = 0 ```

Environment info

Additional context

Could be related to #1485 as a similar problem is described there.

vaxerski commented 1 year ago

what if you sleep 2 && myscript?

edit: nvm misread

ToppDev commented 1 year ago

I just tried sleep 10 && ~/.config/hypr/attach. Hyprland has low CPU usage while the sleep is running. Then as soon as the script starts, the CPU goes to 100%

droc12345 commented 1 year ago

What does it do with a reasonable timeout value (1000 seconds is a little over 15 minutes) for socat?

ToppDev commented 1 year ago

The default value I think is 0.5

vaxerski commented 1 year ago

can you please not offtopic here, thanks.

droc12345 commented 1 year ago

I was being ontopic, as the value of -t 1000 seems a tad high and is part of the problem as you're telling socat to try and stay alive, even though it thinks it should quit.

If it was me trying to debug the problem I'd put echo statements inside "handle" so that you can see what it's looking at, and I'd turn on socat with -v (verbose) to see if you get more info.

Have fun with it.

beni69 commented 1 year ago

I've been having the same problem and I've accidentally discovered a detail, I'm not 100% sure that's the issue here, but it might be.

For me, Hyprland CPU usage jumps to 100% only when something is sent to the socket. (And it calms down only when the connection is closed.) It's perfectly fine as long as I'm only reading from it. I think that's also why it doesn't happen if you run the script from the console. But maybe there is something on stdin if you run it with exec-once?? I'm not sure.

A workaround I found, was to use socat in unidirectional mode, preventing it from sending anything to the socket with one of these flags, depending on the order of the arguments:

-u     unidirectional mode (left to right)
-U     unidirectional mode (right to left)
vaxerski commented 1 year ago

exec-once executes /bin/sh -c "WAYLAND_DISPLAY=<env> DISPLAY=<env> <command>"

Hm. I can't repro this myself, tho I am using a nest and not a real session.

beni69 commented 1 year ago

I just opened a terminal and ran

socat -v unix-connect:/tmp/hypr/$HYPRLAND_INSTANCE_SIGNATURE/.socket2.sock -

CPU usage was fine... then I pressed enter, which sent a message to the socket and suddenly one core jumped to 100% usage by Hyprland

vaxerski commented 1 year ago

should be fixed with e4e653ada6fc729efad3f6a0d49cf72b94c43b6c, verify

Alan-Kuan commented 1 year ago

I still encountered the same issue on version 0.24.1-1. However, I tried the solution provided by @beni69 and found that it worked.

A workaround I found, was to use socat in unidirectional mode, preventing it from sending anything to the socket with one of these flags, depending on the order of the arguments:

-u     unidirectional mode (left to right)
-U     unidirectional mode (right to left)

The following is my script that will be executed by exec-once on startup. Without the -U option, the CPU usage will reach about 100%.

#!/usr/bin/env bash

function handle() {
    if [[ ${1:0:12} == "monitoradded" ]]; then
        sleep 0.5
        eww -c ~/.config/eww/bar close bar0
        eww -c ~/.config/eww/bar open bar1
    elif [[ ${1:0:14} == "monitorremoved" ]]; then
        sleep 0.5
        eww -c ~/.config/eww/bar open bar0
    fi
}

SOCK2="/tmp/hypr/`echo $HYPRLAND_INSTANCE_SIGNATURE`/.socket2.sock"
socat -t 100 -U - "UNIX-CONNECT:$SOCK2" | while read line; do handle $line; done
ToppDev commented 1 year ago

Same for me, issue is still there on newest git version, but adding -U solves it. I guess it's up to the devs. Either update the wiki with the -U option, or try to fix it. Both seems acceptable to me.

vaxerski commented 1 year ago

wut, in another issue a dude said it got fixed. odd

beni69 commented 1 year ago

Interesting, the commit did seem to fix the case I described, that being opening the socket interactively in a terminal and sending something to it, but I've had the same findings: running it in a script still gets the CPU running. I have no clue why, but it has to be related to the input of the socket, since it's fixed by the -u or -U flag.

romanstingler commented 1 year ago

@beni69 this should be fixed now, or?

ToppDev commented 1 year ago

I am not sure how the Wiki is built, but https://wiki.hyprland.org/Configuring/Expanding-functionality/ has still the old example without the -U option

beni69 commented 1 year ago

I am not sure how the Wiki is built, but https://wiki.hyprland.org/Configuring/Expanding-functionality/ has still the old example without the -U option

My PR changed the example on the page titled "IPC" and that's been up ever since, I guess I must've forgotten to change this example as well.

NikkSaan commented 8 months ago

I propose a better example:

socat -u "UNIX-CONNECT:/tmp/hypr/$HYPRLAND_INSTANCE_SIGNATURE/.socket2.sock" STDOUT | while read -r line; do handle "$line"; done

I believe this conveys the intention of the one-liner much better - socket in -> std out. Not to mention it solves all the problems we've been having. This works even without the -u which in this example serves to suppress a small warning. I've tested this for a week. No issues!

An even better proposal would be to add a listen option to hyprctl and get rid of socat all together. Like so..

hyprctl listen | while read -r line; do handle "$line"; done

sooo much cleener. :)

--

Fedora 39, Hyprland v0.37.1.

JassonCordones commented 3 weeks ago

is this still a thing?