37d0105c83
This change only affects clients that are subscribed to events, which should be the main cause of our problems. In the common case (no buffered data) the behaviour doesn't change at all: the message is sent directly, no ev_io / ev_timeout callback is enabled. Once a write to a client's socket is not completed fully (returns with EAGAIN error), we put the message in the tail of a queue and init an ev_io callback and a corresponding timer. If the timer is triggered first, the socket is closed and the client connection is removed. If the socket becomes writeable before the timeout we either reset the timer if we couldn't push all the buffered data or completely remove it if everything was pushed. We could also replace ipc_send_message() for all client connections in i3, not just those subscribed to events. Furthermore, we could limit the amount of messages stored and increase the timeout (or use multiple timeouts): eg it's ok if a client is not reading for 10 seconds and we are only holding 5KB of messages for them but it is not ok if they are inactive for 5 seconds and we have 30MB of messages held. Closes #2999 Closes #2539
113 lines
2.6 KiB
C
113 lines
2.6 KiB
C
/*
|
|
* vim:ts=4:sw=4:expandtab
|
|
*
|
|
* i3 - an improved dynamic tiling window manager
|
|
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
|
|
*
|
|
*/
|
|
#include "libi3.h"
|
|
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <unistd.h>
|
|
#include <stdio.h>
|
|
#include <err.h>
|
|
#include <errno.h>
|
|
|
|
/*
|
|
* The s* functions (safe) are wrappers around malloc, strdup, …, which exits if one of
|
|
* the called functions returns NULL, meaning that there is no more memory available
|
|
*
|
|
*/
|
|
void *smalloc(size_t size) {
|
|
void *result = malloc(size);
|
|
if (result == NULL)
|
|
err(EXIT_FAILURE, "malloc(%zd)", size);
|
|
return result;
|
|
}
|
|
|
|
void *scalloc(size_t num, size_t size) {
|
|
void *result = calloc(num, size);
|
|
if (result == NULL)
|
|
err(EXIT_FAILURE, "calloc(%zd, %zd)", num, size);
|
|
return result;
|
|
}
|
|
|
|
void *srealloc(void *ptr, size_t size) {
|
|
void *result = realloc(ptr, size);
|
|
if (result == NULL && size > 0)
|
|
err(EXIT_FAILURE, "realloc(%zd)", size);
|
|
return result;
|
|
}
|
|
|
|
char *sstrdup(const char *str) {
|
|
char *result = strdup(str);
|
|
if (result == NULL)
|
|
err(EXIT_FAILURE, "strdup()");
|
|
return result;
|
|
}
|
|
|
|
char *sstrndup(const char *str, size_t size) {
|
|
char *result = strndup(str, size);
|
|
if (result == NULL)
|
|
err(EXIT_FAILURE, "strndup()");
|
|
return result;
|
|
}
|
|
|
|
int sasprintf(char **strp, const char *fmt, ...) {
|
|
va_list args;
|
|
int result;
|
|
|
|
va_start(args, fmt);
|
|
if ((result = vasprintf(strp, fmt, args)) == -1)
|
|
err(EXIT_FAILURE, "asprintf(%s)", fmt);
|
|
va_end(args);
|
|
return result;
|
|
}
|
|
|
|
ssize_t writeall(int fd, const void *buf, size_t count) {
|
|
size_t written = 0;
|
|
|
|
while (written < count) {
|
|
const ssize_t n = write(fd, buf + written, count - written);
|
|
if (n == -1) {
|
|
if (errno == EINTR || errno == EAGAIN)
|
|
continue;
|
|
return n;
|
|
}
|
|
written += (size_t)n;
|
|
}
|
|
|
|
return written;
|
|
}
|
|
|
|
ssize_t writeall_nonblock(int fd, const void *buf, size_t count) {
|
|
size_t written = 0;
|
|
|
|
while (written < count) {
|
|
const ssize_t n = write(fd, buf + written, count - written);
|
|
if (n == -1) {
|
|
if (errno == EAGAIN) {
|
|
return written;
|
|
} else if (errno == EINTR) {
|
|
continue;
|
|
} else {
|
|
return n;
|
|
}
|
|
}
|
|
written += (size_t)n;
|
|
}
|
|
return written;
|
|
}
|
|
|
|
ssize_t swrite(int fd, const void *buf, size_t count) {
|
|
ssize_t n;
|
|
|
|
n = writeall(fd, buf, count);
|
|
if (n == -1)
|
|
err(EXIT_FAILURE, "Failed to write %d", fd);
|
|
else
|
|
return n;
|
|
}
|