Krolyxon
Commit 49d11f0

Branches

Refs →

nsxiv49d11f0

spawn(): improve performance and simplify API

posix_spawn() is designed especially for this purpose, and thus it's
much more lightweight and efficient than manually fork/dup/exec-ing.

on my system, it improves the performance of spawn() by about 10x. given
that we make frequent calls to potentially multiple scripts, the
increased efficiency will add up overtime.

using posix_spawn() also simplifies the logic quite a bit, despite the
very verbose function names. however it does make cleanup a bit more
complicated.

this patch uses the linux kernel style cleanup strategy [0] (which I'm
personally not a huge fan of, but it fits this situation quite nicely)
with a "stack-like" unwinding via `goto`-s.

additionally simplify the spawn() API by taking in {read,write}fd
pointers and returning the pid instead of using some custom struct.

this coincidently also fixes #299

[0]: https://www.kernel.org/doc/html/v4.10/process/coding-style.html?highlight=goto#centralized-exiting-of-functions
Author
NRK
Date
2022-06-22 09:28 +0600
Committer
NRK · 2023-01-09 11:07 +0600
Parent
76c2b81
Commit
49d11f0d1fae43ae1e5f61204ed051e889062767

+67 −79

3 files changed

  1. M main.c +13 −21
  2. M nsxiv.h +1 −12
  3. M util.c +53 −46
M main.c View
@@ -277,9 +277,9 @@ static bool check_timeouts(int *t)
277277 static size_t get_win_title(char *buf, size_t len)
278278 {
279279 char *argv[8];
280− spawn_t pfd;
281280 char w[12] = "", h[12] = "", z[12] = "", fidx[12], fcnt[12];
282281 ssize_t n = -1;
282+ int readfd;
283283
284284 if (wintitle.f.err || buf == NULL || len == 0)
285285 return 0;
@@ -293,11 +293,10 @@ static size_t get_win_title(char *buf, size_t len)
293293 snprintf(fcnt, ARRLEN(fcnt), "%d", filecnt);
294294 construct_argv(argv, ARRLEN(argv), wintitle.f.cmd, files[fileidx].path,
295295 fidx, fcnt, w, h, z, NULL);
296− pfd = spawn(wintitle.f.cmd, argv, X_READ);
297− if (pfd.readfd >= 0) {
298− if ((n = read(pfd.readfd, buf, len-1)) > 0)
296+ if (spawn(&readfd, NULL, argv) > 0) {
297+ if ((n = read(readfd, buf, len-1)) > 0)
299298 buf[n] = '\0';
300− close(pfd.readfd);
299+ close(readfd);
301300 }
302301
303302 return MAX(0, n);
@@ -314,9 +313,7 @@ void close_info(void)
314313
315314 void open_info(void)
316315 {
317− spawn_t pfd;
318− char w[12] = "", h[12] = "";
319− char *argv[6];
316+ char *argv[6], w[12] = "", h[12] = "";
320317 char *cmd = mode == MODE_IMAGE ? info.f.cmd : info.ft.cmd;
321318 bool ferr = mode == MODE_IMAGE ? info.f.err : info.ft.err;
322319
@@ -329,12 +326,8 @@ void open_info(void)
329326 }
330327 construct_argv(argv, ARRLEN(argv), cmd, files[fileidx].name, w, h,
331328 files[fileidx].path, NULL);
332− pfd = spawn(cmd, argv, X_READ);
333− if (pfd.readfd >= 0) {
334− fcntl(pfd.readfd, F_SETFL, O_NONBLOCK);
335− info.fd = pfd.readfd;
336− info.pid = pfd.pid;
337− }
329+ if ((info.pid = spawn(&info.fd, NULL, argv)) > 0)
330+ fcntl(info.fd, F_SETFL, O_NONBLOCK);
338331 }
339332
340333 static void read_info(void)
@@ -579,13 +572,13 @@ static bool run_key_handler(const char *key, unsigned int mask)
579572 FILE *pfs;
580573 bool marked = mode == MODE_THUMB && markcnt > 0;
581574 bool changed = false;
582− int f, i;
575+ pid_t pid;
576+ int writefd, f, i;
583577 int fcnt = marked ? markcnt : 1;
584578 char kstr[32];
585579 struct stat *oldst, st;
586580 XEvent dump;
587581 char *argv[3];
588− spawn_t pfd;
589582
590583 if (keyhandler.f.err) {
591584 if (!keyhandler.warned) {
@@ -608,12 +601,11 @@ static bool run_key_handler(const char *key, unsigned int mask)
608601 mask & Mod1Mask ? "M-" : "",
609602 mask & ShiftMask ? "S-" : "", key);
610603 construct_argv(argv, ARRLEN(argv), keyhandler.f.cmd, kstr, NULL);
611− pfd = spawn(keyhandler.f.cmd, argv, X_WRITE);
612− if (pfd.writefd < 0)
604+ if ((pid = spawn(NULL, &writefd, argv)) < 0)
613605 return false;
614− if ((pfs = fdopen(pfd.writefd, "w")) == NULL) {
606+ if ((pfs = fdopen(writefd, "w")) == NULL) {
615607 error(0, errno, "open pipe");
616− close(pfd.writefd);
608+ close(writefd);
617609 return false;
618610 }
619611
@@ -626,7 +618,7 @@ static bool run_key_handler(const char *key, unsigned int mask)
626618 }
627619 }
628620 fclose(pfs);
629− while (waitpid(pfd.pid, NULL, 0) == -1 && errno == EINTR);
621+ while (waitpid(pid, NULL, 0) == -1 && errno == EINTR);
630622
631623 for (f = i = 0; f < fcnt; i++) {
632624 if ((marked && (files[i].flags & FF_MARK)) || (!marked && i == fileidx)) {
M nsxiv.h View
@@ -326,17 +326,6 @@ typedef struct {
326326 int stlen;
327327 } r_dir_t;
328328
329−typedef struct {
330− int readfd;
331− int writefd;
332− pid_t pid;
333−} spawn_t;
334−
335−enum {
336− X_READ = (1 << 0),
337− X_WRITE = (1 << 1)
338−};
339−
340329 extern const char *progname;
341330
342331 void* emalloc(size_t);
@@ -349,7 +338,7 @@ int r_closedir(r_dir_t*);
349338 char* r_readdir(r_dir_t*, bool);
350339 int r_mkdir(char*);
351340 void construct_argv(char**, unsigned int, ...);
352−spawn_t spawn(const char*, char *const [], unsigned int);
341+pid_t spawn(int*, int*, char *const []);
353342
354343
355344 /* window.c */
M util.c View
@@ -21,6 +21,7 @@
2121
2222 #include <assert.h>
2323 #include <errno.h>
24+#include <spawn.h>
2425 #include <stdarg.h>
2526 #include <stdio.h>
2627 #include <stdlib.h>
@@ -28,6 +29,7 @@
2829 #include <sys/stat.h>
2930 #include <unistd.h>
3031
32+extern char **environ;
3133 const char *progname = "nsxiv";
3234
3335 void* emalloc(size_t size)
@@ -228,60 +230,65 @@ void construct_argv(char **argv, unsigned int len, ...)
228230 error(EXIT_FAILURE, 0, "argv not NULL terminated");
229231 }
230232
231−spawn_t spawn(const char *cmd, char *const argv[], unsigned int flags)
233+static int mkspawn_pipe(posix_spawn_file_actions_t *fa, const char *cmd, int *pfd, int dupidx)
232234 {
233− pid_t pid;
234− spawn_t status = { -1, -1, -1 };
235− int pfd_read[2] = { -1, -1 }, pfd_write[2] = { -1, -1 };
236− const bool r = flags & X_READ;
237− const bool w = flags & X_WRITE;
238−
239− if (cmd == NULL || argv == NULL || flags == 0)
240− return status;
241−
242− if (r && pipe(pfd_read) < 0) {
235+ int err;
236+ if (pipe(pfd) < 0) {
243237 error(0, errno, "pipe: %s", cmd);
244− return status;
238+ return -1;
245239 }
240+ err = posix_spawn_file_actions_adddup2(fa, pfd[dupidx], dupidx);
241+ err = err ? err : posix_spawn_file_actions_addclose(fa, pfd[0]);
242+ err = err ? err : posix_spawn_file_actions_addclose(fa, pfd[1]);
243+ if (err) {
244+ error(0, err, "posix_spawn_file_actions: %s", cmd);
245+ close(pfd[0]);
246+ close(pfd[1]);
247+ }
248+ return err ? -1 : 0;
249+}
246250
247− if (w && pipe(pfd_write) < 0) {
248− error(0, errno, "pipe: %s", cmd);
249− if (r) {
250− close(pfd_read[0]);
251− close(pfd_read[1]);
252− }
253− return status;
251+pid_t spawn(int *readfd, int *writefd, char *const argv[])
252+{
253+ pid_t pid = -1;
254+ const char *cmd;
255+ int err, pfd_read[2], pfd_write[2];
256+ posix_spawn_file_actions_t fa;
257+
258+ assert(argv != NULL && argv[0] != NULL);
259+ cmd = argv[0];
260+
261+ if ((err = posix_spawn_file_actions_init(&fa)) != 0) {
262+ error(0, err, "spawn: %s", cmd);
263+ return pid;
254264 }
255265
256− if ((pid = fork()) == 0) { /* in child */
257− if ((r && dup2(pfd_read[1], 1) < 0) || (w && dup2(pfd_write[0], 0) < 0))
258− error(EXIT_FAILURE, errno, "dup2: %s", cmd);
266+ if (readfd != NULL && mkspawn_pipe(&fa, cmd, pfd_read, 1) < 0)
267+ goto err_destroy_fa;
268+ if (writefd != NULL && mkspawn_pipe(&fa, cmd, pfd_write, 0) < 0)
269+ goto err_close_readfd;
270+
271+ if ((err = posix_spawn(&pid, cmd, &fa, NULL, argv, environ)) != 0) {
272+ error(0, err, "spawn: %s", cmd);
273+ } else {
274+ if (readfd != NULL)
275+ *readfd = pfd_read[0];
276+ if (writefd != NULL)
277+ *writefd = pfd_write[1];
278+ }
259279
260− if (r) {
261− close(pfd_read[0]);
262− close(pfd_read[1]);
263− }
264− if (w) {
265− close(pfd_write[0]);
266− close(pfd_write[1]);
267− }
268− execv(cmd, argv);
269− error(EXIT_FAILURE, errno, "exec: %s", cmd);
270− } else if (pid < 0) { /* fork failed */
271− error(0, errno, "fork: %s", cmd);
272− if (r)
273− close(pfd_read[0]);
274− if (w)
280+ if (writefd != NULL) {
281+ close(pfd_write[0]);
282+ if (pid < 0)
275283 close(pfd_write[1]);
276− } else { /* in parent */
277− status.pid = pid;
278− status.readfd = pfd_read[0];
279− status.writefd = pfd_write[1];
280284 }
281−
282− if (r)
285+err_close_readfd:
286+ if (readfd != NULL) {
287+ if (pid < 0)
288+ close(pfd_read[0]);
283289 close(pfd_read[1]);
284− if (w)
285− close(pfd_write[0]);
286− return status;
290+ }
291+err_destroy_fa:
292+ posix_spawn_file_actions_destroy(&fa);
293+ return pid;
287294 }