terom@23: #include terom@23: #include terom@23: #include terom@23: #include terom@23: #include terom@23: terom@23: #include terom@23: #include terom@23: terom@23: #include "common.h" terom@23: #include "render_thread.h" terom@23: #include "render_thread_struct.h" terom@23: #include "render_local.h" terom@23: terom@23: static void *_render_thread_func (void *arg) { terom@23: struct render_thread *ctx = arg; terom@23: terom@23: // measure how long it takes terom@23: double duration; terom@23: terom@24: struct render_local local_ctx; terom@24: terom@24: // initialize it... terom@24: if (!(ctx->err = render_local_init(&local_ctx, &ctx->render_info))) { terom@24: // setup the cancel exit handlers terom@24: pthread_cleanup_push( (void (*)(void *)) render_local_deinit, &local_ctx); terom@23: terom@24: // render it... terom@24: ctx->err = render_local_run(&local_ctx, &duration); terom@24: terom@24: if (!ctx->err) { terom@24: #if INFO_ENABLED terom@24: u_int32_t img_w, img_h; terom@23: terom@24: render_get_size(&ctx->render_info, &img_w, &img_h); terom@23: terom@24: // report the duration terom@24: INFO("rendered [%ux%u] in %f seconds", img_w, img_h, duration); terom@23: #endif terom@24: } terom@24: terom@24: // cleanup terom@24: pthread_cleanup_pop(1); terom@24: } terom@23: terom@23: // notify completion, writeall() terom@23: ssize_t ret; terom@23: char *buf = (void *) &ctx; terom@23: size_t len = sizeof(ctx); terom@23: terom@23: do { terom@23: ret = write(ctx->notify_fd, buf, len); terom@23: terom@23: if (ret > 0) { terom@23: buf += ret; terom@23: len -= ret; terom@23: } terom@23: } while ((ret == -1 && errno == EINTR) || len > 0); terom@23: terom@23: if (ret == -1) terom@23: PERROR("write"); terom@23: terom@23: // done... terom@23: return NULL; terom@23: terom@23: error: terom@24: // if notifying of completion failed... terom@23: return ctx; terom@23: } terom@23: terom@23: static void _render_thread_done (evutil_socket_t fd, short what, void *arg) { terom@23: struct render_thread *ctx = arg; terom@23: void *thread_return; terom@23: terom@24: // join the thread and check the return value terom@24: if (pthread_join(ctx->thread_id, &thread_return)) terom@24: PWARNING("pthread_join"); terom@24: else if (thread_return == PTHREAD_CANCELED) terom@24: PWARNING("PTHREAD_CANCELED"); terom@24: else if (thread_return != NULL) terom@24: PWARNING("thread_return != NULL"); terom@24: terom@23: // make a lazy effort to read the contents of the pipe terom@23: struct render_thread *ctx2; terom@23: char *buf = (void *) &ctx2; terom@23: size_t len = sizeof(ctx); terom@23: terom@23: ssize_t ret; terom@23: terom@23: if ((ret = read(fd, buf, len)) == -1) terom@23: PWARNING("read"); terom@23: terom@23: else if (ret != len) terom@23: WARNING("short read"); terom@23: terom@23: else if (ctx2 != ctx) terom@23: FATAL("wrong ctx: %p <> %p", ctx, ctx2); terom@23: terom@23: // close the pipe terom@23: if (close(fd)) terom@23: PWARNING("close(pipe-read)"); terom@23: terom@23: if (close(ctx->notify_fd)) terom@23: PWARNING("close(pipe-write)"); terom@23: terom@23: // mark it as done terom@23: ctx->is_active = 0; terom@23: terom@23: // call our callback terom@24: ctx->cb_func(ctx, ctx->err, ctx->cb_arg); terom@23: } terom@23: terom@23: int render_thread_init (struct render_thread *ctx, struct render *render_info, render_thread_done_cb cb_func, void *cb_arg) { terom@23: // we need to copy over the render info, as it will probably be invalidated before the thread finishes terom@23: memcpy(&ctx->render_info, render_info, sizeof(ctx->render_info)); terom@23: terom@23: // the cb stuff terom@23: ctx->cb_func = cb_func; terom@23: ctx->cb_arg = cb_arg; terom@23: terom@23: // the notify pipe terom@23: int pipefds[2]; terom@23: terom@23: if (pipe(pipefds)) terom@23: PERROR("pipe"); terom@23: terom@23: // the write end... terom@23: ctx->notify_fd = pipefds[1]; terom@23: terom@23: // the read end... terom@23: event_set(&ctx->ev, pipefds[0], EV_READ, &_render_thread_done, ctx); terom@23: terom@23: if (event_add(&ctx->ev, NULL)) terom@23: PERROR("event_add"); terom@23: terom@23: // spawn the render thread terom@23: if (pthread_create(&ctx->thread_id, NULL, &_render_thread_func, ctx)) terom@23: PERROR("pthread_create(manager_func)"); terom@23: terom@23: // mark it as active terom@23: ctx->is_active = 1; terom@23: terom@23: return 0; terom@23: terom@23: error: terom@23: render_thread_deinit(ctx); terom@23: return -1; terom@23: } terom@23: terom@23: void render_thread_cancel (struct render_thread *ctx) { terom@23: assert(ctx->is_active); terom@23: terom@23: // we don't care about joining the thread, detach terom@23: // XXX if already detached, it won't get canceled here terom@23: if (pthread_detach(ctx->thread_id)) terom@23: PWARNING("pthread_detach"); terom@23: terom@23: // cancel it terom@23: else if (pthread_cancel(ctx->thread_id)) terom@23: PWARNING("pthread_cancel"); terom@23: terom@23: // XXX: should we actually join it before continuing? terom@23: terom@23: // slam the pipe shut in front of its face terom@23: if (close(event_get_fd(&ctx->ev))) terom@23: PWARNING("close"); terom@23: terom@23: if (close(ctx->notify_fd)) terom@23: PWARNING("close"); terom@23: terom@23: // pipe's closed, and don't call _render_thread_func terom@23: event_del(&ctx->ev); terom@23: terom@23: // we are now ready for deinit terom@23: ctx->is_active = 0; terom@23: } terom@23: terom@23: void render_thread_deinit (struct render_thread *ctx) { terom@23: assert(!ctx->is_active); terom@23: terom@23: // nothing to do terom@23: } terom@23: terom@23: struct render_thread *render_thread_alloc (struct render *render_info, render_thread_done_cb cb_func, void *cb_arg) { terom@23: struct render_thread *ctx = NULL; terom@23: terom@23: if (!(ctx = calloc(1, sizeof(*ctx)))) terom@23: ERROR("calloc"); terom@23: terom@23: // flag it for free()ing terom@23: ctx->owned_by_me = 1; terom@23: terom@23: // init with silent fall-through terom@23: if (render_thread_init(ctx, render_info, cb_func, cb_arg)) terom@23: goto error; terom@23: terom@23: // success terom@23: return ctx; terom@23: terom@23: error: terom@23: // XXX: do other modules do this? terom@23: free(ctx); terom@23: terom@23: return NULL; terom@23: } terom@23: terom@23: void render_thread_free (struct render_thread *ctx) { terom@23: render_thread_deinit(ctx); terom@23: terom@23: if (ctx->owned_by_me) terom@23: free(ctx); terom@23: } terom@23: