summaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
Diffstat (limited to 'src/common')
-rw-r--r--src/common/backend.c12
-rw-r--r--src/common/backend.h1
-rw-r--r--src/common/loop.c173
-rw-r--r--src/common/timing.c9
-rw-r--r--src/common/utf8.c39
5 files changed, 203 insertions, 31 deletions
diff --git a/src/common/backend.c b/src/common/backend.c
index a6fa6f2..2220f1f 100644
--- a/src/common/backend.c
+++ b/src/common/backend.c
@@ -85,6 +85,18 @@ void yasl_display_dispatch(struct yasl_display *d)
d->ops->display_dispatch(d);
}
+int yasl_display_dispatch_timeout(struct yasl_display *d, int timeout_ms)
+{
+ if (!d) return -1;
+ if (d->ops->display_dispatch_timeout)
+ return d->ops->display_dispatch_timeout(d, timeout_ms);
+ if (d->ops->display_dispatch) {
+ d->ops->display_dispatch(d);
+ return 0;
+ }
+ return -1;
+}
+
void yasl_display_flush(struct yasl_display *d)
{
if (!d || !d->ops->display_flush)
diff --git a/src/common/backend.h b/src/common/backend.h
index 0294aed..c1d878b 100644
--- a/src/common/backend.h
+++ b/src/common/backend.h
@@ -19,6 +19,7 @@ struct yasl_backend_ops {
void (*display_close)(struct yasl_display *d);
uint32_t (*display_caps)(const struct yasl_display *d);
void (*display_dispatch)(struct yasl_display *d);
+ int (*display_dispatch_timeout)(struct yasl_display *d, int timeout_ms);
void (*display_flush)(struct yasl_display *d);
int (*display_modifiers)(const struct yasl_display *d);
void (*display_copy)(struct yasl_display *d, enum yasl_selection sel, const char *text);
diff --git a/src/common/loop.c b/src/common/loop.c
index 4c2da0d..ea9bee3 100644
--- a/src/common/loop.c
+++ b/src/common/loop.c
@@ -1,13 +1,16 @@
+#define _GNU_SOURCE
#define _POSIX_C_SOURCE 200809L
#include "yasl/yasl.h"
#include <errno.h>
+#include <fcntl.h>
#include <poll.h>
#include <stdlib.h>
+#include <string.h>
#include <time.h>
#include <unistd.h>
-#define YASL_MAX_FDS 64
+#define YASL_LOOP_INIT_CAP 32
typedef int (*yasl_loop_fd_fn)(int fd, short revents, void *ud);
@@ -20,11 +23,15 @@ struct yasl_fd_source {
};
struct yasl_loop {
- struct yasl_fd_source fds[YASL_MAX_FDS];
- struct pollfd pollfds[YASL_MAX_FDS];
+ struct yasl_fd_source *fds;
+ struct pollfd *pollfds;
int count;
+ int capacity;
int next_id;
bool running;
+ int wake_rfd;
+ int wake_wfd;
+ int wake_watch;
};
uint64_t yasl_loop_now_ns(void)
@@ -34,25 +41,95 @@ uint64_t yasl_loop_now_ns(void)
return (uint64_t)ts.tv_sec * 1000000000ull + (uint64_t)ts.tv_nsec;
}
+static int loop_wake_cb(int fd, short revents, void *ud)
+{
+ (void)revents;
+ (void)ud;
+ char buf[128];
+ ssize_t n;
+ while ((n = read(fd, buf, sizeof(buf))) > 0 || (n < 0 && errno == EINTR)) {}
+ return 0;
+}
+
struct yasl_loop *yasl_loop_create(void)
{
struct yasl_loop *l = calloc(1, sizeof(*l));
if (!l) return NULL;
+
+ l->capacity = YASL_LOOP_INIT_CAP;
+ l->fds = malloc((size_t)l->capacity * sizeof(*l->fds));
+ l->pollfds = malloc((size_t)l->capacity * sizeof(*l->pollfds));
+ if (!l->fds || !l->pollfds) {
+ free(l->fds);
+ free(l->pollfds);
+ free(l);
+ return NULL;
+ }
+
l->next_id = 1;
+ l->wake_rfd = -1;
+ l->wake_wfd = -1;
+
+ int p[2];
+#if defined(__linux__) && defined(O_CLOEXEC)
+ if (pipe2(p, O_CLOEXEC | O_NONBLOCK) == 0) {
+ l->wake_rfd = p[0];
+ l->wake_wfd = p[1];
+ }
+#else
+ if (pipe(p) == 0) {
+ int f0 = fcntl(p[0], F_GETFL, 0);
+ int f1 = fcntl(p[1], F_GETFL, 0);
+ if (f0 >= 0) fcntl(p[0], F_SETFL, f0 | O_NONBLOCK);
+ if (f1 >= 0) fcntl(p[1], F_SETFL, f1 | O_NONBLOCK);
+ fcntl(p[0], F_SETFD, FD_CLOEXEC);
+ fcntl(p[1], F_SETFD, FD_CLOEXEC);
+ l->wake_rfd = p[0];
+ l->wake_wfd = p[1];
+ }
+#endif
+
+ if (l->wake_rfd >= 0) {
+ l->wake_watch = yasl_loop_add_fd(l, l->wake_rfd, POLLIN, loop_wake_cb, l);
+ }
return l;
}
void yasl_loop_destroy(struct yasl_loop *l)
{
+ if (!l) return;
+ if (l->wake_watch > 0)
+ yasl_loop_del_fd(l, l->wake_watch);
+ if (l->wake_rfd >= 0)
+ close(l->wake_rfd);
+ if (l->wake_wfd >= 0)
+ close(l->wake_wfd);
+ free(l->fds);
+ free(l->pollfds);
free(l);
}
int yasl_loop_add_fd(struct yasl_loop *l, int fd, short events,
int (*fn)(int fd, short revents, void *ud), void *ud)
{
- if (!l || l->count >= YASL_MAX_FDS || fd < 0)
+ if (!l || fd < 0)
return -1;
+
+ if (l->count >= l->capacity) {
+ int new_cap = l->capacity ? l->capacity * 2 : YASL_LOOP_INIT_CAP;
+ struct yasl_fd_source *new_fds = realloc(l->fds, (size_t)new_cap * sizeof(*new_fds));
+ if (!new_fds) return -1;
+ l->fds = new_fds;
+
+ struct pollfd *new_pollfds = realloc(l->pollfds, (size_t)new_cap * sizeof(*new_pollfds));
+ if (!new_pollfds) return -1;
+ l->pollfds = new_pollfds;
+ l->capacity = new_cap;
+ }
+
int id = l->next_id++;
+ if (l->next_id <= 0) l->next_id = 1;
+
l->fds[l->count] = (struct yasl_fd_source){
.fn = fn, .ud = ud, .id = id, .fd = fd, .events = events,
};
@@ -92,7 +169,74 @@ int yasl_loop_set_fd_events(struct yasl_loop *l, int id, short events)
void yasl_loop_kick(struct yasl_loop *l)
{
- (void)l;
+ if (!l || l->wake_wfd < 0) return;
+ char b = 1;
+ ssize_t n;
+ do {
+ n = write(l->wake_wfd, &b, 1);
+ } while (n < 0 && errno == EINTR);
+}
+
+int yasl_loop_step(struct yasl_loop *l, int timeout_ms)
+{
+ if (!l) return -1;
+ if (l->count <= 0) {
+ if (timeout_ms > 0) {
+ struct timespec req = {
+ .tv_sec = timeout_ms / 1000,
+ .tv_nsec = (long)(timeout_ms % 1000) * 1000000L,
+ };
+ nanosleep(&req, NULL);
+ }
+ return 0;
+ }
+
+ int n;
+ do {
+ n = poll(l->pollfds, (nfds_t)l->count, timeout_ms);
+ } while (n < 0 && errno == EINTR);
+
+ if (n < 0) return -1;
+ if (n == 0) return 0;
+
+ int handled = 0;
+ int count = l->count;
+
+ struct yasl_fd_source sstack[32];
+ struct pollfd pstack[32];
+ struct yasl_fd_source *scopy = (count <= 32) ? sstack : malloc((size_t)count * sizeof(*scopy));
+ struct pollfd *pcopy = (count <= 32) ? pstack : malloc((size_t)count * sizeof(*pcopy));
+ if (!scopy || !pcopy) {
+ if (scopy && scopy != sstack) free(scopy);
+ if (pcopy && pcopy != pstack) free(pcopy);
+ return -1;
+ }
+
+ memcpy(scopy, l->fds, (size_t)count * sizeof(*scopy));
+ memcpy(pcopy, l->pollfds, (size_t)count * sizeof(*pcopy));
+
+ for (int i = 0; i < count; i++) {
+ if (pcopy[i].revents) {
+ short revents = pcopy[i].revents;
+ // Verify that the source was not deleted by an earlier callback in this step
+ bool valid = false;
+ for (int j = 0; j < l->count; j++) {
+ if (l->fds[j].id == scopy[i].id) {
+ l->pollfds[j].revents = 0;
+ valid = true;
+ break;
+ }
+ }
+ if (valid && scopy[i].fn) {
+ scopy[i].fn(scopy[i].fd, revents, scopy[i].ud);
+ handled++;
+ }
+ }
+ }
+
+ if (scopy != sstack) free(scopy);
+ if (pcopy != pstack) free(pcopy);
+ return handled;
}
int yasl_loop_run(struct yasl_loop *l)
@@ -100,20 +244,8 @@ int yasl_loop_run(struct yasl_loop *l)
if (!l) return -1;
l->running = true;
while (l->running) {
- int n = poll(l->pollfds, (nfds_t)l->count, -1);
- if (n < 0) {
- if (errno == EINTR) continue;
+ if (yasl_loop_step(l, -1) < 0)
return -1;
- }
- for (int i = 0; i < l->count && l->running; i++) {
- if (l->pollfds[i].revents) {
- struct yasl_fd_source src = l->fds[i];
- short revents = l->pollfds[i].revents;
- l->pollfds[i].revents = 0;
- if (src.fn)
- src.fn(src.fd, revents, src.ud);
- }
- }
}
return 0;
}
@@ -122,3 +254,8 @@ void yasl_loop_quit(struct yasl_loop *l)
{
if (l) l->running = false;
}
+
+bool yasl_loop_is_running(const struct yasl_loop *l)
+{
+ return l ? l->running : false;
+}
diff --git a/src/common/timing.c b/src/common/timing.c
index b5e342f..3e3a705 100644
--- a/src/common/timing.c
+++ b/src/common/timing.c
@@ -5,8 +5,8 @@ uint64_t yasl_clock_extend_ms(uint32_t ms, uint64_t now_ns)
uint64_t now_ms = now_ns / 1000000;
int64_t ahead = (int32_t)(ms - (uint32_t)now_ms);
if (ahead < 0 && (uint64_t)-ahead > now_ms)
- return (uint64_t)ms * 1000000;
- return (now_ms + (uint64_t)ahead) * 1000000;
+ return (uint64_t)ms * 1000000ull;
+ return (uint64_t)((int64_t)now_ms + ahead) * 1000000ull;
}
void yasl_clock_presented(struct yasl_frame_clock *c, uint64_t now, bool sample)
@@ -45,9 +45,8 @@ double yasl_clock_refresh_ms(const struct yasl_frame_clock *c)
uint64_t yasl_clock_next_present_ns(const struct yasl_frame_clock *c)
{
- return c->last_present_ns
- ? c->last_present_ns + (uint64_t)c->refresh_ns
- : 0;
+ uint64_t next = c->last_present_ns + (uint64_t)c->refresh_ns;
+ return (uint64_t)-(c->last_present_ns != 0) & next;
}
uint64_t yasl_clock_frame_timestamp_ns(struct yasl_frame_clock *c, uint64_t now)
diff --git a/src/common/utf8.c b/src/common/utf8.c
index 089fd89..1d4cf13 100644
--- a/src/common/utf8.c
+++ b/src/common/utf8.c
@@ -3,30 +3,46 @@
#include <stdbool.h>
#include <stdint.h>
+#ifndef likely
+#define likely(x) __builtin_expect(!!(x), 1)
+#endif
+#ifndef unlikely
+#define unlikely(x) __builtin_expect(!!(x), 0)
+#endif
+
bool yasl_utf8_validate(const char *str, size_t len)
{
const unsigned char *p = (const unsigned char *)str;
const unsigned char *end = p + len;
while (p < end) {
- if (*p < 0x80) {
+ while (p + 8 <= end) {
+ uint64_t v;
+ memcpy(&v, p, 8);
+ if (v & 0x8080808080808080ull)
+ break;
+ p += 8;
+ }
+ if (p >= end) break;
+
+ if (likely(*p < 0x80)) {
p++;
} else if ((*p & 0xe0) == 0xc0) {
- if (p + 1 >= end || (p[1] & 0xc0) != 0x80 || (*p & 0x1e) == 0)
+ if (unlikely(p + 1 >= end || (p[1] & 0xc0) != 0x80 || (*p & 0x1e) == 0))
return false;
p += 2;
} else if ((*p & 0xf0) == 0xe0) {
- if (p + 2 >= end || (p[1] & 0xc0) != 0x80 || (p[2] & 0xc0) != 0x80)
+ if (unlikely(p + 2 >= end || (p[1] & 0xc0) != 0x80 || (p[2] & 0xc0) != 0x80))
return false;
uint32_t cp = ((p[0] & 0x0f) << 12) | ((p[1] & 0x3f) << 6) | (p[2] & 0x3f);
- if (cp < 0x800 || (cp >= 0xd800 && cp <= 0xdfff))
+ if (unlikely(cp < 0x800 || (cp >= 0xd800 && cp <= 0xdfff)))
return false;
p += 3;
} else if ((*p & 0xf8) == 0xf0) {
- if (p + 3 >= end || (p[1] & 0xc0) != 0x80 || (p[2] & 0xc0) != 0x80 || (p[3] & 0xc0) != 0x80)
+ if (unlikely(p + 3 >= end || (p[1] & 0xc0) != 0x80 || (p[2] & 0xc0) != 0x80 || (p[3] & 0xc0) != 0x80))
return false;
uint32_t cp = ((p[0] & 0x07) << 18) | ((p[1] & 0x3f) << 12) | ((p[2] & 0x3f) << 6) | (p[3] & 0x3f);
- if (cp < 0x10000 || cp > 0x10ffff)
+ if (unlikely(cp < 0x10000 || cp > 0x10ffff))
return false;
p += 4;
} else {
@@ -38,10 +54,17 @@ bool yasl_utf8_validate(const char *str, size_t len)
char *yasl_utf8_sanitize_dup(const char *str)
{
- if (!str) return NULL;
+ if (unlikely(!str)) return NULL;
size_t len = strlen(str);
+ if (likely(yasl_utf8_validate(str, len))) {
+ char *out = malloc(len + 1);
+ if (unlikely(!out)) return NULL;
+ memcpy(out, str, len + 1);
+ return out;
+ }
+
char *out = malloc(len + 1);
- if (!out) return NULL;
+ if (unlikely(!out)) return NULL;
const unsigned char *p = (const unsigned char *)str;
size_t o = 0;