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

xtitle -s hangs from time to time #422

Open
lucas-mior opened this issue Apr 20, 2020 · 12 comments · May be fixed by #425
Open

xtitle -s hangs from time to time #422

lucas-mior opened this issue Apr 20, 2020 · 12 comments · May be fixed by #425

Comments

@lucas-mior
Copy link

lucas-mior commented Apr 20, 2020

xtitle -s -t 30 runs fine in a terminal, but in i3blocks it will eventually just freeze in some window title and not update itself any longer, requiring i3 to be restarted.

Another one is having the same issue: https://www.reddit.com/r/i3wm/comments/fsf26n/xtitle_in_i3blocks_hangs_in_bar/

My config:
[title]
command=xtitle -s -t 30
interval=persist

@jasontbradshaw
Copy link

jasontbradshaw commented May 3, 2020

I can confirm I'm having the same issue as well.

Running NixOS:

$ uname -a
Linux hostname 5.6.7-hardened #1-NixOS SMP Thu Apr 23 08:38:27 UTC 2020 x86_64 GNU/Linux

Interestingly, it seems to happen when switching tabs in Firefox. xtitle doesn't seem to be the problem since when run in a terminal it works fine, however after switching tabs in Firefox once or twice the xtitle -s configuration of my block simply... stops updating.

My full ~/.i3blocks.conf:

separator_block_width=30
command=~/bin/blocks/$BLOCK_NAME < /dev/null
markup=pango

[current-window]
command=xtitle -s
interval=persist

[bluetooth]
interval=60
signal=6

[wifi]
interval=15
signal=5

[battery]
interval=60
signal=4

[brightness]
interval=60
signal=2

[volume]
interval=60
signal=3

[redshift]
interval=120

[time]
interval=1

[hostname]
command=hostname
interval=once

Interestingly the Firefox output from xtitle -s is doubled, like so (switching between two tabs):

xtitle -s hangs from time to time · Issue #422 · vivien/i3blocks - Firefox Developer Edition
xtitle -s hangs from time to time · Issue #422 · vivien/i3blocks - Firefox Developer Edition
vivien/i3blocks: A feed generator for text based status bars - Firefox Developer Edition
vivien/i3blocks: A feed generator for text based status bars - Firefox Developer Edition

However, other windows don't display doubled output.

@zsugabubus
Copy link

Try the following.

However this whole thing is extremely fragile. If blocklet does not write the whole line at once, race condition may occur and parts of the line may get lost. Forever.

diff --git a/block.c b/block.c
index c24fb0a..d94ff25 100644
--- a/block.c
+++ b/block.c
@@ -94,18 +94,12 @@ static int block_stdout(struct block *block)
        const char *label, *full_text;
        int out = block->out[0];
        char buf[BUFSIZ];
-       size_t count;
        int err;
 
-       if (block->interval == INTERVAL_PERSIST)
-               count = 1;
-       else
-               count = -1; /* SIZE_MAX */
-
        if (block->format == FORMAT_JSON)
-               err = json_read(out, count, block->env);
+               err = json_read(out, -1, block->env);
        else
-               err = i3bar_read(out, count, block->env);
+               err = i3bar_read(out, -1, block->env);
 
        if (err && err != -EAGAIN)
                return err;

@zsugabubus zsugabubus linked a pull request May 3, 2020 that will close this issue
@Araly
Copy link

Araly commented May 4, 2020

The pull request #425 fixes the issues I had with xtitle

@lucas-mior
Copy link
Author

The PR works indeed, thanks!

@maxammann
Copy link

Happens for me too.

@nullscm
Copy link

nullscm commented Oct 17, 2020

please merge! Works for me too

@Konokuruma
Copy link

Use unbuffer xtitle -s (from the expect package). It’s the only thing that works for me.

@rhssk
Copy link

rhssk commented Mar 7, 2021

Another workaround (requires python-i3ipc):
blocks/title

#!/usr/bin/env python

import i3ipc


def print_window_title(container):
    print(container.name if container.name is not None else "", flush=True)


def on_window_focus(i3, e):
    print_window_title(e.container)


i3 = i3ipc.Connection()
print_window_title(i3.get_tree().find_focused())
i3.on("window::focus", on_window_focus)
i3.main()

i3blocks.conf:

[title]
command=blocks/title
interval=persist

@Rouji
Copy link

Rouji commented Mar 14, 2021

I've got this in my config and no combination of unbuffer/stdbuf/etc. keeps it from eventually getting stuck.

[active]
command=unbuffer swaymsg -t subscribe -m '["window"]' | jq --unbuffered -r 'select(.change=="focus" or .container.focused == true) | .container.name'
interval=persist

It's a shame this repo seems completely abandoned ...

@zsugabubus
Copy link

no combination of unbuffer/stdbuf/etc. keeps it from eventually getting stuck.

Yes, as I have said above there is no external workaround for this. If your program produces input faster or slower than i3blocks can read the lines, it breaks. No magical buffering mode exists. (Please do not waste your time.)

And again, it is not because xtitle does not flush its output. It flushes everything at the end of the line, so no characters get stuck at the side of xtitle that would require you to use no buffering.

https://github.com/baskerville/xtitle/blob/6f46584fe4ba2763438231e6cce5b180f5ca9c0e/xtitle.c#L212

So every "workaround" that replaces xtitle–and “hey guys it works”–makes no sense. Or at most a false sense of everything working correctly, because it is the same sh** than it was before.

@tiagovercosa
Copy link

Please merge this PR #425 ! Works for me too.

@vivien
Copy link
Owner

vivien commented Dec 28, 2022

It seems like xtitle flushes multiple lines at once, while i3blocks arguably expects one line at a time for persistent blocks, like what line buffered programs such as echo or printf shell commands do. We can reproduce the issue with a program like this:

#include <stdio.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
	int l = 0;

	for (;;) {
		l++;
		fprintf(stderr, "line %d\n", l);
		fprintf(stdout, "line %d\n", l);

		l++;
		fprintf(stderr, "line %d\n", l);
		fprintf(stdout, "line %d\n", l);

		l++;
		fprintf(stderr, "line %d\n", l);
		fprintf(stdout, "line %d\n", l);

		fflush(stdout);
		sleep(3);
	}

	return 0;
}

Here only one signal is sent every 3 lines. The first issue is that we get out of sync between what the block prints and what the bar displays. It may look correct because i3blocks will iterate over the lines and print "line 1", then "line 2", etc., while in fact we may expect to see "line 3", then "line 6", etc.

The second issue is that because 3 lines come in, but only one comes out, lines accumulate in the pipe until it eventually gets full (usually 65536 bytes long). Because we write more that we read, we will eventually block on a call to flush after a while. This is the issue encountered in #450 and #451.

The workaround for this as of 1.5 is to ensure that the program is line buffered or manually flushes one line at a time, otherwise we may pace its output ourselves for example with <command> | while read line ; do echo $line ; done. Line buffering can be changed externally with stdbuf -oL <command> but this won't work if the command tweaks its buffering mode itself.

That said, the only requirement we have for a block command is to provide newline terminated output, since it is the only reliable boundary that a program can provide to delimit the data to be used. Relying on incomplete lines or when the data is received is simply error prone. @zsugabubus actually had a good guess about draining the output, but the suggested PR has a few concerns so I will push a fix soon. Thanks guys for your patience on the issue!

vivien added a commit that referenced this issue Jan 13, 2023
i3blocks currently wrongly assumes that a persistent program will
be line buffered (or act as such) and flush one line at a time. In
this common scenario, it is safe to read a single line per signal,
but there are cases where this behavior is not not wanted.

For example, a program may print several lines at once, resulting in
updating only the first line, while filling up the pipe and eventually
have the block program hang on the next write operation.

To fix this, drain the pipe of a persistent block upon reception of
a signal, but line by line to avoid breaking non JSON blocks.

Also make sure to update a block only after having successfully read
its output, to avoid resetting a block on error such as on incomplete
line, or when the pipe was already flushed.

Closes #422
Refs #425
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

Successfully merging a pull request may close this issue.