commit 63ce6eaeeeea10a855bd1695a87a344eb3346692
parent 309e46917a00d220dea4aff0b19ce38660459fff
Author: Santtu Lakkala <santtu.lakkala@unikie.com>
Date: Thu, 27 Feb 2025 17:46:25 +0200
mmappy things
Diffstat:
6 files changed, 78 insertions(+), 34 deletions(-)
diff --git a/Makefile b/Makefile
@@ -1,7 +1,7 @@
.POSIX:
PREFIX?=/usr/local
PREFIX_MAN=$(PREFIX)/man
-CFLAGS=-std=c99 -Os -Wall -Wextra -pedantic -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=500 -DWITHOUT_TIMEGM
+CFLAGS=-std=c99 -O -Wall -Wextra -pedantic -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=500 -DWITHOUT_TIMEGM
all: snac
diff --git a/data.c b/data.c
@@ -21,6 +21,7 @@
#include <sys/stat.h>
#include <sys/file.h>
#include <sys/time.h>
+#include <sys/mman.h>
#include <fcntl.h>
#include <pthread.h>
#include <dirent.h>
@@ -193,6 +194,8 @@ void srv_free(void)
xs_free(srv_basedir);
xs_free(srv_config);
xs_free(srv_baseurl);
+ xs_free(srv_langs);
+ xs_free(srv_proxy_token_seed);
pthread_mutex_destroy(&data_mutex);
}
@@ -2622,10 +2625,11 @@ void list_distribute(snac *user, const char *who, const xs_dict *post)
/** static data **/
static int _load_raw_file(FILE *f, xs_val **data, int *size,
- const char *inm, xs_str **etag)
+ const char *inm, xs_str **etag, int *mmapped)
/* loads a cached file */
{
int status = HTTP_STATUS_NOT_FOUND;
+ *mmapped = 0;
if (f) {
double tm = mtime_f(f);
@@ -2640,11 +2644,16 @@ static int _load_raw_file(FILE *f, xs_val **data, int *size,
status = HTTP_STATUS_NOT_MODIFIED;
}
else {
+ struct stat st;
+ if (fstat(fileno(f), &st) == 0 && (*data = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fileno(f), 0)) != MAP_FAILED) {
+ *mmapped = 1;
+ *size = st.st_size;
+ }
+ else {
+ *size = XS_ALL;
+ *data = xs_read(f, size);
+ }
/* newer or never downloaded; read the full file */
- *size = XS_ALL;
- *data = xs_read(f, size);
- fclose(f);
-
status = HTTP_STATUS_OK;
}
@@ -2652,6 +2661,7 @@ static int _load_raw_file(FILE *f, xs_val **data, int *size,
if (etag != NULL)
*etag = xs_dup(e);
}
+ fclose(f);
}
return status;
@@ -2731,10 +2741,10 @@ xs_str *_static_fn(snac *snac, const char *id)
int static_get(snac *snac, const char *id, xs_val **data, int *size,
- const char *inm, xs_str **etag)
+ const char *inm, xs_str **etag, int *mmapped)
/* returns static content */
{
- return _load_raw_file(user_open_subfile(snac, "static", id, 0), data, size, inm, etag);
+ return _load_raw_file(user_open_subfile(snac, "static", id, 0), data, size, inm, etag, mmapped);
}
int static_get_partial(snac *snac, const char *id, xs_val **data, int *size,
@@ -2843,9 +2853,9 @@ void history_add(snac *snac, const char *id, const char *content, int size,
int history_get(snac *snac, const char *id, xs_str **content, int *size,
- const char *inm, xs_str **etag)
+ const char *inm, xs_str **etag, int *mmapped)
{
- return _load_raw_file(user_open_subfile(snac, "history", id, 0), content, size, inm, etag);
+ return _load_raw_file(user_open_subfile(snac, "history", id, 0), content, size, inm, etag, mmapped);
}
diff --git a/html.c b/html.c
@@ -17,6 +17,7 @@
#include "xs_random.h"
#include "snac.h"
+#include <sys/mman.h>
int login(snac *user, const xs_dict *headers)
/* tries a login */
@@ -760,6 +761,7 @@ static xs_html *html_instance_body(void)
xs_html *html_user_head(snac *user, const char *desc, const char *url)
{
xs_html *head = html_base_head();
+ int mmapped = 0;
/* add the user CSS */
{
@@ -767,7 +769,7 @@ xs_html *html_user_head(snac *user, const char *desc, const char *url)
int size;
/* try to open the user css */
- if (!valid_status(static_get(user, "style.css", &css, &size, NULL, NULL))) {
+ if (!valid_status(static_get(user, "style.css", &css, &size, NULL, NULL, &mmapped))) {
/* it's not there; try to open the server-wide css */
FILE *f;
xs *g_css_fn = xs_fmt("%s/style.css", srv_basedir);
@@ -783,6 +785,11 @@ xs_html *html_user_head(snac *user, const char *desc, const char *url)
xs_html_tag("style",
xs_html_raw(css)));
}
+
+ if (mmapped) {
+ munmap(css, size);
+ css = NULL;
+ }
}
/* title */
@@ -3601,7 +3608,7 @@ void set_user_lang(snac *user)
int html_get_handler(const xs_dict *req, const char *q_path,
char **body, int *b_size, char **ctype,
xs_str **etag, xs_str **last_modified,
- xs_dict **headers)
+ xs_dict **headers, int *mmapped)
{
const char *accept = xs_dict_get(req, "accept");
int status = HTTP_STATUS_NOT_FOUND;
@@ -3616,6 +3623,8 @@ int html_get_handler(const xs_dict *req, const char *q_path,
const xs_dict *q_vars = xs_dict_get(req, "q_vars");
+ *mmapped = 0;
+
xs *l = xs_split_n(q_path, "/", 2);
v = xs_list_get(l, 1);
@@ -3732,7 +3741,7 @@ int html_get_handler(const xs_dict *req, const char *q_path,
snac_debug(&snac, 1, xs_fmt("serving cached local timeline"));
status = history_get(&snac, h, body, b_size,
- xs_dict_get(req, "if-none-match"), etag);
+ xs_dict_get(req, "if-none-match"), etag, mmapped);
}
else {
xs *list = NULL;
@@ -3744,7 +3753,8 @@ int html_get_handler(const xs_dict *req, const char *q_path,
list = timeline_list(&snac, "public", skip, show, &more);
xs *pins = pinned_list(&snac);
- pins = xs_list_cat(pins, list);
+ if (xs_is_list(list))
+ pins = xs_list_cat(pins, list);
*body = html_timeline(&snac, pins, 1, skip, show, more, NULL, "", 1, error);
@@ -3912,7 +3922,7 @@ int html_get_handler(const xs_dict *req, const char *q_path,
snac_debug(&snac, 1, xs_fmt("serving cached timeline"));
status = history_get(&snac, "timeline.html_", body, b_size,
- xs_dict_get(req, "if-none-match"), etag);
+ xs_dict_get(req, "if-none-match"), etag, mmapped);
}
else {
int more = 0;
@@ -4127,7 +4137,7 @@ int html_get_handler(const xs_dict *req, const char *q_path,
}
} else {
status = static_get(&snac, id, body, &sz,
- xs_dict_get(req, "if-none-match"), etag);
+ xs_dict_get(req, "if-none-match"), etag, mmapped);
}
if (valid_status(status) && status != HTTP_STATUS_PARTIAL_CONTENT) {
@@ -4155,7 +4165,7 @@ int html_get_handler(const xs_dict *req, const char *q_path,
}
else
status = history_get(&snac, id, body, b_size,
- xs_dict_get(req, "if-none-match"), etag);
+ xs_dict_get(req, "if-none-match"), etag, mmapped);
}
}
else
diff --git a/httpd.c b/httpd.c
@@ -392,6 +392,7 @@ void httpd_connection(FILE *f)
int p_size = 0;
const char *p;
int fcgi_id;
+ int mmapped = 0;
if (p_state->use_fcgi)
req = xs_fcgi_request(f, &payload, &p_size, &fcgi_id);
@@ -440,7 +441,7 @@ void httpd_connection(FILE *f)
#endif /* NO_MASTODON_API */
if (status == 0)
- status = html_get_handler(req, q_path, &body, &b_size, &ctype, &etag, &last_modified, &headers);
+ status = html_get_handler(req, q_path, &body, &b_size, &ctype, &etag, &last_modified, &headers, &mmapped);
}
else
if (strcmp(method, "POST") == 0) {
@@ -552,8 +553,15 @@ void httpd_connection(FILE *f)
b_size = strlen(body);
/* if it was a HEAD, no body will be sent */
- if (strcmp(method, "HEAD") == 0)
- body = xs_free(body);
+ if (strcmp(method, "HEAD") == 0) {
+ if (mmapped) {
+ munmap(body, b_size);
+ body = NULL;
+ mmapped = 0;
+ }
+ else
+ body = xs_free(body);
+ }
headers = xs_dict_append(headers, "access-control-allow-origin", "*");
headers = xs_dict_append(headers, "access-control-allow-headers", "*");
@@ -581,7 +589,13 @@ void httpd_connection(FILE *f)
}
}
- xs_free(body);
+ if (mmapped) {
+ munmap(body, b_size);
+ body = NULL;
+ mmapped = 0;
+ }
+ else
+ body = xs_free(body);
}
diff --git a/snac.h b/snac.h
@@ -244,7 +244,7 @@ int actor_add(const char *actor, const xs_dict *msg);
int actor_get(const char *actor, xs_dict **data);
int actor_get_refresh(snac *user, const char *actor, xs_dict **data);
-int static_get(snac *snac, const char *id, xs_val **data, int *size, const char *inm, xs_str **etag);
+int static_get(snac *snac, const char *id, xs_val **data, int *size, const char *inm, xs_str **etag, int *mmapped);
int static_get_partial(snac *snac, const char *id, xs_val **data, int *size, const char *inm, xs_str **etag, int start, int *end);
void static_put(snac *snac, const char *id, const char *data, int size);
void static_put_meta(snac *snac, const char *id, const char *str);
@@ -254,7 +254,7 @@ double history_mtime(snac *snac, const char *id);
void history_add(snac *snac, const char *id, const char *content, int size,
xs_str **etag);
int history_get(snac *snac, const char *id, xs_str **content, int *size,
- const char *inm, xs_str **etag);
+ const char *inm, xs_str **etag, int *mmapped);
int history_del(snac *snac, const char *id);
xs_list *history_list(snac *snac);
@@ -401,7 +401,8 @@ xs_str *html_timeline(snac *user, const xs_list *list, int read_only,
int html_get_handler(const xs_dict *req, const char *q_path,
char **body, int *b_size, char **ctype,
xs_str **etag, xs_str **last_modified,
- xs_dict **headers);
+ xs_dict **headers,
+ int *mmapped);
int html_post_handler(const xs_dict *req, const char *q_path,
char *payload, int p_size,
diff --git a/xs_fcgi.h b/xs_fcgi.h
@@ -322,26 +322,24 @@ void xs_fcgi_response(FILE *f, int status, xs_dict *headers, xs_str *body, int b
out = xs_str_cat(out, s1);
}
+ if (body == NULL)
+ b_size = 0;
+
out = xs_str_cat(out, "\r\n");
/* everything is text by now */
int size = strlen(out);
- /* add the body */
- if (body != NULL && b_size > 0) {
- out = xs_append_m(out, body, b_size);
- size += b_size;
- }
-
/* now send all the STDOUT in packets */
hdr.version = FCGI_VERSION_1;
hdr.type = FCGI_STDOUT;
hdr.id = fcgi_id;
- int offset = 0;
+ int offset;
+ size_t sz;
- while (offset < size) {
- size_t sz = size - offset;
+ for (offset = 0; offset < size; offset += sz) {
+ sz = size - offset;
if (sz > 0xffff)
sz = 0xffff;
@@ -350,10 +348,21 @@ void xs_fcgi_response(FILE *f, int status, xs_dict *headers, xs_str *body, int b
/* write or fail */
if (!fwrite(&hdr, sizeof(hdr), 1, f) || fwrite(out + offset, 1, sz, f) != sz)
return;
+ }
+
+ for (offset = 0; offset < b_size; offset += sz) {
+ sz = b_size - offset;
+ if (sz > 0xffff)
+ sz = 0xffff;
+
+ hdr.content_len = htons(sz);
- offset += sz;
+ /* write or fail */
+ if (!fwrite(&hdr, sizeof(hdr), 1, f) || fwrite(body + offset, 1, sz, f) != sz)
+ return;
}
+
/* final STDOUT packet with 0 size */
hdr.content_len = 0;
if (!fwrite(&hdr, sizeof(hdr), 1, f))