--- a/Makefile Tue Jun 17 19:08:05 2008 +0300
+++ b/Makefile Thu Jun 26 01:32:56 2008 +0300
@@ -6,25 +6,26 @@
all: web_main file_main node_main
common.o: common.c common.h
-http.o: http.c http.h common.o
-remote_node.o: remote_node.c remote_node.h common.o
-remote_pool.o: remote_pool.c remote_pool.h common.o
-render.o: render.c render.h common.o
-render_remote.o: render_remote.c render_remote.h common.o
-render_png.o: render_png.c render_png.h common.o
-render_raw.o: render_raw.c render_raw.h common.o
-render_local.o: render_local.c render_local.h common.o
-render_multi.o: render_multi.c render_multi.h render_remote.o common.o
-render_mandelbrot.o: render_mandelbrot.c render_mandelbrot.h common.o
-render_slices.o: render_slices.c render_slices.h common.o
-render_threads.o: render_threads.c render_threads.h common.o
+http.o: http.c http.h common.h
+remote_node.o: remote_node.c remote_node.h common.h
+remote_pool.o: remote_pool.c remote_pool.h common.h
+render.o: render.c render.h common.h
+render_remote.o: render_remote.c render_remote.h common.h
+render_png.o: render_png.c render_png.h common.h
+render_raw.o: render_raw.c render_raw.h common.h
+render_local.o: render_local.c render_local.h common.h render_png.h render_raw.h render_mandelbrot.h
+render_multi.o: render_multi.c render_multi.h render_remote.o common.h
+render_mandelbrot.o: render_mandelbrot.c render_mandelbrot.h common.h
+render_slices.o: render_slices.c render_slices.h common.h
+render_threads.o: render_threads.c render_threads.h common.h
+render_thread.o: render_thread.c render_thread.h common.h render_local.h
file_main.o: file_main.c
-node_main.o: node_main.c
+node_main.o: node_main.c render_net.h
web_main.o: web_main.c
file_main: file_main.o common.o render.o render_raw.o render_png.o render_local.o render_mandelbrot.o
-node_main: node_main.o common.o render.o render_raw.o render_png.o render_local.o render_slices.o render_threads.o render_mandelbrot.o
+node_main: node_main.o common.o render.o render_thread.o render_local.o render_png.o render_raw.o render_mandelbrot.o
web_main: web_main.o common.o http.o render.o render_png.o remote_node.o remote_pool.o render_remote.o render_multi.o render_slices.o
clean :
--- a/common.c Tue Jun 17 19:08:05 2008 +0300
+++ b/common.c Thu Jun 26 01:32:56 2008 +0300
@@ -7,31 +7,33 @@
#include "common.h"
-static void _generic_err_vargs (const char *func, int perr, const char *fmt, va_list va) {
+static void _generic_err_vargs (int use_stderr, const char *func, int perr, const char *fmt, va_list va) {
+ FILE *stream = use_stderr ? stderr : stdin;
+
if (func)
- fprintf(stderr, "%s: ", func);
+ fprintf(stream, "%s: ", func);
- vfprintf(stderr, fmt, va);
+ vfprintf(stream, fmt, va);
if (perr)
- fprintf(stderr, ": %s\n", strerror(errno));
+ fprintf(stream, ": %s\n", strerror(errno));
- fprintf(stderr, "\n");
+ fprintf(stream, "\n");
}
-void _generic_err (const char *func, int perr, const char *fmt, ...) {
+void _generic_err (int use_stderr, const char *func, int perr, const char *fmt, ...) {
va_list va;
va_start(va, fmt);
- _generic_err_vargs(func, perr, fmt, va);
+ _generic_err_vargs(use_stderr, func, perr, fmt, va);
va_end(va);
}
-void _generic_err_exit (const char *func, int perr, const char *fmt, ...) {
+void _generic_err_exit (int use_stderr, const char *func, int perr, const char *fmt, ...) {
va_list va;
va_start(va, fmt);
- _generic_err_vargs(func, perr, fmt, va);
+ _generic_err_vargs(use_stderr, func, perr, fmt, va);
va_end(va);
exit(EXIT_FAILURE);
--- a/common.h Tue Jun 17 19:08:05 2008 +0300
+++ b/common.h Thu Jun 26 01:32:56 2008 +0300
@@ -3,15 +3,14 @@
* error handling
*/
-void _generic_err ( /*int level, */ const char *func, int perr, const char *fmt, ...)
- __attribute__ ((format (printf, 3, 4)));
+void _generic_err ( /*int level, */ int use_stderr, const char *func, int perr, const char *fmt, ...)
+ __attribute__ ((format (printf, 4, 5)));
// needs to be defined as its own function for the noreturn attribute
-void _generic_err_exit ( /* int level, */ const char *func, int perr, const char *fmt, ...)
- __attribute__ ((format (printf, 3, 4)))
+void _generic_err_exit ( /* int level, */ int used_stderr, const char *func, int perr, const char *fmt, ...)
+ __attribute__ ((format (printf, 4, 5)))
__attribute__ ((noreturn));
-/*
enum _debug_level {
DEBUG_FATAL,
DEBUG_ERROR,
@@ -20,18 +19,19 @@
DEBUG_DEBUG,
};
+// not currently used
extern enum _debug_level _cur_debug_level;
-*/
// various kinds of ways to handle an error, 2**3 of them, *g*
-#define error(...) _generic_err( NULL, 0, __VA_ARGS__)
-#define err_exit(...) _generic_err_exit( NULL, 0, __VA_ARGS__)
-#define perr(...) _generic_err( NULL, 1, __VA_ARGS__)
-#define perr_exit(...) _generic_err_exit( NULL, 1, __VA_ARGS__)
-#define err_func(func, ...) _generic_err( func, 0, __VA_ARGS__)
-#define err_func_exit(func, ...) _generic_err_exit( func, 0, __VA_ARGS__)
-#define perr_func(func, ...) _generic_err( func, 1, __VA_ARGS__)
-#define perr_func_exit(func, ...) _generic_err_exit( func, 1, __VA_ARGS__)
+#define info(...) _generic_err( 0, NULL, 0, __VA_ARGS__ )
+#define error(...) _generic_err( 1, NULL, 0, __VA_ARGS__ )
+#define err_exit(...) _generic_err_exit( 1, NULL, 0, __VA_ARGS__ )
+#define perr(...) _generic_err( 1, NULL, 1, __VA_ARGS__ )
+#define perr_exit(...) _generic_err_exit( 1, NULL, 1, __VA_ARGS__ )
+#define err_func(func, ...) _generic_err( 1, func, 0, __VA_ARGS__ )
+#define err_func_exit(func, ...) _generic_err_exit( 1, func, 0, __VA_ARGS__ )
+#define perr_func(func, ...) _generic_err( 1, func, 1, __VA_ARGS__ )
+#define perr_func_exit(func, ...) _generic_err_exit( 1, func, 1, __VA_ARGS__ )
// error(func + colon + msg, ...) + goto error
#define ERROR(...) do { err_func(__func__, __VA_ARGS__); goto error; } while (0)
@@ -47,6 +47,17 @@
#define DEBUG(...) if (0) { }
#endif
+// default is to enable INFO
+#ifndef INFO_ENABLED
+#define INFO_ENABLED 1
+#endif
+
+#if INFO_ENABLED
+#define INFO(...) info(__VA_ARGS__)
+#else
+#define INFO(...) if (0) { }
+#endif
+
/*
* Parse a host:port string.
*
--- a/node_main.c Tue Jun 17 19:08:05 2008 +0300
+++ b/node_main.c Thu Jun 26 01:32:56 2008 +0300
@@ -7,16 +7,19 @@
#include <string.h>
#include <unistd.h>
+#include <assert.h>
+
+#include <event2/event.h>
+#include <event2/event_struct.h>
+#include <event2/event_compat.h>
+#include <event2/bufferevent.h>
+
#include "common.h"
#include "render.h"
-#include "render_remote.h" // for RENDER_PORT_NAME
-#include "render_local.h"
-#include "render_threads.h"
-
-void sigpipe_handler (int signal) {
- /* ignore */
- fprintf(stderr, "SIGPIPE\n");
-}
+#include "render_struct.h"
+#include "render_net.h"
+#include "render_thread.h"
+#include "render_thread_struct.h"
void sigpipe_ignore () {
struct sigaction sigpipe_action;
@@ -28,123 +31,211 @@
perr_exit("sigaction");
}
-int my_fread(FILE *fh, void *ptr, size_t size) {
- int ret = fread(ptr, size, 1, fh);
+/*
+ * State needed to handle a client
+ */
+struct client_info {
+ // the client socket
+ evutil_socket_t socket;
- if (ret == 0) {
- error("EOF");
- return 0;
+ // the read-a-command buffer
+ struct bufferevent *bufev;
- } else if (ret != 1) {
- perror("fread");
- return 0;
+ // the write-a-mandelbrot stream
+ FILE *out_stream;
+
+ // the render_thread op
+ // thread_info.is_active is useful
+ struct render_thread thread_info;
+};
+
+static void client_free (struct client_info *ctx) {
+ // free the read-a-command buffer
+ if (ctx->bufev)
+ bufferevent_free(ctx->bufev);
+
+ // cancel the render thread if needed
+ if (ctx->thread_info.is_active)
+ render_thread_cancel(&ctx->thread_info);
+
+ // deinit it in any case
+ render_thread_deinit(&ctx->thread_info);
+
+ // close the write-a-mandelbrot stream, or just the socket
+ if (ctx->out_stream) {
+ if (fclose(ctx->out_stream))
+ PWARNING("fclose");
+
+ } else if (ctx->socket != -1) {
+ if (close(ctx->socket))
+ PWARNING("close");
+
}
- return 1;
-}
-
-int read_byte (FILE *fh, u_int8_t *byte) {
- return my_fread(fh, byte, sizeof(*byte));
-}
-
-int read_int (FILE *fh, u_int32_t *i) {
- if (!my_fread(fh, i, sizeof(*i)))
- return 0;
-
- *i = ntohl(*i);
-
- return 1;
-}
-
-int read_double (FILE *fh, double *dbl) {
- if (!my_fread(fh, dbl, sizeof(*dbl)))
- return 0;
-
- return 1;
+ // free the client info
+ free(ctx);
}
-void handle_client (int sock) {
- double duration;
- struct render *ctx = NULL;
- FILE *fh;
- u_int8_t mode;
- u_int32_t img_w, img_h;
- double x1, y1, x2, y2;
+static void handle_render_done (struct render_thread *thread_info, void *arg) {
+ struct client_info *ctx = arg;
- struct render_threads *threads_info = NULL;
-
- // open it as a FILE*
- if (!(fh = fdopen(sock, "r+")))
+ // just free it, it takes care of closing it as well
+ client_free(ctx);
+}
+
+static int handle_render_cmd (struct client_info *ctx, struct render_cmd *cmd) {
+ // the render ctx...
+ struct render render_info;
+
+ // open it as a normal FILE*
+ if (!(ctx->out_stream = fdopen(ctx->socket, "w")))
ERROR("fdopen");
-
- // read the parameters
+
+#if INFO_ENABLED
+ INFO("client [%p]: render [%ux%u] (%f, %f) -> (%f, %f)", ctx, cmd->img_w, cmd->img_h, cmd->x1, cmd->y1, cmd->x2, cmd->y2);
+#endif
+
+ // set up the render_info
if (
- !read_byte(fh, &mode)
- || !read_int(fh, &img_w)
- || !read_int(fh, &img_h)
- || !read_double(fh, &x1)
- || !read_double(fh, &y1)
- || !read_double(fh, &x2)
- || !read_double(fh, &y2)
+ render_init(&render_info)
+ || render_set_mode(&render_info, cmd->mode)
+ || render_set_size(&render_info, cmd->img_w, cmd->img_h)
+ || render_region_raw(&render_info, cmd->x1, cmd->y1, cmd->x2, cmd->y2)
+ || render_io_stream(&render_info, ctx->out_stream)
)
- ERROR("read_{byte,int,double}");
-
- printf("RENDER: [%ux%u] (%f, %f) -> (%f, %f): ... ", img_w, img_h, x1, y1, x2, y2);
- fflush(stdout);
-
- // set up the render_ctx
- if (!(ctx = render_alloc()))
- ERROR("render_alloc");
-
- if (render_set_mode(ctx, mode))
- ERROR("render_set_mode");
+ ERROR("render_*");
+
+ // start the render thread
+ if (render_thread_init(&ctx->thread_info, &render_info, &handle_render_done, ctx))
+ ERROR("render_thread_init");
- if (render_set_size(ctx, img_w, img_h))
- ERROR("render_set_size");
-
- if (render_region_raw(ctx, x1, y1, x2, y2))
- ERROR("render_region_raw");
+ // ok, wait for it to complete
+ return 0;
- if (render_io_stream(ctx, fh))
- ERROR("render_io_stream");
+error:
+ // FAAAIL
+ return -1;
+}
+
+static void handle_read (struct bufferevent *bev, void *arg) {
+ struct client_info *ctx = arg;
+ struct render_cmd cmd;
+
+ // meh, just read it in
+ size_t len;
+ // we set a watermark, so this should hold true
+ assert(len = bufferevent_read(bev, &cmd, sizeof(cmd)) == sizeof(cmd));
- // render threaded \o/
- if (!(threads_info = render_threads_alloc(ctx)))
+ // fix the byte order
+ cmd.img_w = ntohl(cmd.img_w);
+ cmd.img_h = ntohl(cmd.img_h);
+
+ // handle it
+ if (handle_render_cmd(ctx, &cmd))
goto error;
- if (render_threads_wait(threads_info))
- goto error;
+ // ok
+ return;
+
+error:
+ client_free(ctx);
+}
+
+static void handle_error (struct bufferevent *bev, short what, void *arg) {
+ struct client_info *ctx = arg;
- printf("done!\n");
-/*
- // render!
- if (render_local(ctx, &duration))
- ERROR("render_local");
+ // read-EOF
+ if ((what & (EVBUFFER_READ | EVBUFFER_EOF)) && ctx->thread_info.is_active) {
+ // this is fine, expected, and doesn't matter
+ return;
+ }
+
+ PWARNING("eventbuffer: %s %s",
+ (what & EVBUFFER_READ) ? "read" : ((what & EVBUFFER_WRITE) ? "write" : "???"),
+ (what & EVBUFFER_EOF) ? "eof" : ((what & EVBUFFER_ERROR) ? "error" : ((what & EVBUFFER_TIMEOUT) ? "timeout" : "???"))
+ );
- printf("time=%fs\n", duration);
-*/
+ client_free(ctx);
+}
+
+static void handle_accept (evutil_socket_t fd, short event, void *arg) {
+ struct client_info *ctx = NULL;
+
+ evutil_socket_t socket = -1;
+ struct sockaddr_storage addr;
+ socklen_t addr_len;
- // fall through to just clean up normally
+ // arg is NULL and unused
+ (void) arg;
+
+ // accept the connection
+ addr_len = sizeof(struct sockaddr_storage);
+
+ if ((socket = accept(fd, (struct sockaddr *) &addr, &addr_len)) == -1)
+ PERROR("accept");
+
+#if INFO_ENABLED
+ assert(INET_ADDRSTRLEN < INET6_ADDRSTRLEN);
+
+ char addr_buf[INET6_ADDRSTRLEN];
+ const char *addr_str;
+ short nport;
+
+ if (addr.ss_family == AF_UNIX)
+ addr_str = "local";
+ else if (addr.ss_family == AF_INET || addr.ss_family == AF_INET6) {
+ const void *src;
+
+ if (addr.ss_family == AF_INET) {
+ src = &(((struct sockaddr_in *) &addr)->sin_addr);
+ nport = ((struct sockaddr_in *) &addr)->sin_port;
+ } else {
+ src = &(((struct sockaddr_in6 *) &addr)->sin6_addr);
+ nport = ((struct sockaddr_in6 *) &addr)->sin6_port;
+ }
+
+ if (!(inet_ntop(addr.ss_family, src, addr_buf, sizeof(addr_buf))))
+ PERROR("inet_ntop");
+
+ addr_str = addr_buf;
+ }
+
+ INFO("ACCEPT: %s:%hu\n", addr_str, ntohs(nport));
+
+#endif
+
+ // alloc a new client_info
+ if (!(ctx = calloc(1, sizeof(*ctx))))
+ ERROR("calloc");
+
+ // store the socket
+ ctx->socket = socket;
+
+ // then a bufferevent so that we can read in the command
+ if (!(ctx->bufev = bufferevent_new(ctx->socket, &handle_read, NULL, &handle_error, ctx)))
+ ERROR("bufferevent_new");
+
+ // and enable it for read only
+ if (bufferevent_enable(ctx->bufev, EV_READ))
+ ERROR("bufferevent_enable");
+
+ // set the watermark for receiving the render_cmd
+ bufferevent_setwatermark(ctx->bufev, EV_READ, sizeof(struct render_cmd), 0);
+
+ // now we just wait for the cmd...
error:
if (ctx)
- render_free(ctx);
-
- if (threads_info)
- render_threads_free(threads_info);
-
- // close the FILE* and socket
- fclose(fh);
-
- return;
+ client_free(ctx);
+ else if (socket != -1)
+ close(socket);
}
-
int main (int argc, char** argv) {
- int ssock, sock;
+ struct event_base *ev_base;
+ int ssock;
struct sockaddr_in addr;
- socklen_t addr_len;
// parse arguments
int opt;
@@ -171,6 +262,10 @@
if (!(port = atoi(port_name)))
ERROR("invalid port: %s", port_name);
+
+ // init libevent
+ if (!(ev_base = event_init()))
+ FATAL("event_init");
// create the socket
if ((ssock = socket(PF_INET, SOCK_STREAM, 0)) == -1)
@@ -186,26 +281,32 @@
if (listen(ssock, 1) == -1)
PERROR("listen");
+ // create the listen event
+ struct event listen_ev;
+
+ event_set(&listen_ev, ssock, EV_READ, &handle_accept, NULL);
+
+ if (event_add(&listen_ev, NULL))
+ PERROR("event_add");
+
// ignore sigpipe
sigpipe_ignore();
- // main accept loop
+ // run the libevent mainloop
printf("RUN: %s:%hu\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
-
- while (1) {
- addr_len = sizeof(struct sockaddr_in);
+
+ if (event_base_dispatch(ev_base))
+ WARNING("event_dispatch");
- // accept a new client
- if ((sock = accept(ssock, (struct sockaddr *) &addr, &addr_len)) == -1)
- PERROR("accept");
-
- printf("ACCEPT: %s:%hu\n", inet_ntoa(addr.sin_addr), addr.sin_port);
-
- // handle their resquest
- handle_client(sock);
- }
+ printf("SHUTDOWN\n");
+
+ event_base_free(ev_base);
+
+ // succesful exit
+ return EXIT_SUCCESS;
error:
- return 1;
+ // failure
+ return EXIT_FAILURE;
}
--- a/remote_node.c Tue Jun 17 19:08:05 2008 +0300
+++ b/remote_node.c Thu Jun 26 01:32:56 2008 +0300
@@ -5,7 +5,7 @@
#include <assert.h>
#include "common.h"
-#include "render_remote.h"
+#include "render_net.h"
#include "remote_node.h"
int remote_node_init (struct remote_node *node_info, const char *hostname, const char *portname) {
--- a/render.c Tue Jun 17 19:08:05 2008 +0300
+++ b/render.c Thu Jun 26 01:32:56 2008 +0300
@@ -4,10 +4,10 @@
#include "render_struct.h"
#include "render.h"
-int render_init (struct render *ctx, int mode) {
+int render_init (struct render *ctx) {
memset(ctx, 0, sizeof(*ctx));
-
- return render_set_mode(ctx, mode);
+
+ return 0;
}
struct render *render_alloc () {
--- a/render.h Tue Jun 17 19:08:05 2008 +0300
+++ b/render.h Thu Jun 26 01:32:56 2008 +0300
@@ -36,7 +36,7 @@
/*
* Clear out the value of the given render context
*/
-int render_init (struct render *ctx, int mode);
+int render_init (struct render *ctx);
/*
* What kind of image to render, PNG or RAW?
--- a/render_local.c Tue Jun 17 19:08:05 2008 +0300
+++ b/render_local.c Thu Jun 26 01:32:56 2008 +0300
@@ -15,15 +15,12 @@
unsigned char *rowbuf = NULL;
struct render_png png_ctx;
struct render_raw raw_ctx;
+ int must_deinit = 0;
clock_t t1, t2;
if (duration)
*duration = -1;
- // ugh
- memset(&png_ctx, 0, sizeof(png_ctx));
- memset(&raw_ctx, 0, sizeof(raw_ctx));
-
// alloc the memory buffer
if (!(rowbuf = malloc(render->img_w)))
ERROR("malloc");
@@ -32,9 +29,13 @@
switch (render->mode) {
case RENDER_PNG :
// the render_png stuff
+ memset(&png_ctx, 0, sizeof(png_ctx));
+
if (render_png_init(&png_ctx, render))
ERROR("render_png_init");
-
+
+ must_deinit = RENDER_PNG;
+
// set render_* to use the render_png
if (render_local_mem(render, &rowbuf, (int(*)(void *arg, unsigned char *)) &render_png_row, &png_ctx))
ERROR("render_local_mem");
@@ -43,8 +44,12 @@
case RENDER_RAW :
// the render_raw stuff
+ memset(&raw_ctx, 0, sizeof(raw_ctx));
+
if (render_raw_init(&raw_ctx, render))
ERROR("render_raw_init");
+
+ must_deinit = RENDER_RAW;
// set render_* to use the render_raw
if (render_local_mem(render, &rowbuf, (int(*)(void *arg, unsigned char *)) &render_raw_row, &raw_ctx))
@@ -73,9 +78,12 @@
error:
free(rowbuf);
+
+ if (must_deinit == RENDER_PNG)
+ render_png_deinit(&png_ctx);
- render_png_deinit(&png_ctx);
- render_raw_deinit(&raw_ctx);
+ else if (must_deinit == RENDER_RAW)
+ render_raw_deinit(&raw_ctx);
return -1;
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/render_net.h Thu Jun 26 01:32:56 2008 +0300
@@ -0,0 +1,21 @@
+#include <sys/types.h>
+
+#define RENDER_PORT_NAME "6159"
+
+#pragma pack(push)
+#pragma pack(1)
+
+struct render_cmd {
+ u_int8_t mode;
+
+ u_int32_t img_w;
+ u_int32_t img_h;
+
+ double x1;
+ double y1;
+ double x2;
+ double y2;
+};
+
+#pragma pack(pop)
+
--- a/render_remote.c Tue Jun 17 19:08:05 2008 +0300
+++ b/render_remote.c Thu Jun 26 01:32:56 2008 +0300
@@ -9,8 +9,9 @@
#include <event2/event_struct.h>
#include <event2/bufferevent.h>
-#include "render_struct.h" // for render_cmd_build
+#include "render_struct.h"
#include "render_remote.h"
+#include "render_net.h"
#include "common.h"
struct render_remote {
@@ -21,22 +22,8 @@
struct event ev_connect, ev_data;
struct bufferevent *bev_data;
- #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)
+ // the command
+ struct render_cmd render_cmd;
// have we sent the command yet?
int cmd_sent;
--- a/render_remote.h Tue Jun 17 19:08:05 2008 +0300
+++ b/render_remote.h Thu Jun 26 01:32:56 2008 +0300
@@ -11,9 +11,6 @@
* Execute a render operation on a remote render_node
*/
-
-#define RENDER_PORT_NAME "6159"
-
struct render_remote;
/*
--- a/render_slices.c Tue Jun 17 19:08:05 2008 +0300
+++ b/render_slices.c Thu Jun 26 01:32:56 2008 +0300
@@ -45,8 +45,11 @@
// should probably use memcpy instead...
if (
- render_init (&ctx->slices[0].render_info, RENDER_RAW)
- || render_init (&ctx->slices[1].render_info, RENDER_RAW)
+ render_init (&ctx->slices[0].render_info)
+ || render_init (&ctx->slices[1].render_info)
+
+ || render_set_mode (&ctx->slices[0].render_info, RENDER_RAW)
+ || render_set_mode (&ctx->slices[1].render_info, RENDER_RAW)
|| render_set_size (&ctx->slices[0].render_info, render->img_w, render->img_h)
|| render_set_size (&ctx->slices[1].render_info, render->img_w, render->img_h)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/render_thread.c Thu Jun 26 01:32:56 2008 +0300
@@ -0,0 +1,200 @@
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+
+#include <pthread.h>
+#include <event2/event.h>
+
+#include "common.h"
+#include "render_thread.h"
+#include "render_thread_struct.h"
+#include "render_local.h"
+
+static void *_render_thread_func (void *arg) {
+ struct render_thread *ctx = arg;
+
+ // measure how long it takes
+ double duration;
+
+ // render it...
+ if (render_local(&ctx->render_info, &duration))
+ goto error;
+
+#ifdef DEBUG_INFO
+ u_int32_t img_w, img_h;
+
+ render_get_size(&ctx->render_info, &img_w, &img_h);
+
+ // report the duration
+ INFO("rendered [%ux%u] in %f seconds", img_w, img_h, duration);
+#endif
+
+ // notify completion, writeall()
+ ssize_t ret;
+ char *buf = (void *) &ctx;
+ size_t len = sizeof(ctx);
+
+ do {
+ ret = write(ctx->notify_fd, buf, len);
+
+ if (ret > 0) {
+ buf += ret;
+ len -= ret;
+ }
+ } while ((ret == -1 && errno == EINTR) || len > 0);
+
+ if (ret == -1)
+ PERROR("write");
+
+ // done...
+ return NULL;
+
+error:
+ return ctx;
+}
+
+static void _render_thread_done (evutil_socket_t fd, short what, void *arg) {
+ struct render_thread *ctx = arg;
+ void *thread_return;
+
+ // make a lazy effort to read the contents of the pipe
+ struct render_thread *ctx2;
+ char *buf = (void *) &ctx2;
+ size_t len = sizeof(ctx);
+
+ ssize_t ret;
+
+ if ((ret = read(fd, buf, len)) == -1)
+ PWARNING("read");
+
+ else if (ret != len)
+ WARNING("short read");
+
+ else if (ctx2 != ctx)
+ FATAL("wrong ctx: %p <> %p", ctx, ctx2);
+
+ // close the pipe
+ if (close(fd))
+ PWARNING("close(pipe-read)");
+
+ if (close(ctx->notify_fd))
+ PWARNING("close(pipe-write)");
+
+ // join the thread and check the return value
+ if (pthread_join(ctx->thread_id, &thread_return))
+ PWARNING("pthread_join");
+ else if (thread_return == PTHREAD_CANCELED)
+ PWARNING("PTHREAD_CANCELED");
+ else if (thread_return != NULL)
+ PWARNING("thread_return != NULL");
+
+ // mark it as done
+ ctx->is_active = 0;
+
+ // call our callback
+ ctx->cb_func(ctx, ctx->cb_arg);
+}
+
+int render_thread_init (struct render_thread *ctx, struct render *render_info, render_thread_done_cb cb_func, void *cb_arg) {
+ // we need to copy over the render info, as it will probably be invalidated before the thread finishes
+ memcpy(&ctx->render_info, render_info, sizeof(ctx->render_info));
+
+ // the cb stuff
+ ctx->cb_func = cb_func;
+ ctx->cb_arg = cb_arg;
+
+ // the notify pipe
+ int pipefds[2];
+
+ if (pipe(pipefds))
+ PERROR("pipe");
+
+ // the write end...
+ ctx->notify_fd = pipefds[1];
+
+ // the read end...
+ event_set(&ctx->ev, pipefds[0], EV_READ, &_render_thread_done, ctx);
+
+ if (event_add(&ctx->ev, NULL))
+ PERROR("event_add");
+
+ // spawn the render thread
+ if (pthread_create(&ctx->thread_id, NULL, &_render_thread_func, ctx))
+ PERROR("pthread_create(manager_func)");
+
+ // mark it as active
+ ctx->is_active = 1;
+
+ return 0;
+
+error:
+ render_thread_deinit(ctx);
+ return -1;
+}
+
+void render_thread_cancel (struct render_thread *ctx) {
+ assert(ctx->is_active);
+
+ // we don't care about joining the thread, detach
+ // XXX if already detached, it won't get canceled here
+ if (pthread_detach(ctx->thread_id))
+ PWARNING("pthread_detach");
+
+ // cancel it
+ else if (pthread_cancel(ctx->thread_id))
+ PWARNING("pthread_cancel");
+
+ // XXX: should we actually join it before continuing?
+
+ // slam the pipe shut in front of its face
+ if (close(event_get_fd(&ctx->ev)))
+ PWARNING("close");
+
+ if (close(ctx->notify_fd))
+ PWARNING("close");
+
+ // pipe's closed, and don't call _render_thread_func
+ event_del(&ctx->ev);
+
+ // we are now ready for deinit
+ ctx->is_active = 0;
+}
+
+void render_thread_deinit (struct render_thread *ctx) {
+ assert(!ctx->is_active);
+
+ // nothing to do
+}
+
+struct render_thread *render_thread_alloc (struct render *render_info, render_thread_done_cb cb_func, void *cb_arg) {
+ struct render_thread *ctx = NULL;
+
+ if (!(ctx = calloc(1, sizeof(*ctx))))
+ ERROR("calloc");
+
+ // flag it for free()ing
+ ctx->owned_by_me = 1;
+
+ // init with silent fall-through
+ if (render_thread_init(ctx, render_info, cb_func, cb_arg))
+ goto error;
+
+ // success
+ return ctx;
+
+error:
+ // XXX: do other modules do this?
+ free(ctx);
+
+ return NULL;
+}
+
+void render_thread_free (struct render_thread *ctx) {
+ render_thread_deinit(ctx);
+
+ if (ctx->owned_by_me)
+ free(ctx);
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/render_thread.h Thu Jun 26 01:32:56 2008 +0300
@@ -0,0 +1,19 @@
+#ifndef RENDER_THREAD_H
+#define RENDER_THREAD_H
+
+#include "render.h"
+
+/*
+ * Compared to render_threads, this just uses a single thread to render the image and the PNG, which is better for
+ * rendering many small images
+ */
+
+struct render_thread;
+
+typedef void (*render_thread_done_cb)(struct render_thread *ctx, void *arg);
+
+struct render_thread *render_thread_alloc (struct render *render, render_thread_done_cb cb_func, void *cb_arg);
+
+void render_thread_free(struct render_thread *ctx);
+
+#endif /* RENDER_THREAD_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/render_thread_struct.h Thu Jun 26 01:32:56 2008 +0300
@@ -0,0 +1,39 @@
+#ifndef RENDER_THREAD_STRUCT_H
+#define RENDER_THREAD_STRUCT_H
+
+#include <pthread.h>
+#include <event2/event_struct.h>
+
+#include "render_thread.h"
+#include "render_struct.h"
+
+struct render_thread {
+ int owned_by_me;
+
+ // the thread identifier
+ pthread_t thread_id;
+
+ // the render op, this includes the socket/stream
+ struct render render_info;
+
+ // completion callback
+ render_thread_done_cb cb_func;
+ void *cb_arg;
+
+ // what fd to use for completion notification
+ int notify_fd;
+
+ // the notification event
+ struct event ev;
+
+ // already canceled/done?
+ int is_active;
+};
+
+int render_thread_init (struct render_thread *ctx, struct render *render_info, render_thread_done_cb cb_func, void *cb_arg);
+
+void render_thread_cancel (struct render_thread *ctx);
+
+void render_thread_deinit (struct render_thread *ctx);
+
+#endif /* RENDER_THREAD_STRUCT_H */
--- a/render_threads.c Tue Jun 17 19:08:05 2008 +0300
+++ b/render_threads.c Thu Jun 26 01:32:56 2008 +0300
@@ -3,7 +3,7 @@
#include <pthread.h>
-#define DEBUG_ENABLED
+//#define DEBUG_ENABLED
#include "common.h"
#include "render_threads.h"