--- /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");