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

Command socket does not close if wait for answer fails #21

Open
svensp opened this issue Jan 6, 2025 · 3 comments
Open

Command socket does not close if wait for answer fails #21

svensp opened this issue Jan 6, 2025 · 3 comments

Comments

@svensp
Copy link

svensp commented Jan 6, 2025

If hyprland takes longer than the specified 0.5 seconds to respond to a command sent via the CommandSocket the socket will stay open and the next use of send_command will crash the program.

For me this happens when listening for the monitorremoved event which I added in main...svensp:hyprpy:main
and then using instance.get_monitors() inside the handler.
Actual code where the error appears: https://gitlab.com/asterkeks/hyprmonitors/-/blob/main/main.py?ref_type=heads

I don't have a lot of python experience but the following fix works for me: b83da96

@ulinja
Copy link
Owner

ulinja commented Jan 9, 2025

Hi @svensp!
It seems odd that the hyprland socket would take longer than 500ms to respond to an IPC command. It should usually instantly respond with data.
The 500ms are just a grace period to avoid calling CommandSocket.wait() without any timeout parameter, which would lead to the Instance.get_monitors() call to hang indefinitely if somehow no data gets returned. No data being returned within that time is unexpected, a SocketError indicating that no data came back is correct behaviour in this case.

That's not to say that there is no hyprpy bug there, but I don't have an external monitor currently, so I am unable to test this myself at the moment.
In your instance, I suspect that either no data gets returned at all by the socket when you call Instance.get_monitors(), or that it takes longer than 500ms. Could you check what happens when you increase the timeout to something longer, e.g. self.wait(10)?

What is the value of response = self.read() in your case? If the call to read() returns nothing at all, that would indicate that the Hyprland socket was closed externally for some reason:

When a recv returns 0 bytes, it means the other side has closed (or is in the process of closing) the connection. You will not receive any more data on this connection. Ever.

If this is the case, then hyprpy should probably also be made to raise an error when you call read() on a socket that was closed by the other side.

@svensp
Copy link
Author

svensp commented Jan 20, 2025

Hi @ulinja,
I've played around a bit and a raising the timeout caused the error to no longer happen. So I made it print the timings on how long a request took. One of the tries:

start and end are time.time(): print( command + ": " "{:.2f}".format(end - start) )

workspaces: 0.00
dispatch: 0.00
dispatch: 0.00
dispatch: 0.00
dispatch: 0.00
workspaces: 0.08
Traceback (most recent call last):
  File "/home/speckmaier/Projekte/hyprmonitors/main.py", line 49, in on_createworkspace
    workspace = next(
                ^^^^^
StopIteration
monitors: 0.00
mainline monitors [<Monitor(id=0, name='eDP-1', width=2880, height=1800)>]
left monitor <Monitor(id=0, name='eDP-1', width=2880, height=1800)>
center monitor <Monitor(id=0, name='eDP-1', width=2880, height=1800)>
right monitor <Monitor(id=0, name='eDP-1', width=2880, height=1800)>
workspaces: 0.00
workspaces: 0.70
dispatch: 0.00
monitors: 0.00
mainline monitors [<Monitor(id=0, name='eDP-1', width=2880, height=1800)>, <Monitor(id=2, name='DP-1', width=1920, height=1080)>, <Monitor(id=1, name='HDMI-A-1', width=1920, height=1080)>]
left monitor <Monitor(id=0, name='eDP-1', width=2880, height=1800)>
center monitor <Monitor(id=2, name='DP-1', width=1920, height=1080)>
right monitor <Monitor(id=1, name='HDMI-A-1', width=1920, height=1080)>
workspaces: 0.00
dispatch: 0.00
dispatch: 0.00
dispatch: 0.00
dispatch: 0.00
dispatch: 0.00
workspaces: 0.00
monitors: 0.00

Another one that would've cause the error was at 0.67 seconds.

Simply adding and removing a single monitor does not seem to cause it with my code but adding or removing both external monitors seems to cause it consistent-ish. Wild guess: some kind of mutex in hyprland preventing the workspaces ipc command from being answered while the workspaces are being moved around.

Right, my Hyprland version:

Hyprland 0.45.2 built from branch v0.45.2-b at commit 12f9a0d0b93f691d4d9923716557154d74777b0a  ([gha] Nix: update inputs).
Date: 2024-11-19
Tag: v0.45.2, commits: 12f9a0d0b93f691d4d9923716557154d74777b0a
built against aquamarine 0.4.4


flags set:
debug

Basically hyprland from nix home-manager

@roastedramen
Copy link

seeing the same issue here:

Traceback (most recent call last):
  File "/nix/store/4yzpdhnbxyd9n0hnzr64ywsibxa6k1jj-hyprland-monitor-workspaces", line 100, in <module>
    reorganize_workspaces(instance)
  File "/nix/store/4yzpdhnbxyd9n0hnzr64ywsibxa6k1jj-hyprland-monitor-workspaces", line 56, in reorganize_workspaces
    monitors = instance.get_monitors().sort(key=cmp_to_key(compare_monitors))
               ^^^^^^^^^^^^^^^^^^^^^^^
  File "/nix/store/y7mf839msfyb8dmq96kmpmqwl25xb8kh-python3-3.12.8-env/lib/python3.12/site-packages/hyprpy/components/instances.py", line 173, in get_monitors
    monitors_data = json.loads(self.command_socket.send_command('monitors', flags=['-j']))
                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/nix/store/y7mf839msfyb8dmq96kmpmqwl25xb8kh-python3-3.12.8-env/lib/python3.12/site-packages/hyprpy/utils/sockets.py", line 273, in send_command
    self.wait(0.5)
  File "/nix/store/y7mf839msfyb8dmq96kmpmqwl25xb8kh-python3-3.12.8-env/lib/python3.12/site-packages/hyprpy/utils/sockets.py", line 172, in wait
    raise SocketError(f"Waiting socket timed out after {timeout} seconds.")
hyprpy.utils.sockets.SocketError: Waiting socket timed out after 0.5 seconds.

This occurs when I run instance.get_monitors() in the handler of a monitoradded/monitorremoved event.

Using Hyprpy 0.1.10 and Hyprland 0.46.2 on nixos

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants