render_thread.c
changeset 23 31307efd7e78
child 24 8307d28329ae
--- /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);
+}
+