~mil/sxmo-devel

This thread contains a patchset. You're looking at the original emails, but you may wish to use the patch review UI. Review patch
1

[PATCH wayout] refactor reading stdin

Details
Message ID
<20221006201016.88627-1-aren@peacevolution.org>
DKIM signature
pass
Download raw message
Patch: +78 -61
Motivation: the desktop widget is consistently one minute behind in
sxmo, I believe this is the root cause.

If more than one line is available when poll returns, getline will only
read the first line and ignore the rest. Poll believes we have read all
of it and won't notify again.

This includes a handful of closely related changes:

* replace getline with read, this should fix the original problem

* --feed-delimiter now takes a regex, and doesn't match full line by
  default. Note: this is a breaking change to get the old behavior -d
  '^old param value$' can be used.

* exit if stdin receives eof, previously it would just stop reading it

* make sure to handle cases where multiple frames are read at once, or
  the start of the next frame is read. Both should be very rare anyway
  given wayout's intended use case.
---
 src/wayout.c | 136 ++++++++++++++++++++++++++++-----------------------
 src/wayout.h |   3 +-
 2 files changed, 78 insertions(+), 61 deletions(-)

diff --git a/src/wayout.c b/src/wayout.c
index ef51b92..95ffe9a 100644
--- a/src/wayout.c
+++ b/src/wayout.c
@@ -162,6 +162,18 @@ static void finish_wayland (struct App *app)
	wl_display_disconnect(app->display);
}

void set_delimiter(char *delim, struct App * app)
{
	printlog(app, 2, "Compiling delimiter regex: [%s]", delim);
	int err = regcomp(&app->delimiter, delim, REG_NEWLINE);
	if (err) {
		char errbuf[512];
		regerror(err, &app->delimiter, errbuf, sizeof(errbuf));
		printlog(app, 0, "ERROR: invalid regex: %s");
		exit(EXIT_FAILURE);
	}
}

static bool handle_command_flags (struct App *app, int argc, char *argv[])
{

@@ -191,7 +203,7 @@ static bool handle_command_flags (struct App *app, int argc, char *argv[])
		"  -w, --no-wrap                   Disable wordwrap\n"
		"  -l, --feed-line                 Each line delimits the input\n"
		"  -p, --feed-par                  Empty lines delimit the input\n"
		"  -d, --feed-delimiter [line]     A custom delimiter delimits the input\n"
		"  -d, --feed-delimiter [regex]    A custom delimiter delimits the input\n"
		"  -i, --interval [ms]             Poll interval to check for new input\n"
		"\n";

@@ -351,15 +363,15 @@ static bool handle_command_flags (struct App *app, int argc, char *argv[])
				return false;
			}
		} else if (!strcmp(argv[i],"-l") || !strcmp(argv[i],"--feed-line")) {
            app->feed = true;
            app->delimiter = "";
			app->feed = true;
			set_delimiter("\n", app);
		} else if (!strcmp(argv[i],"-p") || !strcmp(argv[i],"--feed-par")) {
            app->feed = true;
            app->delimiter = "\n";
			app->feed = true;
			set_delimiter("\n\n", app);
		} else if (!strcmp(argv[i],"-d") || !strcmp(argv[i],"--feed-delimiter")) {
			if (i + 1 >= argc) goto error;
            app->feed = true;
            app->delimiter = argv[++i];
			app->feed = true;
			set_delimiter(argv[++i], app);
		} else if (!strcmp(argv[i],"-i") || !strcmp(argv[i],"--interval")) {
			if (i + 1 >= argc) goto error;
            app->interval = atoi(argv[++i]);
@@ -393,6 +405,58 @@ static bool handle_command_flags (struct App *app, int argc, char *argv[])
		return false;
}

char buffer[BUFFERSIZE];
char *bufferhead = (char*) &buffer;

// if the input buffer contains the delimiter, flush it to the display
bool try_shift_buffer(struct App * app) {
	regmatch_t pmatch[1];
	*bufferhead = 0;

	if (!regexec(&app->delimiter, buffer, 1, pmatch, REG_NOTEOL)) {
		printlog(app, 2, "Flushing buffer (size %d)\n", bufferhead - buffer);

		// copy the text to the display buffer
		if (app->text != NULL) free(app->text);
		app->text = strndup(buffer, pmatch[0].rm_so);

		// shift the input buffer back by the amount we copied
		// this is in case we read part of the next frame
		char *new_start = buffer + pmatch[0].rm_eo;
		memmove(buffer, new_start, bufferhead - new_start);
		bufferhead -= pmatch[0].rm_eo;

		app->require_update = true;
		return true;
	}

	return false;
}

void process_stdin(struct App *app)
{
	int ret;
	int buflen = bufferhead - buffer;

	ret = read(STDIN_FILENO, bufferhead, BUFFERSIZE - (buflen + 1));
	if (-1 == ret && errno != EAGAIN) {
		printlog(NULL, 0, "ERROR: read stdin: %s\n", strerror(errno));
		return;
	}
	bufferhead += ret;

	// Loop to make sure we display the latest version
	while (try_shift_buffer(app)) {}

	// if the buffer is full clear it and print an error
	if (bufferhead - buffer >= BUFFERSIZE - 1) {
		printlog(NULL, 0, "ERROR: input too long, resetting\n");
		bufferhead = buffer;

		// Make sure we didn't miss any data
		process_stdin(app);
	}
}


/* Timeout until next update */
@@ -472,8 +536,6 @@ static void app_run (struct App *app)
	fds[signal_fd].fd = -1;
#endif

	char buffer[BUFFERSIZE];
	char * bufferhead = (char*) &buffer;
	bool first_update = true; //first update is forced

	while (app->loop)
@@ -514,50 +576,14 @@ static void app_run (struct App *app)
			goto error;
		}

		size_t line_size;
		char *line = NULL;
		bool flushbuffer = false;

		if ( fds[stdin_fd].revents & POLLIN)
		{
			printlog(app, 2, "Processing stdin\n");
			if ( getline(&line, &line_size, stdin) != -1 ) {
				printlog(app, 2, "Read line (size=%d)\n", line_size);
				if (app->feed && strcmp(app->delimiter,"") == 0) {
					if (app->text != NULL) free(app->text);
					app->text = strdup(line);
					app->require_update = true;
				} else if (app->feed && strcmp(app->delimiter, line) == 0) {
					flushbuffer = true;
				} else {
					if ((bufferhead - buffer) + strlen(line) >= BUFFERSIZE) {
						printlog(app, 2, "Buffer size exceeded.. ignoring line\n");
					} else {
						strncpy(bufferhead, line, strlen(line));
						bufferhead += strlen(line);
					}
				}
				if (feof(stdin)) flushbuffer = true; //not sure this can actually happen here
			} else {
				printlog(app, 2, "No line to get\n");
				flushbuffer = true;
			}

		if (fds[stdin_fd].revents & POLLIN) {
			process_stdin(app);
		}

		if ((fds[stdin_fd].revents & POLLHUP))
		{
			//input pipe got closed, process all remaining input
			printlog(app, 2, "Input pipe got closed\n");
			while ( fgets(bufferhead, BUFFERSIZE - strlen(bufferhead), stdin) ) {
				bufferhead += strlen(bufferhead);
			}
			if (bufferhead != buffer) flushbuffer = true;

			//stop reading stdin
			fds[stdin_fd].events = 0;
			fds[stdin_fd].revents = 0;
			fds[stdin_fd].fd = -1;
			printlog(app, 1, "[main] Got end of input, exiting\n");
			goto exit;
		}

		if ( fds[timer_fd].revents & POLLIN)
@@ -567,16 +593,6 @@ static void app_run (struct App *app)
			read(fds[timer_fd].fd, &elapsed, sizeof(elapsed));
		}

		if ((flushbuffer) && (bufferhead != buffer)) {
			printlog(app, 2, "Flushing buffer (size %d)\n", bufferhead - buffer);
			if (app->text != NULL) free(app->text);
			*bufferhead = 0;
			app->text = strdup(buffer);
			bufferhead = (char*) &buffer;
			*bufferhead = 0;
			app->require_update = true;
		}

#ifdef HANDLE_SIGNALS
		/* Signal events. */
		if ( fds[signal_fd].revents & POLLIN )
@@ -646,7 +662,7 @@ int main (int argc, char *argv[])
	app.wordwrap = true;
	app.center = false;
	app.font_pattern = "Monospace 26";
	app.delimiter = "";
	set_delimiter("\n", &app);
	set_string(&app.namespace, "wayout");
	app.border_bottom = app.border_top
		= app.border_left = app.border_right = 0;
diff --git a/src/wayout.h b/src/wayout.h
index ff4395b..e794ad5 100644
--- a/src/wayout.h
+++ b/src/wayout.h
@@ -1,6 +1,7 @@
#ifndef WLCLOCK_WLCLOCK_H
#define WLCLOCK_WLCLOCK_H

#include<regex.h>
#include<stdbool.h>
#include<stdint.h>
#include<time.h>
@@ -45,7 +46,7 @@ struct App

	bool feed;
	int32_t interval;
	char *delimiter;
	regex_t delimiter;

	struct Draw_colour background_colour;
	struct Draw_colour border_colour;
-- 
2.37.3
Details
Message ID
<20221007183717.5ki43mdjkpn4rhri@worker.anaproy.lxd>
In-Reply-To
<20221006201016.88627-1-aren@peacevolution.org> (view parent)
DKIM signature
missing
Download raw message
Thanks, this looks very promising! However, the patch doesn't cleanly
apply to the latest git master tree, I'm not sure if you have other changes
in your tree or if you are missing something. Could you rebase and resend perhaps?

-- 

Maarten van Gompel
Reply to thread Export thread (mbox)