working web_main that uses render_remote
authorTero Marttila <terom@fixme.fi>
Sat, 31 May 2008 19:35:21 +0300
changeset 2 69f8c0acaac7
parent 1 6aa1a0d1f88d
child 3 675be0a45157
working web_main that uses render_remote

committer: Tero Marttila <terom@fixme.fi>
.gitignore
Makefile
common.c
common.h
render.c
render.h
render_node.c
render_remote.c
render_remote.h
web_main.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/.gitignore	Sat May 31 19:35:21 2008 +0300
@@ -0,0 +1,6 @@
+.*.swp
+*.o
+render_file
+render_node
+web_main
+*.png
--- a/Makefile	Sat May 31 02:22:27 2008 +0300
+++ b/Makefile	Sat May 31 19:35:21 2008 +0300
@@ -1,7 +1,7 @@
 LDFLAGS = -L/usr/local/lib -levent -lpng
 CFLAGS = -Wall -g
 
-OBJS = common.o mandelbrot.o render.o
+OBJS = common.o mandelbrot.o render.o render_remote.o
 HEADERS = common.h mandelbrot.h render.h
 
 render_file: ${OBJS}
--- a/common.c	Sat May 31 02:22:27 2008 +0300
+++ b/common.c	Sat May 31 19:35:21 2008 +0300
@@ -21,6 +21,16 @@
     fprintf(stderr, "\n");
 }
 
+void perr (const char *fmt, ...) {
+    va_list va;
+
+    va_start(va, fmt);
+    vfprintf(stderr, fmt, va);
+    va_end(va);
+
+    fprintf(stderr, ": %s\n", strerror(errno));
+}
+
 void perr_exit (const char *fmt, ...) {
     va_list va;
 
--- a/common.h	Sat May 31 02:22:27 2008 +0300
+++ b/common.h	Sat May 31 19:35:21 2008 +0300
@@ -1,5 +1,6 @@
 void die (const char *msg);
 void error (const char *fmt, ...);
+void perr (const char *fmt, ...);
 void perr_exit (const char *fmt, ...);
 void err_exit (const char *fmt, ...);
 
--- a/render.c	Sat May 31 02:22:27 2008 +0300
+++ b/render.c	Sat May 31 19:35:21 2008 +0300
@@ -64,14 +64,3 @@
     return RENDER_OK;
 }
 
-int render_cmd_build (render_t *ctx, struct render_cmd *cmd) {
-   cmd->img_w = htonl(ctx->img_w);
-   cmd->img_h = htonl(ctx->img_h);
-   cmd->x1 = ctx->x1;
-   cmd->y1 = ctx->y1;
-   cmd->x2 = ctx->x2;
-   cmd->y2 = ctx->y2;
-
-   return RENDER_OK;
-}
-
--- a/render.h	Sat May 31 02:22:27 2008 +0300
+++ b/render.h	Sat May 31 19:35:21 2008 +0300
@@ -90,31 +90,4 @@
 int render_io_custom (render_t *ctx, render_ctx_write_cb write_fn, render_ctx_flush_cb flush_fn, void *arg);
 int render_io_stream (render_t *ctx, FILE *fh);
 
-/*
- * Remote rendering
- */
-
-#pragma pack(push)
-#pragma pack(1)
-
-struct render_cmd {
-    u_int32_t   img_w;
-    u_int32_t   img_h;
-
-    double      x1;
-    double      y1;
-    double      x2;
-    double      y2;
-};
-
-#pragma pack(pop)
-
-#define RENDER_PORT 6159
-#define RENDER_CMD_SIZE sizeof(struct render_cmd)
-
-/*
- * build the command to send for remote rendering
- */
-int render_cmd_build (render_t *ctx, struct render_cmd *cmd);
-
 #endif /* RENDER_H */
--- a/render_node.c	Sat May 31 02:22:27 2008 +0300
+++ b/render_node.c	Sat May 31 19:35:21 2008 +0300
@@ -4,6 +4,8 @@
 #include <sys/socket.h>
 #include <stdlib.h>
 
+#include "render.h"
+#include "render_remote.h"
 #include "mandelbrot.h"
 #include "common.h"
 
@@ -71,15 +73,13 @@
 
     // set up the render_ctx
     struct render_ctx ctx;
-    render_ctx_stream(&ctx, fh);
+    render_init(&ctx, RENDER_PNG);
+    render_set_size(&ctx, img_w, img_h);
+    render_region_raw(&ctx, x1, y1, x2, y2);
+    render_io_stream(&ctx, fh);
     
     // render!
-    mandelbrot_render_region_timed(
-        &ctx,
-        img_w, img_h,
-        x1, y1, x2, y2,
-        &duration
-    );
+    mandelbrot_render_timed(&ctx, &duration);
 
     printf("time=%fs\n", duration);
     
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/render_remote.c	Sat May 31 19:35:21 2008 +0300
@@ -0,0 +1,203 @@
+#include <stdlib.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "render_remote.h"
+#include "common.h"
+
+struct remote_render_ctx {
+    struct event ev_conn;
+    struct bufferevent *data_bev;
+
+    #pragma pack(push)
+    #pragma pack(1)
+
+    struct {
+        u_int8_t    mode;
+
+        u_int32_t   img_w;
+        u_int32_t   img_h;
+
+        double      x1;
+        double      y1;
+        double      x2;
+        double      y2;
+    } render_cmd;
+
+    #pragma pack(pop)
+
+    void (*cb_sent)(void *arg);
+    void (*cb_data)(struct evbuffer *buf, void *arg);
+    void (*cb_done)(void *arg);
+    void (*cb_fail)(void *arg);
+
+    void *cb_arg;
+};
+
+void _remote_render_ctx_free (struct remote_render_ctx **ctx) {
+    // free the data_bev
+    if ((*ctx)->data_bev) {
+        bufferevent_free((*ctx)->data_bev);
+        (*ctx)->data_bev = NULL;
+    }
+    
+    // free the context structure
+    free(*ctx);
+    
+    *ctx = NULL;
+}
+
+#define RENDER_FAILED(ctx, desc) \
+    do {                                        \
+        perror(desc);                           \
+        ctx->cb_fail(ctx->cb_arg);              \
+        _remote_render_ctx_free(&ctx);          \
+    } while (0)
+
+void _remote_write (struct bufferevent *bev, void *arg) {
+    struct remote_render_ctx *ctx = arg;
+
+    // the write buffer was drained, so the render command was sent
+    ctx->cb_sent(ctx->cb_arg);
+    
+    // we don't care about EV_WRITE anymore
+    if (bufferevent_disable(ctx->data_bev, EV_WRITE))
+        RENDER_FAILED(ctx, "render_remote: bufferevent_disable");
+
+    // start receiving data
+    if (bufferevent_enable(ctx->data_bev, EV_READ))
+        RENDER_FAILED(ctx, "render_remote: bufferevent_enable");
+}
+
+void _remote_read (struct bufferevent *bev, void *arg) {
+    struct remote_render_ctx *ctx = arg;
+    
+    // pass the bufferevent's input buffer to our callback - libevent doesn't provide any function to access this, but hopefully this works correctly
+    ctx->cb_data(bev->input, ctx->cb_arg);
+}
+
+void _remote_error (struct bufferevent *bev, short what, void *arg) {
+    struct remote_render_ctx *ctx = arg;
+
+    // OH NOES; WHAT DO WE DO!?
+    
+    if (what & EVBUFFER_EOF) {
+        // great!
+        ctx->cb_done(ctx->cb_arg);
+
+    } else if (what & EVBUFFER_ERROR) {
+        // crap.
+        perr("render_remote");
+
+        ctx->cb_fail(ctx->cb_arg);
+
+    } else if (what & EVBUFFER_TIMEOUT) {
+        // ah well
+        error("render_remote: timeout");
+
+        ctx->cb_fail(ctx->cb_arg);
+
+    } else {
+        err_exit("weird bufferevent error code: 0x%02X", what);
+    }
+
+    // free resources
+    _remote_render_ctx_free(&ctx);
+}
+
+void _remote_connected (int fd, short event, void *arg) {
+    struct remote_render_ctx *ctx = arg;
+
+    // set up the read/write bufferevent
+    if ((ctx->data_bev = bufferevent_new(fd, &_remote_read, &_remote_write, &_remote_error, ctx)) == NULL)
+        RENDER_FAILED(ctx, "render_remote: bufferevent_new");
+
+    // write the render command
+    if (bufferevent_write(ctx->data_bev, &ctx->render_cmd, sizeof(ctx->render_cmd)))
+        RENDER_FAILED(ctx, "render_remote: bufferevent_write");
+
+    // wait for it to be written out
+    if (bufferevent_enable(ctx->data_bev, EV_WRITE))
+        RENDER_FAILED(ctx, "render_remote: bufferevent_enable");
+}
+
+void render_cmd_build (render_t *rctx, struct remote_render_ctx *rrctx) {
+    // just copy over the render params to the render_cmd
+    rrctx->render_cmd.mode = rctx->mode;
+    rrctx->render_cmd.img_w = htonl(rctx->img_w);
+    rrctx->render_cmd.img_h = htonl(rctx->img_h);
+    rrctx->render_cmd.x1 = rctx->x1;
+    rrctx->render_cmd.y1 = rctx->y1;
+    rrctx->render_cmd.x2 = rctx->x2;
+    rrctx->render_cmd.y2 = rctx->y2;
+}
+
+int render_remote (
+        render_t *render_ctx,
+        struct sockaddr_storage *remote,
+        void (*cb_sent)(void *arg),
+        void (*cb_data)(struct evbuffer *buf, void *arg),
+        void (*cb_done)(void *arg),
+        void (*cb_fail)(void *arg),
+        void *cb_arg
+) {    
+    // alloc the remote render ctx
+    struct remote_render_ctx *ctx = malloc(sizeof(struct remote_render_ctx));
+
+    if (!ctx) {
+        error("render_remote: malloc");
+        return -1;
+    }
+    
+    // store the provided callback functions
+    ctx->cb_sent = cb_sent;
+    ctx->cb_data = cb_data;
+    ctx->cb_done = cb_done;
+    ctx->cb_fail = cb_fail;
+    ctx->cb_arg = cb_arg;
+    
+    // copy the relevant stuff from the render_ctx
+    render_cmd_build(render_ctx, ctx);
+    
+    // create the socket
+    int sock = socket(remote->ss_family, SOCK_STREAM, 0);
+
+    if (sock < 0) {
+        free(ctx);
+        perror("render_remote: socket");
+        return -1;
+    }
+
+    // mark it as nonblocking
+    if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) {
+        free(ctx);
+        close(sock);
+        perror("render_remote: fcntl");
+        return -1;
+    }
+    
+    // initiate the connect
+    int err = connect(sock, (struct sockaddr *) remote, sizeof(*remote));
+
+    if (err != -1 || errno != EINPROGRESS) {
+        free(ctx);
+        close(sock);
+        perror("render_remote: connect");
+        return -1;
+    }
+
+    // do the libevent dance
+    event_set(&ctx->ev_conn, sock, EV_WRITE, &_remote_connected, ctx);
+
+    if (event_add(&ctx->ev_conn, NULL)) {
+        free(ctx);
+        close(sock);
+        error("render_remote: event_add");
+        return -1;
+    }
+    
+    // success
+    return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/render_remote.h	Sat May 31 19:35:21 2008 +0300
@@ -0,0 +1,28 @@
+#ifndef RENDER_REMOTE_H
+#define RENDER_REMOTE_H
+
+#include <event.h>
+
+#include "render.h"
+
+/*
+ * Execute a render_t operation on a remote render_node
+ */
+
+
+#define RENDER_PORT 6159
+
+/*
+ * Execute the given render operation on the render_node at the given remote address
+ */
+int render_remote (
+        render_t *render_ctx,               // what to render
+        struct sockaddr_storage *remote,    // what render node to use
+        void (*cb_sent)(void *arg),
+        void (*cb_data)(struct evbuffer *buf, void *arg),
+        void (*cb_done)(void *arg),
+        void (*cb_fail)(void *arg),
+        void *cb_arg;
+);
+
+#endif /* RENDER_REMOTE_H */
--- a/web_main.c	Sat May 31 02:22:27 2008 +0300
+++ b/web_main.c	Sat May 31 19:35:21 2008 +0300
@@ -1,48 +1,110 @@
 #include <sys/types.h>
 #include <sys/queue.h>
+#include <sys/socket.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
 
 #include <event.h>
 #include <evhttp.h>
 
-#include "mandelbrot.h"
+#include "render.h"
+#include "render_remote.h"
 #include "common.h"
 
-struct http_render_state {
-    struct evbuffer *buf;
-
-} _http_render_state = { NULL };
+// what render node to use
+static struct sockaddr_storage render_node;
 
-int _http_render_cb (const unsigned char *data, size_t length, void *arg) {
+// info on a render request
+struct render_request {
+    struct evhttp_request *http_request;
 
-    // create a buffer
-    struct evbuffer *buf = evbuffer_new();
+    int headers_sent;
+};
 
-    if (!buf) {
-        error("_http_render_cb: evbuffer_new failed");
-        return RENDER_CB_ERR;
+void _render_sent (void *arg) {
+    struct render_request *ctx = arg;
+
+    // send headers
+    evhttp_add_header(ctx->http_request->output_headers, "Content-Type", "image/png");
+    evhttp_send_reply_start(ctx->http_request, HTTP_OK, "OK");
+
+    ctx->headers_sent = 1;
+
+    printf("render [%p]: sent headers\n", ctx);
+}
+
+void _render_data (struct evbuffer *buf, void *arg) {
+    struct render_request *ctx = arg;
+
+    // send chunk
+    evhttp_send_reply_chunk(ctx->http_request, buf);
+    
+    printf("render [%p]: sent chunk\n", ctx);
+}
+
+void _render_done (void *arg) {
+    struct render_request *ctx = arg;
+
+    // send end
+    evhttp_send_reply_end(ctx->http_request);
+
+    printf("render [%p]: done\n", ctx);
+
+    // clean up
+    free(ctx);
+}
+
+void _render_fail (void *arg) {
+    struct render_request *ctx = arg;
+
+    if (ctx->headers_sent) {
+        // just terminate the PNG stream where it is
+        evhttp_send_reply_end(ctx->http_request);
+    } else {
+        evhttp_send_error(ctx->http_request, 500, "Internal Server Error");
     }
     
-    // add the data to it
-    if (evbuffer_add(buf, data, length)) {
-        error("_http_render_cb: evbuffer_add(%d) failed", length);
-        return RENDER_CB_ERR;
+    printf("render [%p]: failed\n", ctx);
+
+    // clean up
+    free(ctx);
+}
+
+void _http_render_execute (struct evhttp_request *request, u_int32_t img_w, u_int32_t img_h) {
+    // render request context
+    struct render_request *req_ctx = calloc(1, sizeof(struct render_request));
+    
+    req_ctx->http_request = request;
+    req_ctx->headers_sent = 0;
+    
+    // render context
+    render_t rend_ctx;
+    render_init(&rend_ctx, RENDER_PNG);
+    render_set_size(&rend_ctx, img_w, img_h);
+    render_region_full(&rend_ctx);
+    
+    // initiate the remote render operation
+    if (render_remote(&rend_ctx, &render_node,
+        &_render_sent,
+        &_render_data,
+        &_render_done,
+        &_render_fail,
+        req_ctx
+    )) {
+        free(req_ctx);
+        fprintf(stderr, "ERR: render_remote\n");
+        return;
     }
     
-    // send it
-    evhttp_send_reply_chunk((struct evhttp_request *) arg, buf);
-
-    printf("_http_render_cb: sent chunk: %zu\n", length);
-    
-    // free the buffer (!!!)
-    evbuffer_free(buf);
-    
-    // return ok
-    return RENDER_CB_OK;
+    printf("render [%p]: started\n", req_ctx);
 }
 
+/*
+ * HTTP request handler
+ */
 void http_render (struct evhttp_request *request, void *arg) {
     // gather some info about the request
     const char *uri = evhttp_request_uri(request);
@@ -71,22 +133,8 @@
     // request log
     printf("REQ: [%s:%d] uri=%s, img_w=%d, img_h=%d\n", peer_address, peer_port, uri, img_w, img_h);
     
-    // set headers
-    evhttp_add_header(request->output_headers, "Content-Type", "image/png");
-    evhttp_send_reply_start(request, HTTP_OK, "OK");
-    
-    // render
-    struct render_ctx ctx;
-    render_ctx_set(&ctx, &_http_render_cb, NULL, request);
-
-    int err = mandelbrot_render_full(&ctx, img_w, img_h);
-
-    if (err) {
-        fprintf(stderr, "ERR: mandelbrot_render_full(%d, %d): %d\n", img_w, img_h, err);
-    }
-    
-    // reply end
-    evhttp_send_reply_end(request);
+    // do it
+    _http_render_execute(request, img_w, img_h);
 }
 
 int main (void) {
@@ -107,7 +155,14 @@
     
     // add our http request handler
     evhttp_set_cb(http_server, "/render", &http_render, NULL);
-    
+
+    // hack in our render node
+    struct sockaddr_in *render_node_addr = (struct sockaddr_in *) &render_node;
+
+    render_node_addr->sin_family = AF_INET;
+    render_node_addr->sin_port = htons(RENDER_PORT);
+    inet_aton("127.0.0.1", &render_node_addr->sin_addr);
+
     // we shall now run
     printf("RUN 0.0.0.0:8117\n");