Browse Source

libreactor: Refactoring and optimization (#6219)

* libreactor: Remove the unused jansson dependency

* libreactor: move json() and plaintext() into helpers.c

* libreactor: move setup() to helpers.c and delete setup.c for the main (platform) implementation

* libreactor: replace setup.c with helpers.c for the libreactor-server implementation

* libreactor: run app as non-root user

* libreactor: use -march=native for app & libclo, and clean up other CFLAGS

libdynamic and libreactor already use -march=native by default

* libreactor: specify git commit hashes to ensure consistency

* libreactor: "optimize" server header

* libreactor: specify git commit hashes for libreactor-server as well

* libreactor: Unify both implementations under a single src folder

Share the same helpers rather than duplicating
Marc Richards 4 years ago
parent
commit
8110fd1afe

+ 13 - 7
frameworks/C/libreactor/Makefile

@@ -1,10 +1,16 @@
-PROG    = libreactor
-OBJS    = src/setup.o src/helpers.o src/main.o
-CFLAGS  = -std=gnu11 -Wall -Wextra -Wpedantic -O3 -g -flto -fcommon
-LDADD   = -lreactor -ldynamic -lclo
+LR       = src/libreactor.o
+LRSRV    = src/libreactor-server.o
+HELPERS  = src/helpers.o
+CFLAGS   = -std=gnu11 -Wall -Wextra -Wpedantic -O3 -g -march=native -flto
+LDADD    = -lreactor -ldynamic -lclo
 
-$(PROG): $(OBJS)
-	$(CC) -o $@ $^ $(CFLAGS) $(LDFLAGS) $(LDADD)
+all: libreactor libreactor-server
+
+libreactor: $(LR) $(HELPERS)
+	$(CC) -o $@ $^ $(CFLAGS) $(LDADD)
+
+libreactor-server: $(LRSRV) $(HELPERS)
+	$(CC) -o $@ $^ $(CFLAGS) $(LDADD)
 
 clean:
-	rm -f $(PROG) $(OBJS)
+	rm -f libreactor libreactor-server $(LR) $(LRSRV) $(HELPERS)

+ 0 - 11
frameworks/C/libreactor/Makefile-server

@@ -1,11 +0,0 @@
-PROG    = libreactor
-OBJS    = src/setup.o src/main.o
-CFLAGS  = -std=gnu11 -Wall -Wextra -Wpedantic -O3 -g -flto -fcommon
-LDFLAGS = -pthread
-LDADD   = -lreactor -ldynamic -lclo
-
-$(PROG): $(OBJS)
-	$(CC) -o $@ $^ $(CFLAGS) $(LDFLAGS) $(LDADD)
-
-clean:
-	rm -f $(PROG) $(OBJS)

+ 2 - 2
frameworks/C/libreactor/README.md

@@ -2,8 +2,8 @@
 
 ### Test Type Implementation Source Code
 
-* [JSON](src/main.c)
-* [PLAINTEXT](src/main.c)
+* [JSON](src/libreactor.c)
+* [PLAINTEXT](src/libreactor.c)
 
 
 ## Important Libraries

+ 16 - 16
frameworks/C/libreactor/libreactor-server.dockerfile

@@ -7,42 +7,42 @@ WORKDIR /build
 
 ENV CC=gcc-10 AR=gcc-ar-10 NM=gcc-nm-10 RANLIB=gcc-ranlib-10
 
-RUN wget -q https://github.com/akheron/jansson/archive/v2.12.tar.gz -O jansson-2.12.tar.gz && \
-    tar xfz jansson-2.12.tar.gz && \
-    cd jansson-2.12 && \
-    autoreconf -fi && \
-    ./configure && \
-    make install
-
 RUN git clone https://github.com/fredrikwidlund/libdynamic && \
     cd libdynamic && \
+    git checkout aee8f053c113 && \
     ./autogen.sh && \
-    ./configure --prefix=/usr CFLAGS="-Wall -Wextra -Wpedantic -O3 -g"&& \
+    ./configure && \
     make install
 
-# Using sed to remove the unused "#include <dynamic.h>" directive since it causes a build error: "unknown type name 'pthread_t'"
+# Remove the unused "#include <dynamic.h>" directive since it causes a build error: "unknown type name 'pthread_t'".
+# Specify configure prefix so libclo gets installed in /usr/lib like libdynamic and libreactor.
+# Add march=native. Both libdynamic and libreactor are already using it.
 RUN wget -q https://github.com/fredrikwidlund/libclo/releases/download/v1.0.0/libclo-1.0.0.tar.gz && \
     tar xfz libclo-1.0.0.tar.gz && \
     cd libclo-1.0.0 && \
     sed -i '/#include <dynamic.h>/d' ./src/clo.c && \
-    ./configure --prefix=/usr CFLAGS="-Wall -Wextra -Wpedantic -O3 -g" && \
+    ./configure --prefix=/usr CFLAGS="-march=native" && \
     make install
 
 RUN git clone https://github.com/fredrikwidlund/libreactor --single-branch --branch release-2.0 libreactor-2 && \
     cd libreactor-2 && \
+    git checkout 63fa717a8047 && \
     ./autogen.sh && \
-    ./configure --prefix=/usr CFLAGS="-Wall -Wextra -Wpedantic -O3 -g -fcommon" && \
+    ./configure && \
     make install
 
-COPY src-server/ /build/src/
-COPY Makefile-server /build/Makefile
+COPY src/ /build/src/
+COPY Makefile /build/Makefile
 
-RUN make
+RUN make libreactor-server
 
 
 FROM ubuntu:20.04
 
 WORKDIR /app
-COPY --from=builder /build/libreactor .
+COPY --from=builder /build/libreactor-server .
+
+RUN groupadd -r libreactor && useradd --no-log-init -r -g libreactor libreactor
+USER libreactor
 
-CMD ["./libreactor"]
+CMD ["./libreactor-server"]

+ 12 - 12
frameworks/C/libreactor/libreactor.dockerfile

@@ -7,37 +7,34 @@ WORKDIR /build
 
 ENV CC=gcc-10 AR=gcc-ar-10 NM=gcc-nm-10 RANLIB=gcc-ranlib-10
 
-RUN wget -q https://github.com/akheron/jansson/archive/v2.12.tar.gz -O jansson-2.12.tar.gz && \
-    tar xfz jansson-2.12.tar.gz && \
-    cd jansson-2.12 && \
-    autoreconf -fi && \
-    ./configure && \
-    make install
-
 RUN git clone https://github.com/fredrikwidlund/libdynamic && \
     cd libdynamic && \
+    git checkout aee8f053c113 && \
     ./autogen.sh && \
-    ./configure --prefix=/usr CFLAGS="-Wall -Wextra -Wpedantic -O3 -g"&& \
+    ./configure && \
     make install
 
-# Using sed to remove the unused "#include <dynamic.h>" directive since it causes a build error: "unknown type name 'pthread_t'"
+# Remove the unused "#include <dynamic.h>" directive since it causes a build error: "unknown type name 'pthread_t'".
+# Specify configure prefix so libclo gets installed in /usr/lib like libdynamic and libreactor.
+# Add march=native. Both libdynamic and libreactor are already using it.
 RUN wget -q https://github.com/fredrikwidlund/libclo/releases/download/v1.0.0/libclo-1.0.0.tar.gz && \
     tar xfz libclo-1.0.0.tar.gz && \
     cd libclo-1.0.0 && \
     sed -i '/#include <dynamic.h>/d' ./src/clo.c && \
-    ./configure --prefix=/usr CFLAGS="-Wall -Wextra -Wpedantic -O3 -g" && \
+    ./configure --prefix=/usr CFLAGS="-march=native" && \
     make install
 
 RUN git clone https://github.com/fredrikwidlund/libreactor --single-branch --branch release-2.0 libreactor-2 && \
     cd libreactor-2 && \
+    git checkout 63fa717a8047 && \
     ./autogen.sh && \
-    ./configure --prefix=/usr CFLAGS="-Wall -Wextra -Wpedantic -O3 -g -fcommon" && \
+    ./configure && \
     make install
 
 COPY src/ /build/src/
 COPY Makefile /build/Makefile
 
-RUN make
+RUN make libreactor
 
 
 FROM ubuntu:20.04
@@ -45,4 +42,7 @@ FROM ubuntu:20.04
 WORKDIR /app
 COPY --from=builder /build/libreactor .
 
+RUN groupadd -r libreactor && useradd --no-log-init -r -g libreactor libreactor
+USER libreactor
+
 CMD ["./libreactor"]

+ 0 - 88
frameworks/C/libreactor/src-server/setup.c

@@ -1,88 +0,0 @@
-#define _GNU_SOURCE
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sched.h>
-#include <sys/resource.h>
-#include <sys/wait.h>
-#include <err.h>
-
-#include <dynamic.h>
-
-struct cpu
-{
-  int       id;
-  pid_t     pid;
-  cpu_set_t set;
-};
-
-static void cpu_list(vector *list, int use_all_cores)
-{
-  struct cpu cpu;
-  char path[1024], buf[1024];
-  size_t i, id;
-  ssize_t n;
-  int fd;
-
-  vector_construct(list, sizeof cpu);
-  for (i = 0;; i ++)
-    {
-      snprintf(path, sizeof path, "/sys/devices/system/cpu/cpu%lu/topology/thread_siblings_list", i);
-      fd = open(path, O_RDONLY);
-      if (fd == -1)
-        break;
-      n = read(fd, buf, sizeof buf - 1);
-      if (n == -1)
-        err(1, "read");
-      buf[n] = 0;
-      close(fd);
-
-      id = strtoul(buf, NULL, 0);
-
-      if (use_all_cores){
-        id = i; /* add every cpu (whether physical or logical) to the list */
-      }
-      else if (id != i){
-        continue; /* ignore sibling CPUs */
-      }
-
-      cpu = (struct cpu) {0};
-      cpu.id = id;
-      CPU_ZERO(&cpu.set);
-      CPU_SET(id, &cpu.set);
-      vector_push_back(list, &cpu);
-    }
-}
-
-void setup()
-{
-  vector list;
-  struct cpu *cpu;
-  size_t i;
-  int e;
-  pid_t cpid;
-
-  signal(SIGPIPE, SIG_IGN);
-  cpu_list(&list, 1);
-
-  for (i = 0; i < vector_size(&list); i++)
-    {
-      cpu = vector_at(&list, i);
-      cpid = fork();
-      if (cpid == -1)
-        err(1, "fork");
-      if (cpid == 0)
-        {
-          e = sched_setaffinity(0, sizeof cpu->set, &cpu->set);
-          if (e == -1)
-            err(1, "sched_setaffinity");
-          return;
-        }
-      cpu->pid = cpid;
-    }
-  wait(NULL);
-  vector_destruct(&list, NULL);
-}

+ 0 - 6
frameworks/C/libreactor/src-server/setup.h

@@ -1,6 +0,0 @@
-#ifndef SETUP_H_INCLUDED
-#define SETUP_H_INCLUDED
-
-void setup();
-
-#endif /* SETUP_H_INCLUDED */

+ 65 - 4
frameworks/C/libreactor/src/helpers.c

@@ -1,17 +1,30 @@
+#define _GNU_SOURCE
+
 #include <stdio.h>
 #include <stdint.h>
 #include <stdlib.h>
 #include <unistd.h>
-#include <limits.h>
 #include <string.h>
+#include <sched.h>
+#include <sys/wait.h>
 #include <err.h>
 
 #include <dynamic.h>
 #include <reactor.h>
 #include <clo.h>
 
+
+#define JSON_PREAMBLE "HTTP/1.1 200 OK\r\n"\
+                      "Server: L\r\n"\
+                      "Content-Type: application/json\r\n"
+
+#define TEXT_PREAMBLE "HTTP/1.1 200 OK\r\n"\
+                      "Server: L\r\n"\
+                      "Content-Type: text/plain\r\n"
+
+
 // Returns the full header and trailing \r\n
-segment http_date_header()
+static segment http_date_header()
 {
   static __thread char date_header[38] = "Date: Thu, 01 Jan 1970 00:00:00 GMT\r\n";
   segment date = http_date(0);
@@ -22,7 +35,7 @@ segment http_date_header()
 
 // Returns the full header and trailing \r\n
 // Also includes the final \r\n separating the headers from response body
-segment http_content_length_header(uint32_t n)
+static segment http_content_length_header(uint32_t n)
 {
   // Max content length limited by uint32_t which is 4294967296 (4GB) or 10 chars when written out.
   // 16 (header name) + 10 (header value) + 4 (newlines) + 1 (null terminator) = 31
@@ -35,7 +48,7 @@ segment http_content_length_header(uint32_t n)
   return (segment) {header, length + 16 + 4};
 }
 
-void write_response(stream *stream, segment preamble, segment body)
+static void write_response(stream *stream, segment preamble, segment body)
 {
   segment date_header = http_date_header(0); // includes header name, value, and \r\n
   segment content_length_header = http_content_length_header(body.size); // includes header name, value, and \r\n\r\n
@@ -50,4 +63,52 @@ void write_response(stream *stream, segment preamble, segment body)
   memcpy(output_buffer_ptr + preamble.size, date_header.base, date_header.size);
   memcpy(output_buffer_ptr + preamble.size + date_header.size, content_length_header.base, content_length_header.size);
   memcpy(output_buffer_ptr + preamble.size + date_header.size + content_length_header.size, body.base, body.size);
+}
+
+void plaintext(server_context *context, char *response)
+{
+  static const segment text_preamble = { .base = TEXT_PREAMBLE, .size = sizeof(TEXT_PREAMBLE) - 1 };
+  write_response(&context->session->stream, text_preamble, segment_string(response));
+}
+
+void json(server_context *context, clo *json_object)
+{
+  static const segment json_preamble = { .base = JSON_PREAMBLE, .size = sizeof(JSON_PREAMBLE) - 1 };
+  static char json_string[4096];
+
+  (void) clo_encode(json_object, json_string, sizeof(json_string));
+  write_response(&context->session->stream, json_preamble, segment_string(json_string));
+}
+
+void setup()
+{
+  int e;
+  pid_t pid;
+  cpu_set_t available_cpus, cpu;
+
+  signal(SIGPIPE, SIG_IGN);
+  CPU_ZERO(&available_cpus);
+  sched_getaffinity(0, sizeof(available_cpus), &available_cpus); // Get set of all available CPUs
+
+  for (int i = 0; i < CPU_SETSIZE; i++)
+  {
+    if (CPU_ISSET(i, &available_cpus))
+    {
+      pid = fork();
+      if (pid == -1)
+        err(1, "fork");
+
+      if (pid == 0)
+      {
+       CPU_ZERO(&cpu);
+       CPU_SET(i, &cpu);
+        e = sched_setaffinity(0, sizeof cpu, &cpu);
+        if (e == -1)
+          err(1, "sched_setaffinity");
+
+        return;
+      }
+    }
+  }
+  wait(NULL);
 }

+ 3 - 3
frameworks/C/libreactor/src/helpers.h

@@ -1,10 +1,10 @@
 #ifndef HELPERS_H_INCLUDED
 #define HELPERS_H_INCLUDED
 
-segment http_date_header(int update);
+void plaintext(server_context *context, char *response);
 
-segment http_content_length_header(uint32_t n);
+void json(server_context *context, clo *json_object);
 
-void write_response(stream *stream, segment preamble, segment body);
+void setup();
 
 #endif /* HELPERS_H_INCLUDED */

+ 12 - 8
frameworks/C/libreactor/src-server/main.c → frameworks/C/libreactor/src/libreactor-server.c

@@ -1,33 +1,37 @@
 #include <stdio.h>
-#include <stdlib.h>
 #include <stdint.h>
+#include <stdlib.h>
 #include <unistd.h>
-#include <string.h>
 #include <err.h>
 
 #include <dynamic.h>
 #include <reactor.h>
 #include <clo.h>
 
-#include "setup.h"
+#include "helpers.h"
+
 
 static core_status server_handler(core_event *event)
 {
-  static char json_msg[4096];
+  static char hello_string[] = "Hello, World!";
+  static char default_string[] = "Hello from libreactor!\n";
+  static clo_pair json_pair[] = {{ .string = "message", .value = { .type = CLO_STRING, .string = "Hello, World!" }}};
+  static clo json_object[] = {{ .type = CLO_OBJECT, .object = json_pair }};
+  static char json_string[4096];
 
   server *server = event->state;
   server_context *context = (server_context *) event->data;
 
   if (event->type == SERVER_REQUEST){
     if (segment_equal(context->request.target, segment_string("/json"))){
-      (void) clo_encode((clo[]) {clo_object({"message", clo_string("Hello, World!")})}, json_msg, sizeof(json_msg));
-      server_ok(context, segment_string("application/json"), segment_string(json_msg));
+      (void) clo_encode(json_object, json_string, sizeof(json_string));
+      server_ok(context, segment_string("application/json"), segment_string(json_string));
     }
     else if (segment_equal(context->request.target, segment_string("/plaintext"))){
-      server_ok(context, segment_string("text/plain"), segment_string("Hello, World!"));
+      server_ok(context, segment_string("text/plain"), segment_string(hello_string));
     }
     else{
-      server_ok(context, segment_string("text/plain"), segment_string("Hello from libreactor!\n"));
+      server_ok(context, segment_string("text/plain"), segment_string(default_string));
     }
     return CORE_OK;
   }

+ 0 - 25
frameworks/C/libreactor/src/main.c → frameworks/C/libreactor/src/libreactor.c

@@ -2,39 +2,14 @@
 #include <stdint.h>
 #include <stdlib.h>
 #include <unistd.h>
-#include <string.h>
-#include <time.h>
 #include <err.h>
 
 #include <dynamic.h>
 #include <reactor.h>
 #include <clo.h>
 
-#include "setup.h"
 #include "helpers.h"
 
-#define JSON_PREAMBLE "HTTP/1.1 200 OK\r\n"\
-                      "Server: libreactor\r\n"\
-                      "Content-Type: application/json\r\n"
-
-#define TEXT_PREAMBLE "HTTP/1.1 200 OK\r\n"\
-                      "Server: libreactor\r\n"\
-                      "Content-Type: text/plain\r\n"
-
-void plaintext(server_context *context, char *response)
-{
-  static const segment text_preamble = { .base = TEXT_PREAMBLE, .size = sizeof(TEXT_PREAMBLE) - 1 };
-  write_response(&context->session->stream, text_preamble, segment_string(response));
-}
-
-void json(server_context *context, clo *json_object)
-{
-  static const segment json_preamble = { .base = JSON_PREAMBLE, .size = sizeof(JSON_PREAMBLE) - 1 };
-  static char json_string[4096];
-
-  (void) clo_encode(json_object, json_string, sizeof(json_string));
-  write_response(&context->session->stream, json_preamble, segment_string(json_string));
-}
 
 static core_status server_handler(core_event *event)
 {

+ 0 - 43
frameworks/C/libreactor/src/setup.c

@@ -1,43 +0,0 @@
-#define _GNU_SOURCE
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <unistd.h>
-#include <sched.h>
-#include <sys/wait.h>
-#include <err.h>
-
-
-void setup()
-{
-  int e;
-  pid_t pid;
-  cpu_set_t available_cpus, cpu;
-
-  signal(SIGPIPE, SIG_IGN);
-  CPU_ZERO(&available_cpus);
-  sched_getaffinity(0, sizeof(available_cpus), &available_cpus); // Get set of all available CPUs
-
-  for (int i = 0; i < CPU_SETSIZE; i++)
-  {
-    if (CPU_ISSET(i, &available_cpus))
-    {
-      pid = fork();
-      if (pid == -1)
-        err(1, "fork");
-
-      if (pid == 0)
-      {
-       CPU_ZERO(&cpu);
-       CPU_SET(i, &cpu);
-        e = sched_setaffinity(0, sizeof cpu, &cpu);
-        if (e == -1)
-          err(1, "sched_setaffinity");
-
-        return;
-      }
-    }
-  }
-  wait(NULL);
-}

+ 0 - 6
frameworks/C/libreactor/src/setup.h

@@ -1,6 +0,0 @@
-#ifndef SETUP_H_INCLUDED
-#define SETUP_H_INCLUDED
-
-void setup();
-
-#endif /* SETUP_H_INCLUDED */