Switch to poll-based execution of blocks

This commit is contained in:
Utkarsh Verma 2021-10-15 01:58:20 +05:30
parent 770742a8ee
commit 839ccc5b1d
No known key found for this signature in database
GPG Key ID: 817656CF818EFCCC
2 changed files with 127 additions and 91 deletions

View File

@ -8,8 +8,8 @@ const Block blocks[] = {
BLOCK("sb-disk", 1800, 19) BLOCK("sb-disk", 1800, 19)
BLOCK("sb-memory", 1800, 20) BLOCK("sb-memory", 1800, 20)
BLOCK("sb-loadavg", 1800, 21) BLOCK("sb-loadavg", 1800, 21)
BLOCK("sb-volume", 1800, 22) BLOCK("sb-volume", 0, 22)
BLOCK("sb-battery", 5, 23) BLOCK("sb-battery", 5, 23)
BLOCK("sb-date", 5, 24) BLOCK("sb-date", 1, 24)
BLOCK("sb-network", 5, 25) BLOCK("sb-network", 5, 25)
}; };

214
main.c
View File

@ -1,33 +1,37 @@
#define _GNU_SOURCE #define _GNU_SOURCE
#include <X11/Xlib.h> #include <X11/Xlib.h>
#include <limits.h> #include <assert.h>
#include <signal.h> #include <signal.h>
#include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/epoll.h>
#include <sys/signalfd.h>
#include <time.h> #include <time.h>
#include <unistd.h> #include <unistd.h>
#define LEN(arr) (sizeof(arr) / sizeof(arr[0])) #define LEN(arr) (sizeof(arr) / sizeof(arr[0]))
#define STR2(a) #a #define BLOCK(cmd, interval, signal) {"echo \"_$(" cmd ")\"", interval, signal},
#define STR(a) STR2(a)
#define BLOCK(cmd, interval, signal) {"echo \"" STR(__COUNTER__) "$(" cmd ")\"", interval, signal},
typedef struct { typedef struct {
const char *command; const char* command;
const unsigned int interval; const unsigned int interval;
const unsigned int signal; const unsigned int signal;
} Block; } Block;
#include "config.h" #include "config.h"
static Display *dpy; static Display* dpy;
static int screen; static int screen;
static Window root; static Window root;
static unsigned short int statusContinue = 1;
static char outputs[LEN(blocks)][CMDLENGTH + 2]; static char outputs[LEN(blocks)][CMDLENGTH + 2];
static char statusBar[2][LEN(blocks) * ((LEN(outputs[0]) - 1) + (LEN(DELIMITER) - 1)) + 1]; static char statusBar[2][LEN(blocks) * ((LEN(outputs[0]) - 1) + (LEN(DELIMITER) - 1)) + 1];
static int statusContinue = 1; static struct epoll_event event, events[LEN(blocks) + 2];
static volatile sig_atomic_t updatedBlocks = 0; static int pipes[LEN(blocks)][2];
static int timerFD[2];
static int signalFD;
static int epollFD;
void (*writeStatus)(); void (*writeStatus)();
static int pipeFD[2];
int gcd(int a, int b) { int gcd(int a, int b) {
int temp; int temp;
@ -39,16 +43,10 @@ int gcd(int a, int b) {
return a; return a;
} }
void replace(char *str, char old, char new) { void execBlock(int i, const char* button) {
for (char *ch = str; *ch; ch++)
if (*ch == old)
*ch = new;
}
void getCommand(int i, const char *button) {
if (fork() == 0) { if (fork() == 0) {
dup2(pipeFD[1], STDOUT_FILENO); dup2(pipes[i][1], STDOUT_FILENO);
close(pipeFD[0]); close(pipes[i][0]);
if (button) if (button)
setenv("BLOCK_BUTTON", button, 1); setenv("BLOCK_BUTTON", button, 1);
@ -56,35 +54,47 @@ void getCommand(int i, const char *button) {
} }
} }
void getCommands(int time) { void execBlocks(unsigned long long int time) {
for (int i = 0; i < LEN(blocks); i++) for (int i = 0; i < LEN(blocks); i++)
if (time == 0 || (blocks[i].interval != 0 && time % blocks[i].interval == 0)) if (time == 0 || (blocks[i].interval != 0 && time % blocks[i].interval == 0))
getCommand(i, NULL); execBlock(i, NULL);
} }
void getSignalCommand(int signal) { int getStatus(char* new, char* old) {
for (int i = 0; i < LEN(blocks); i++)
if (blocks[i].signal == signal)
getCommand(i, NULL);
}
int getStatus(char *new, char *old) {
strcpy(old, new); strcpy(old, new);
new[0] = 0; new[0] = '\0';
for (int i = 0; i < LEN(blocks); i++) { for (int i = 0; i < LEN(blocks); i++) {
#ifdef TRAILING_DELIMITER #ifdef TRAILING_DELIMITER
if (strlen(outputs[i]) > (blocks[i].signal > 0)) if (strlen(outputs[i]) > (blocks[i].signal > 0))
#else #else
if (strlen(new) && strlen(outputs[i]) > (blocks[i].signal > 0)) if (strlen(new) && strlen(outputs[i]) > (blocks[i].signal > 0))
#endif #endif
strcat(new, DELIMITER); strcat(new, DELIMITER);
strcat(new, outputs[i]); strcat(new, outputs[i]);
} }
new[strlen(new)] = 0;
return strcmp(new, old); return strcmp(new, old);
} }
void updateBlock(int i) {
char* output = outputs[i];
char buffer[LEN(outputs[0])];
int bytesRead = read(pipes[i][0], buffer, LEN(buffer));
buffer[bytesRead - 1] = '\0';
// Clear the pipe
if (bytesRead == LEN(buffer)) {
char ch;
while (read(pipes[i][0], &ch, 1) == 1 && ch != '\n')
;
}
if (blocks[i].signal > 0)
buffer[0] = blocks[i].signal;
strcpy(output, buffer);
}
void debug() { void debug() {
// Only write out if text has changed // Only write out if text has changed
if (!getStatus(statusBar[0], statusBar[1])) if (!getStatus(statusBar[0], statusBar[1]))
@ -99,7 +109,7 @@ void setRoot() {
if (!getStatus(statusBar[0], statusBar[1])) if (!getStatus(statusBar[0], statusBar[1]))
return; return;
Display *d = XOpenDisplay(NULL); Display* d = XOpenDisplay(NULL);
if (d) if (d)
dpy = d; dpy = d;
screen = DefaultScreen(dpy); screen = DefaultScreen(dpy);
@ -108,87 +118,107 @@ void setRoot() {
XCloseDisplay(dpy); XCloseDisplay(dpy);
} }
void signalHandler(int sig, siginfo_t *si, void *ucontext) { void signalHandler() {
sig -= SIGRTMIN; struct signalfd_siginfo info;
int i = 0; read(signalFD, &info, sizeof(info));
while (blocks[i].signal != sig)
i++; for (int j = 0; j < LEN(blocks); j++) {
const char button[2] = {'0' + si->si_value.sival_int & 0xff, 0}; if (blocks[j].signal == info.ssi_signo - SIGRTMIN) {
getCommand(i, button); char button[] = {'0' + info.ssi_int & 0xff, 0};
execBlock(j, button);
break;
}
}
} }
void termHandler(int signal) { void termHandler() {
statusContinue = 0; statusContinue = 0;
exit(EXIT_SUCCESS);
}
void childHandler() {
char i;
read(pipeFD[0], &i, 1);
i -= '0';
char ch;
char buffer[LEN(outputs[0]) - 1];
int j = 0;
while (j < LEN(buffer) - 1 && read(pipeFD[0], &ch, 1) == 1 && ch != '\n')
buffer[j++] = ch;
buffer[j] = 0;
// Clear the pipe until newline
while (ch != '\n' && read(pipeFD[0], &ch, 1) == 1);
char *output = outputs[i];
if (blocks[i].signal > 0) {
output[0] = blocks[i].signal;
output++;
}
// Don't write stale output from the pipe. This only happens when signals
// are received in a rapid succession.
if ((updatedBlocks & (1 << i)) == 0) {
updatedBlocks |= 1 << i;
strcpy(output, buffer);
writeStatus();
}
updatedBlocks &= ~(1 << i);
} }
void setupSignals() { void setupSignals() {
signal(SIGTERM, termHandler); signal(SIGTERM, termHandler);
signal(SIGINT, termHandler); signal(SIGINT, termHandler);
// Handle block update signals // Avoid zombie subprocesses
struct sigaction sa; struct sigaction sa;
sa.sa_flags = SA_SIGINFO; sa.sa_handler = SIG_DFL;
sa.sa_sigaction = signalHandler; sigemptyset(&sa.sa_mask);
for (int i = 0; i < LEN(blocks); i++) { sa.sa_flags = SA_NOCLDWAIT;
sigaction(SIGCHLD, &sa, 0);
// Handle block update signals
sigset_t sigset;
sigemptyset(&sigset);
for (int i = 0; i < LEN(blocks); i++)
if (blocks[i].signal > 0) if (blocks[i].signal > 0)
sigaction(SIGRTMIN + blocks[i].signal, &sa, NULL); sigaddset(&sigset, SIGRTMIN + blocks[i].signal);
signalFD = signalfd(-1, &sigset, 0);
sigprocmask(SIG_BLOCK, &sigset, NULL);
event.data.u32 = LEN(blocks) + 1;
epoll_ctl(epollFD, EPOLL_CTL_ADD, signalFD, &event);
}
void init() {
epollFD = epoll_create(LEN(blocks) + 1);
event.events = EPOLLIN;
for (int i = 0; i < LEN(blocks); i++) {
pipe(pipes[i]);
event.data.u32 = i;
epoll_ctl(epollFD, EPOLL_CTL_ADD, pipes[i][0], &event);
} }
// Handle exit of forks pipe(timerFD);
signal(SIGCHLD, childHandler); event.data.u32 = LEN(blocks);
epoll_ctl(epollFD, EPOLL_CTL_ADD, timerFD[0], &event);
setupSignals();
} }
void statusLoop() { void statusLoop() {
getCommands(0); execBlocks(0);
while (statusContinue) {
int eventCount = epoll_wait(epollFD, events, LEN(events), 1000);
for (int i = 0; i < eventCount; i++) {
unsigned int id = events[i].data.u32;
if (id == LEN(blocks)) {
unsigned long long int j = 0;
read(timerFD[0], &j, sizeof(j));
execBlocks(j);
} else if (id < LEN(blocks)) {
updateBlock(events[i].data.u32);
} else {
signalHandler();
}
}
if (eventCount)
writeStatus();
}
}
void timerLoop() {
close(timerFD[0]);
unsigned int sleepInterval = -1; unsigned int sleepInterval = -1;
for (int i = 0; i < LEN(blocks); i++) for (int i = 0; i < LEN(blocks); i++)
if (blocks[i].interval) if (blocks[i].interval)
sleepInterval = gcd(blocks[i].interval, sleepInterval); sleepInterval = gcd(blocks[i].interval, sleepInterval);
unsigned int i = 0; unsigned long long int i = 0;
struct timespec sleepTime = {sleepInterval, 0}; struct timespec sleepTime = {sleepInterval, 0};
struct timespec toSleep = sleepTime; struct timespec toSleep = sleepTime;
while (statusContinue) { while (1) {
// Sleep for `sleepTime` even on being interrupted // Sleep for `sleepTime` even on being interrupted
if (nanosleep(&toSleep, &toSleep) == -1) if (nanosleep(&toSleep, &toSleep) == -1)
continue; continue;
// Write to status after sleeping // Notify parent to update blocks
getCommands(i); write(timerFD[1], &i, sizeof(i));
// After sleep, reset timer and update counter // After sleep, reset timer and update counter
toSleep = sleepTime; toSleep = sleepTime;
@ -196,13 +226,19 @@ void statusLoop() {
} }
} }
int main(int argc, char **argv) { int main(const int argc, const char* argv[]) {
pipe(pipeFD);
writeStatus = setRoot; writeStatus = setRoot;
for (int i = 0; i < argc; i++) for (int i = 0; i < argc; i++)
if (!strcmp("-d", argv[i])) if (!strcmp("-d", argv[i]))
writeStatus = debug; writeStatus = debug;
setupSignals(); init();
statusLoop();
if (fork() == 0)
timerLoop();
else
statusLoop();
close(epollFD);
return 0;
} }