--- /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);
+}
+