int main(int argc, const char **argv) {
oop_source_sys *sys_events;
const char *arg;
- int r;
+ int r, c;
persist_map_veryearly();
case 'V': simlog_full=1; break;
case 'L': logcopy_fn= arg; arg=0; break;
case 'S': simulate= arg; arg=0; break;
+ case 'R':
+ rtfeats_use= 0;
+ while ((c= *arg++)) {
+ switch (c) {
+ case 'd': rtfeats_use |= RTFEAT_DEFAULTS; break;
+ case 'p': rtfeats_use |= RTFEAT_CPU; break;
+ case 'P': rtfeats_use |= RTFEAT_ALL(CPU); break;
+ case 'm': rtfeats_use |= RTFEAT_MEM; break;
+ case 'M': rtfeats_use |= RTFEAT_ALL(MEM); break;
+ default: badusage("unknown -R suboption");
+ }
+ }
+ break;
default: badusage("unknown option");
}
}
events->on_fd(events, serial_fd, OOP_READ, serial_readable, 0);
events->on_fd(events, serial_fd, OOP_EXCEPTION, read_exception, 0);
+ if (rtfeats_use & RTFEAT_DEFAULTS)
+ rtfeats_use |= RTFEAT_CPU | RTFEAT_MEM;
} else {
sim_initialise(logcopy_fn);
sys_events= 0;
persist_entrails_interpret();
records_parse(argv);
+ realtime_priority();
sta_startup();
if (!simulate) oop_sys_run(sys_events);
-#include <sys/time.h>
+/*
+ * realtime
+ * realtime priority
+ */
+
+#include "realtime.h"
+
#include <sys/resource.h>
#include <signal.h>
#include <time.h>
-#include <unistd.h>
-#include <assert.h>
-#include <string.h>
-
-#define MAXCREDIT 5
-#define MAXNTH 2
+
+#include <sched.h>
+
+/*---------- !ALL(MEM): prevent us eating all RAM ----------*/
+
+static void rtf_limit_MEM(void) { }
+
+/*---------- MEM: lock us into memory ----------*/
+
+static void rtf_acquire_MEM(void) { }
+
+/*---------- !ALL(CPU): use SIGXCPU to detect us spinning ----------*/
+
+#define MAX_CPU_CREDIT 5 /* max burst of 5 seconds */
+#define MAX_CPU_NTH 2 /* allow up to 50% usage, sustained */
static time_t now, last;
static struct rlimit rlcpu;
-static struct sigaction saxcpu;
+static struct sigaction sa_xcpu;
+
+static void write_stderr(const char *m, int l) { write(2,m,l); }
-static void gnow(void) { time(&now); }
+static void cpulim_bomb(const char *what) __attribute__((noreturn));
+static void cpulim_bomb(const char *what) {
+ static const char m[]= "realtime: cpu limitation failure: ";
+ int e;
+ const char *s;
+
+ e= errno;
+ write_stderr(m,sizeof(m)-1);
+ write_stderr(what,strlen(what));
+ write_stderr(": ",2);
+
+ s= strerror(e);
+ write_stderr(s,strlen(s));
+ write_stderr("\n",1);
+ exit(-127);
+}
+
+static const char *cpulim_gnow(void) { /* returns what failed */
+ if (time(&now) == (time_t)-1) return "time(2)";
+ return 0;
+}
-static void makepending(void) {
+static const char *cpulim_makepending(void) { /* returns what failed */
int r;
last= now;
- r= sigaction(SIGXCPU, &saxcpu, 0); assert(!r);
- r= setrlimit(RLIMIT_CPU, &rlcpu); assert(!r);
+ r= sigaction(SIGXCPU, &sa_xcpu, 0); if (r) return "sigaction SIGXCPU";
+ r= setrlimit(RLIMIT_CPU, &rlcpu); if (r) return "setrlimit RLIMIT_CPU";
+ return 0;
}
-static void exceeded(void) {
- const char m[]= "cpu limit exceeded\n";
- write(2,m,sizeof(m)-1);
+static void cpulim_exceeded(void) {
+ static const char m[]= "realtime: cpu limit exceeded\n";
+ write_stderr(m,sizeof(m)-1);
raise(SIGXCPU);
+ abort();
}
static void xcpuhandler(int ignored) {
int credit;
- gnow();
- credit= (now - last)/MAXNTH;
- if (credit <= 0) exceeded();
- if (credit > MAXCREDIT) credit= MAXCREDIT;
+ const char *whatfailed;
+
+ whatfailed= cpulim_gnow();
+ if (whatfailed) goto fail;
+
+ credit= (now - last)/MAX_CPU_NTH;
+ if (credit <= 0) cpulim_exceeded();
+ if (credit > MAX_CPU_CREDIT) credit= MAX_CPU_CREDIT;
rlcpu.rlim_cur += credit;
- makepending();
+
+ whatfailed= cpulim_makepending();
+ if (whatfailed) goto fail;
+ return;
+
+fail:
+ cpulim_bomb(whatfailed);
}
-int main(void) {
+static void rtf_limit_CPU(void) {
int r;
+ const char *whatfailed;
- memset(&saxcpu,0,sizeof(saxcpu));
- saxcpu.sa_handler= xcpuhandler;
- sigemptyset(&saxcpu.sa_mask);
- saxcpu.sa_flags= SA_RESETHAND | SA_RESTART | SA_NODEFER;
+ memset(&sa_xcpu,0,sizeof(sa_xcpu));
+ sa_xcpu.sa_handler= xcpuhandler;
+ sigemptyset(&sa_xcpu.sa_mask);
+ sa_xcpu.sa_flags= SA_RESETHAND | SA_RESTART | SA_NODEFER;
- r= getrlimit(RLIMIT_CPU, &rlcpu); assert(!r);
+ r= getrlimit(RLIMIT_CPU, &rlcpu);
+ if (r) { whatfailed= "getrlimit RLIMIT_CPU"; goto fail; }
+
+ whatfailed= cpulim_gnow();
+ if (whatfailed) goto fail;
+
+ rlcpu.rlim_cur= MAX_CPU_CREDIT;
+
+ whatfailed= cpulim_makepending();
+ if (whatfailed) goto fail;
+ return;
+
+fail:
+ diee("prepare cpu limiter: %s",whatfailed);
+}
+
+/*---------- CPU: set hard scheduling priority ----------*/
+
+static void rtf_acquire_CPU(void) {
+ struct sched_param sp;
+ int r;
+
+ sp.sched_priority= 10;
+ r= sched_setscheduler(0, SCHED_FIFO, &sp);
+ if (r) diee("sched_setscheduler");
+}
+
+/*---------- core ----------*/
- gnow();
- rlcpu.rlim_cur= MAXCREDIT;
- makepending();
+unsigned rtfeats_use= RTFEAT_DEFAULTS;
- for (;;);
+void realtime_priority(void) {
+#define RESOURCE(NAME) \
+ if (rtfeats_use & RTFEAT_##NAME) { \
+ if (!(rtfeats_use & RTFEAT_ALL(NAME))) rtf_limit_##NAME(); \
+ rtf_acquire_##NAME(); \
+ }
+ RESOURCE(MEM);
+ RESOURCE(CPU);
}