diff options
-rw-r--r-- | dimension/dimension.in | 10 | ||||
-rw-r--r-- | libdimension-python/dimension.pxd | 22 | ||||
-rw-r--r-- | libdimension-python/dimension.pyx | 53 | ||||
-rw-r--r-- | libdimension/Makefile.am | 6 | ||||
-rw-r--r-- | libdimension/bench/prtree.c | 2 | ||||
-rw-r--r-- | libdimension/dimension-internal.h | 2 | ||||
-rw-r--r-- | libdimension/dimension.h | 2 | ||||
-rw-r--r-- | libdimension/dimension/future.h (renamed from libdimension/dimension/progress.h) | 30 | ||||
-rw-r--r-- | libdimension/dimension/png.h | 10 | ||||
-rw-r--r-- | libdimension/dimension/raytrace.h | 4 | ||||
-rw-r--r-- | libdimension/future-impl.h (renamed from libdimension/progress-impl.h) | 29 | ||||
-rw-r--r-- | libdimension/future.c | 164 | ||||
-rw-r--r-- | libdimension/png.c | 60 | ||||
-rw-r--r-- | libdimension/progress.c | 165 | ||||
-rw-r--r-- | libdimension/raytrace.c | 26 | ||||
-rw-r--r-- | libdimension/tests/prtree.c | 2 | ||||
-rw-r--r-- | libdimension/tests/render.c | 6 | ||||
-rw-r--r-- | libdimension/threads.c | 13 | ||||
-rw-r--r-- | libdimension/threads.h | 4 |
19 files changed, 303 insertions, 307 deletions
diff --git a/dimension/dimension.in b/dimension/dimension.in index dc2430c..0ea41e4 100644 --- a/dimension/dimension.in +++ b/dimension/dimension.in @@ -119,7 +119,7 @@ if args.output is None: args.output = noext + ".png" # Display a progress bar -def progress_bar(str, progress): +def progress_bar(str, future): try: if not args.quiet: print(str, end = " ") @@ -128,21 +128,21 @@ def progress_bar(str, progress): term_width = terminal_width() width = term_width - (len(str) + 1)%term_width for i in range(width): - progress.wait((i + 1)/width) + future.wait((i + 1)/width) print(".", end = "") sys.stdout.flush() print() sys.stdout.flush() - progress.finish() + future.join() except KeyboardInterrupt: print() sys.stdout.flush() - progress.cancel() + future.cancel() try: - progress.finish() + future.join() except RuntimeError: # Swallow the failure exception pass diff --git a/libdimension-python/dimension.pxd b/libdimension-python/dimension.pxd index b70508c..403b327 100644 --- a/libdimension-python/dimension.pxd +++ b/libdimension-python/dimension.pxd @@ -64,16 +64,16 @@ cdef extern from "../libdimension/dimension.h": void dmnsn_array_remove(dmnsn_array *array, size_t i) void dmnsn_array_apply(dmnsn_array *array, dmnsn_callback_fn *callback) - ############ - # Progress # - ############ + ########### + # Futures # + ########### - ctypedef struct dmnsn_progress + ctypedef struct dmnsn_future - int dmnsn_finish_progress(dmnsn_progress *progress) - void dmnsn_cancel_progress(dmnsn_progress *progress) - double dmnsn_get_progress(dmnsn_progress *progress) - void dmnsn_wait_progress(dmnsn_progress *progress, double prog) + int dmnsn_future_join(dmnsn_future *future) + void dmnsn_future_cancel(dmnsn_future *future) + double dmnsn_future_progress(dmnsn_future *future) + void dmnsn_future_wait(dmnsn_future *future, double progress) ########## # Timers # @@ -194,9 +194,9 @@ cdef extern from "../libdimension/dimension.h": int dmnsn_png_optimize_canvas(dmnsn_canvas *canvas) int dmnsn_png_write_canvas(dmnsn_canvas *canvas, FILE *file) - dmnsn_progress *dmnsn_png_write_canvas_async(dmnsn_canvas *canvas, FILE *file) + dmnsn_future *dmnsn_png_write_canvas_async(dmnsn_canvas *canvas, FILE *file) dmnsn_canvas *dmnsn_png_read_canvas(FILE *file) - dmnsn_progress *dmnsn_png_read_canvas_async(dmnsn_canvas **canvas, FILE *file) + dmnsn_future *dmnsn_png_read_canvas_async(dmnsn_canvas **canvas, FILE *file) int dmnsn_gl_optimize_canvas(dmnsn_canvas *canvas) int dmnsn_gl_write_canvas(dmnsn_canvas *canvas) @@ -391,7 +391,7 @@ cdef extern from "../libdimension/dimension.h": void dmnsn_delete_scene(dmnsn_scene *scene) void dmnsn_raytrace_scene(dmnsn_scene *scene) - dmnsn_progress *dmnsn_raytrace_scene_async(dmnsn_scene *scene) + dmnsn_future *dmnsn_raytrace_scene_async(dmnsn_scene *scene) cdef extern from "platform.h": unsigned int dmnsn_terminal_width() diff --git a/libdimension-python/dimension.pyx b/libdimension-python/dimension.pyx index 3fff622..7f9fda0 100644 --- a/libdimension-python/dimension.pyx +++ b/libdimension-python/dimension.pyx @@ -46,55 +46,55 @@ def terminal_width(): """Return the width of the terminal, if present.""" return dmnsn_terminal_width() -############ -# Progress # -############ +########### +# Futures # +########### -cdef class Progress: - cdef dmnsn_progress *_progress +cdef class Future: + cdef dmnsn_future *_future cdef _finalizer def __cinit__(self): - self._progress = NULL + self._future = NULL self._finalizer = None def __init__(self): - raise RuntimeError("attempt to create a Progress object.") + raise RuntimeError("attempt to create a Future object.") def __dealloc__(self): - if self._progress != NULL: - self.finish() + if self._future != NULL: + self.join() - def finish(self): + def join(self): self._assert_unfinished() try: - if dmnsn_finish_progress(self._progress) != 0: + if dmnsn_future_join(self._future) != 0: raise RuntimeError("background task failed.") if self._finalizer is not None: self._finalizer() finally: - self._progress = NULL + self._future = NULL def cancel(self): self._assert_unfinished() - dmnsn_cancel_progress(self._progress) + dmnsn_future_cancel(self._future) def progress(self): self._assert_unfinished() - return dmnsn_get_progress(self._progress) + return dmnsn_future_progress(self._future) def wait(self, progress): self._assert_unfinished() - dmnsn_wait_progress(self._progress, progress) + dmnsn_future_wait(self._future, progress) def _assert_unfinished(self): - if self._progress == NULL: + if self._future == NULL: raise RuntimeError("background task finished.") -cdef Progress _Progress(dmnsn_progress *progress): - """Wrap a Progress object around an existing dmnsn_progress *.""" - cdef Progress self = Progress.__new__(Progress) - self._progress = progress +cdef Future _Future(dmnsn_future *future): + """Wrap a Future object around an existing dmnsn_future *.""" + cdef Future self = Future.__new__(Future) + self._future = future return self ########## @@ -562,7 +562,7 @@ cdef class Canvas: def write_PNG(self, path): """Export the canvas as a PNG file.""" - self.write_PNG_async(path).finish() + self.write_PNG_async(path).join() def write_PNG_async(self, path): """Export the canvas as a PNG file, in the background.""" bpath = path.encode("UTF-8") @@ -575,14 +575,13 @@ cdef class Canvas: if fclose(file) != 0: _raise_OSError() - cdef dmnsn_progress *progress = dmnsn_png_write_canvas_async(self._canvas, - file) + cdef dmnsn_future *future = dmnsn_png_write_canvas_async(self._canvas, file) try: - if progress == NULL: + if future == NULL: _raise_OSError() - ret = _Progress(progress) + ret = _Future(future) ret._finalizer = finalize return ret except: @@ -1501,7 +1500,7 @@ cdef class Scene: def raytrace(self): """Render the scene.""" - self.raytrace_async().finish() + self.raytrace_async().join() def raytrace_async(self): """Render the scene, in the background.""" # Account for image dimensions in the camera @@ -1519,7 +1518,7 @@ cdef class Scene: # Ensure the default texture is complete cdef Texture default = Texture(pigment = Black) dmnsn_texture_cascade(default._texture, &self._scene.default_texture) - return _Progress(dmnsn_raytrace_scene_async(self._scene)) + return _Future(dmnsn_raytrace_scene_async(self._scene)) def __dealloc__(self): dmnsn_delete_scene(self._scene) diff --git a/libdimension/Makefile.am b/libdimension/Makefile.am index 2a62a91..84f880c 100644 --- a/libdimension/Makefile.am +++ b/libdimension/Makefile.am @@ -37,6 +37,7 @@ nobase_include_HEADERS = dimension.h \ dimension/error.h \ dimension/finish.h \ dimension/finishes.h \ + dimension/future.h \ dimension/geometry.h \ dimension/gl.h \ dimension/inline.h \ @@ -53,7 +54,6 @@ nobase_include_HEADERS = dimension.h \ dimension/pigments.h \ dimension/png.h \ dimension/polynomial.h \ - dimension/progress.h \ dimension/raytrace.h \ dimension/refcount.h \ dimension/scene.h \ @@ -77,6 +77,8 @@ libdimension_la_SOURCES = $(nobase_include_HEADERS) \ dimension-internal.h \ error.c \ finish.c \ + future.c \ + future-impl.h \ geometry.c \ gradient.c \ inline.c \ @@ -98,8 +100,6 @@ libdimension_la_SOURCES = $(nobase_include_HEADERS) \ profile.h \ point_light.c \ polynomial.c \ - progress.c \ - progress-impl.h \ prtree.c \ prtree.h \ raytrace.c \ diff --git a/libdimension/bench/prtree.c b/libdimension/bench/prtree.c index 7c38990..7db2bbc 100644 --- a/libdimension/bench/prtree.c +++ b/libdimension/bench/prtree.c @@ -19,7 +19,7 @@ #include "../prtree.c" #include "../threads.c" -#include "../progress.c" +#include "../future.c" #include <sandglass.h> #include <stdlib.h> diff --git a/libdimension/dimension-internal.h b/libdimension/dimension-internal.h index 21e2f42..4370f6b 100644 --- a/libdimension/dimension-internal.h +++ b/libdimension/dimension-internal.h @@ -32,7 +32,7 @@ #include "compiler.h" #include "profile.h" #include "platform.h" -#include "progress-impl.h" +#include "future-impl.h" #include "threads.h" #include "prtree.h" diff --git a/libdimension/dimension.h b/libdimension/dimension.h index 309eaa2..59b73bd 100644 --- a/libdimension/dimension.h +++ b/libdimension/dimension.h @@ -80,7 +80,7 @@ typedef void dmnsn_free_fn(void *ptr); #include <dimension/refcount.h> #include <dimension/array.h> #include <dimension/dictionary.h> -#include <dimension/progress.h> +#include <dimension/future.h> #include <dimension/timer.h> #include <dimension/geometry.h> #include <dimension/polynomial.h> diff --git a/libdimension/dimension/progress.h b/libdimension/dimension/future.h index 3464a9b..f0693b9 100644 --- a/libdimension/dimension/progress.h +++ b/libdimension/dimension/future.h @@ -21,39 +21,39 @@ /** * @file * An interface for asynchronous tasks. *_async() versions of functions - * return a dmnsn_progress* object which can indicate the progress of the + * return a dmnsn_future* object which can indicate the progress of the * background task, and wait for task completion. The task's return value * is returned as an int from dmnsn_finish_progress(). */ -/** A progress object. */ -typedef struct dmnsn_progress dmnsn_progress; +/** A future object. */ +typedef struct dmnsn_future dmnsn_future; /** - * Join the worker thread and return it's integer return value in addition to - * deleting \p progress. - * @param[in,out] progress The background task to finish. + * Join the worker thread and return its integer return value in addition to + * deleting \p future. + * @param[in,out] future The background task to join. * @return The return value of the background task. */ -int dmnsn_finish_progress(dmnsn_progress *progress); +int dmnsn_future_join(dmnsn_future *future); /** * Interrupt the execution of a background thread. - * @param[in,out] progress The background task to cancel. + * @param[in,out] future The background task to cancel. */ -void dmnsn_cancel_progress(dmnsn_progress *progress); +void dmnsn_future_cancel(dmnsn_future *future); /** * Get the progress of the background task. - * @param[in] progress The background task to examine. - * @return The progress of the background task, out of 1.0. + * @param[in] future The background task to examine. + * @return The progress of the background task, in [0.0, 1.0]. */ -double dmnsn_get_progress(const dmnsn_progress *progress); +double dmnsn_future_progress(const dmnsn_future *future); /** * Wait for a certain amount of progress. Always use this rather than * spinlocking. - * @param[in] progress The background task to monitor. - * @param[in] prog The progress value to wait for. + * @param[in] future The background task to monitor. + * @param[in] progress The progress value to wait for. */ -void dmnsn_wait_progress(const dmnsn_progress *progress, double prog); +void dmnsn_future_wait(const dmnsn_future *future, double progress); diff --git a/libdimension/dimension/png.h b/libdimension/dimension/png.h index e3519a0..244c481 100644 --- a/libdimension/dimension/png.h +++ b/libdimension/dimension/png.h @@ -44,10 +44,10 @@ int dmnsn_png_write_canvas(const dmnsn_canvas *canvas, FILE *file); * Write a canvas to a PNG file in the background. * @param[in] canvas The canvas to write. * @param[in,out] file The file to write to. - * @return A \ref dmnsn_progress object, or NULL on failure. + * @return A \ref dmnsn_future object, or NULL on failure. */ -dmnsn_progress *dmnsn_png_write_canvas_async(const dmnsn_canvas *canvas, - FILE *file); +dmnsn_future *dmnsn_png_write_canvas_async(const dmnsn_canvas *canvas, + FILE *file); /** * Read a canvas from a PNG file. @@ -63,6 +63,6 @@ dmnsn_canvas *dmnsn_png_read_canvas(FILE *file); * contents of \p file. Do not read from this object * until the background task has finished. * @param[in,out] file The PNG file to read. - * @return A \ref dmnsn_progress object, or NULL on failure. + * @return A \ref dmnsn_future object, or NULL on failure. */ -dmnsn_progress *dmnsn_png_read_canvas_async(dmnsn_canvas **canvas, FILE *file); +dmnsn_future *dmnsn_png_read_canvas_async(dmnsn_canvas **canvas, FILE *file); diff --git a/libdimension/dimension/raytrace.h b/libdimension/dimension/raytrace.h index eed8a75..79c148d 100644 --- a/libdimension/dimension/raytrace.h +++ b/libdimension/dimension/raytrace.h @@ -32,6 +32,6 @@ void dmnsn_raytrace_scene(dmnsn_scene *scene); /** * Render a scene in the background. * @param[in,out] scene The scene to render. - * @return A \p dmnsn_progress object. + * @return A \p dmnsn_future object. */ -dmnsn_progress *dmnsn_raytrace_scene_async(dmnsn_scene *scene); +dmnsn_future *dmnsn_raytrace_scene_async(dmnsn_scene *scene); diff --git a/libdimension/progress-impl.h b/libdimension/future-impl.h index 6c10ff8..c25a7c2 100644 --- a/libdimension/progress-impl.h +++ b/libdimension/future-impl.h @@ -20,36 +20,35 @@ /** * @file - * Progress object implementation. + * Future object implementation. */ #include <pthread.h> -/** Allocate a new progress object. */ -DMNSN_INTERNAL dmnsn_progress *dmnsn_new_progress(void); +/** Allocate a new future object. */ +DMNSN_INTERNAL dmnsn_future *dmnsn_new_future(void); /** Set the total number of loop iterations. */ -DMNSN_INTERNAL void dmnsn_set_progress_total(dmnsn_progress *progress, - size_t total); -/** Increment the progress counter. */ -DMNSN_INTERNAL void dmnsn_increment_progress(dmnsn_progress *progress); -/** Instantly complete the progress. */ -DMNSN_INTERNAL void dmnsn_done_progress(dmnsn_progress *progress); - -struct dmnsn_progress { +DMNSN_INTERNAL void dmnsn_future_set_total(dmnsn_future *future, size_t total); +/** Increment the progress of a background task. */ +DMNSN_INTERNAL void dmnsn_future_increment(dmnsn_future *future); +/** Instantly complete the background teask. */ +DMNSN_INTERNAL void dmnsn_future_done(dmnsn_future *future); + +struct dmnsn_future { size_t progress; /**< Completed loop iterations. */ size_t total; /**< Total expected loop iterations. */ - /* The worker thread */ + /** The worker thread. */ pthread_t thread; - /* Read-write synchronization */ + /** Read-write synchronization. */ pthread_rwlock_t *rwlock; - /* Condition variable for waiting for a particular amount of progress */ + /** Condition variable for waiting for a particular amount of progress. */ pthread_cond_t *cond; pthread_mutex_t *mutex; - /* Minimum waited-on value */ + /** Minimum waited-on value. */ double *min_wait; }; diff --git a/libdimension/future.c b/libdimension/future.c new file mode 100644 index 0000000..c328815 --- /dev/null +++ b/libdimension/future.c @@ -0,0 +1,164 @@ +/************************************************************************* + * Copyright (C) 2009-2011 Tavian Barnes <tavianator@tavianator.com> * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * <http://www.gnu.org/licenses/>. * + *************************************************************************/ + +/** + * @file + * Future objects. + */ + +#include "dimension-internal.h" +#include <pthread.h> + +/* Allocate a new dmnsn_future* */ +dmnsn_future * +dmnsn_new_future(void) +{ + dmnsn_future *future = dmnsn_malloc(sizeof(dmnsn_future)); + future->progress = 0; + future->total = 1; + + /* Initialize the rwlock, condition variable, and mutex */ + + future->rwlock = dmnsn_malloc(sizeof(pthread_rwlock_t)); + dmnsn_initialize_rwlock(future->rwlock); + + future->cond = dmnsn_malloc(sizeof(pthread_cond_t)); + dmnsn_initialize_cond(future->cond); + + future->mutex = dmnsn_malloc(sizeof(pthread_mutex_t)); + dmnsn_initialize_mutex(future->mutex); + + future->min_wait = dmnsn_malloc(sizeof(double)); + *future->min_wait = 1.0; + + return future; +} + +/* Join the worker thread and delete `future'. */ +int +dmnsn_future_join(dmnsn_future *future) +{ + void *ptr; + int retval = -1; + + if (future) { + /* Get the thread's return value */ + dmnsn_join_thread(future->thread, &ptr); + if (ptr && ptr != PTHREAD_CANCELED) { + retval = *(int *)ptr; + dmnsn_free(ptr); + } + + /* Free the future object */ + + dmnsn_free(future->min_wait); + + dmnsn_destroy_mutex(future->mutex); + dmnsn_free(future->mutex); + + dmnsn_destroy_cond(future->cond); + dmnsn_free(future->cond); + + dmnsn_destroy_rwlock(future->rwlock); + dmnsn_free(future->rwlock); + + dmnsn_free(future); + } + + return retval; +} + +/* Cancel a background thread */ +void +dmnsn_future_cancel(dmnsn_future *future) +{ + pthread_cancel(future->thread); +} + +/* Get the current progress of the worker thread, in [0.0, 1.0] */ +double +dmnsn_future_progress(const dmnsn_future *future) +{ + double progress; + + dmnsn_read_lock(future->rwlock); + progress = (double)future->progress/future->total; + dmnsn_unlock_rwlock(future->rwlock); + + return progress; +} + +/* Wait until dmnsn_future_progress(future) >= progress */ +void +dmnsn_future_wait(const dmnsn_future *future, double progress) +{ + dmnsn_lock_mutex(future->mutex); + while (dmnsn_future_progress(future) < progress) { + /* Set the minimum waited-on value */ + if (progress < *future->min_wait) + *future->min_wait = progress; + + dmnsn_cond_wait(future->cond, future->mutex); + } + dmnsn_unlock_mutex(future->mutex); +} + +/* Set the total number of loop iterations */ +void +dmnsn_future_set_total(dmnsn_future *future, size_t total) +{ + dmnsn_write_lock(future->rwlock); + future->total = total; + dmnsn_unlock_rwlock(future->rwlock); +} + +/* Increment the number of completed loop iterations */ +void +dmnsn_future_increment(dmnsn_future *future) +{ + /* Allow a thread to be canceled whenever it increments a future object -- + this is close to PTHREAD_CANCEL_ASYNCHRONOUS but allows consistent state + on cancellation */ + pthread_testcancel(); + + dmnsn_write_lock(future->rwlock); + ++future->progress; + dmnsn_unlock_rwlock(future->rwlock); + + dmnsn_lock_mutex(future->mutex); + if (dmnsn_future_progress(future) >= *future->min_wait) { + *future->min_wait = 1.0; + dmnsn_cond_broadcast(future->cond); + } + dmnsn_unlock_mutex(future->mutex); +} + +/* Immediately set to 100% completion */ +void +dmnsn_future_done(dmnsn_future *future) +{ + dmnsn_write_lock(future->rwlock); + future->progress = future->total; + dmnsn_unlock_rwlock(future->rwlock); + + dmnsn_lock_mutex(future->mutex); + dmnsn_cond_broadcast(future->cond); + dmnsn_unlock_mutex(future->mutex); +} diff --git a/libdimension/png.c b/libdimension/png.c index 21ad764..d633abb 100644 --- a/libdimension/png.c +++ b/libdimension/png.c @@ -77,14 +77,14 @@ dmnsn_png_optimizer_fn(const dmnsn_canvas *canvas, /** Payload type for PNG write thread callback. */ typedef struct { - dmnsn_progress *progress; + dmnsn_future *future; const dmnsn_canvas *canvas; FILE *file; } dmnsn_png_write_payload; /** Payload type for PNG read thread callback. */ typedef struct { - dmnsn_progress *progress; + dmnsn_future *future; dmnsn_canvas **canvas; FILE *file; } dmnsn_png_read_payload; @@ -99,26 +99,26 @@ static int dmnsn_png_read_canvas_thread(void *ptr); int dmnsn_png_write_canvas(const dmnsn_canvas *canvas, FILE *file) { - dmnsn_progress *progress = dmnsn_png_write_canvas_async(canvas, file); - return dmnsn_finish_progress(progress); + dmnsn_future *future = dmnsn_png_write_canvas_async(canvas, file); + return dmnsn_future_join(future); } /* Write a canvas to a png file in the background */ -dmnsn_progress * +dmnsn_future * dmnsn_png_write_canvas_async(const dmnsn_canvas *canvas, FILE *file) { - dmnsn_progress *progress = dmnsn_new_progress(); + dmnsn_future *future = dmnsn_new_future(); dmnsn_png_write_payload *payload = dmnsn_malloc(sizeof(dmnsn_png_write_payload)); - payload->progress = progress; - payload->canvas = canvas; - payload->file = file; + payload->future = future; + payload->canvas = canvas; + payload->file = file; /* Create the worker thread */ - dmnsn_new_thread(progress, dmnsn_png_write_canvas_thread, payload); + dmnsn_new_thread(future, dmnsn_png_write_canvas_thread, payload); - return progress; + return future; } /* Read a canvas from the PNG file `file'. Return NULL on error. */ @@ -126,28 +126,28 @@ dmnsn_canvas * dmnsn_png_read_canvas(FILE *file) { dmnsn_canvas *canvas; - dmnsn_progress *progress = dmnsn_png_read_canvas_async(&canvas, file); - dmnsn_finish_progress(progress); + dmnsn_future *future = dmnsn_png_read_canvas_async(&canvas, file); + dmnsn_future_join(future); return canvas; } /* Read a canvas from a png file in the background */ -dmnsn_progress * +dmnsn_future * dmnsn_png_read_canvas_async(dmnsn_canvas **canvas, FILE *file) { - dmnsn_progress *progress = dmnsn_new_progress(); + dmnsn_future *future = dmnsn_new_future(); dmnsn_png_read_payload *payload = dmnsn_malloc(sizeof(dmnsn_png_write_payload)); - payload->progress = progress; - payload->canvas = canvas; - *payload->canvas = NULL; - payload->file = file; + payload->future = future; + payload->canvas = canvas; + *payload->canvas = NULL; + payload->file = file; /* Create the worker thread */ - dmnsn_new_thread(progress, dmnsn_png_read_canvas_thread, payload); + dmnsn_new_thread(future, dmnsn_png_read_canvas_thread, payload); - return progress; + return future; } /* @@ -163,7 +163,7 @@ dmnsn_png_write_canvas_thread(void *ptr) png_uint_32 width = payload->canvas->width; png_uint_32 height = payload->canvas->height; - dmnsn_set_progress_total(payload->progress, height); + dmnsn_future_set_total(payload->future, height); png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); @@ -220,7 +220,7 @@ dmnsn_png_write_canvas_thread(void *ptr) /* Invert the rows. PNG coordinates are fourth quadrant. */ uint16_t *row = (uint16_t *)i->ptr + 4*(height - y - 1)*width; png_write_row(png_ptr, (png_bytep)row); - dmnsn_increment_progress(payload->progress); + dmnsn_future_increment(payload->future); } /* Finish the PNG file */ @@ -251,7 +251,7 @@ dmnsn_png_write_canvas_thread(void *ptr) /* Write the row */ png_write_row(png_ptr, (png_bytep)row); - dmnsn_increment_progress(payload->progress); + dmnsn_future_increment(payload->future); } /* Finish the PNG file */ @@ -263,15 +263,15 @@ dmnsn_png_write_canvas_thread(void *ptr) return 0; } -/** Thread-specific pointer to the appropriate dmnsn_progress* for +/** Thread-specific pointer to the appropriate dmnsn_future* for dmnsn_png_read_row_callback. */ -static __thread dmnsn_progress *dmnsn_tl_png_read_progress; +static __thread dmnsn_future *dmnsn_tl_png_read_future; /** Callback to increment the progress after a row has been read. */ static void dmnsn_png_read_row_callback(png_structp png_ptr, png_uint_32 row, int pass) { - dmnsn_increment_progress(dmnsn_tl_png_read_progress); + dmnsn_future_increment(dmnsn_tl_png_read_future); } /* Read a PNG file */ @@ -279,7 +279,7 @@ static int dmnsn_png_read_canvas_thread(void *ptr) { dmnsn_png_read_payload *payload = ptr; - dmnsn_tl_png_read_progress = payload->progress; + dmnsn_tl_png_read_future = payload->future; png_byte header[8]; if (fread(header, 1, 8, payload->file) != 8) { @@ -336,7 +336,7 @@ dmnsn_png_read_canvas_thread(void *ptr) &interlace_type, &compression_type, &filter_method); int number_of_passes = png_set_interlace_handling(png_ptr); - dmnsn_set_progress_total(payload->progress, (number_of_passes + 1)*height); + dmnsn_future_set_total(payload->future, (number_of_passes + 1)*height); png_set_read_status_fn(png_ptr, dmnsn_png_read_row_callback); /* @@ -423,7 +423,7 @@ dmnsn_png_read_canvas_thread(void *ptr) dmnsn_set_pixel(*payload->canvas, x, height - y - 1, color); } - dmnsn_increment_progress(payload->progress); + dmnsn_future_increment(payload->future); } dmnsn_free(row_pointers); diff --git a/libdimension/progress.c b/libdimension/progress.c deleted file mode 100644 index f2ff25f..0000000 --- a/libdimension/progress.c +++ /dev/null @@ -1,165 +0,0 @@ -/************************************************************************* - * Copyright (C) 2009-2011 Tavian Barnes <tavianator@tavianator.com> * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * <http://www.gnu.org/licenses/>. * - *************************************************************************/ - -/** - * @file - * Progress objects. - */ - -#include "dimension-internal.h" -#include <pthread.h> - -/* Allocate a new dmnsn_progress* */ -dmnsn_progress * -dmnsn_new_progress(void) -{ - dmnsn_progress *progress = dmnsn_malloc(sizeof(dmnsn_progress)); - progress->progress = 0; - progress->total = 1; - - /* Initialize the rwlock, condition variable, and mutex */ - - progress->rwlock = dmnsn_malloc(sizeof(pthread_rwlock_t)); - dmnsn_initialize_rwlock(progress->rwlock); - - progress->cond = dmnsn_malloc(sizeof(pthread_cond_t)); - dmnsn_initialize_cond(progress->cond); - - progress->mutex = dmnsn_malloc(sizeof(pthread_mutex_t)); - dmnsn_initialize_mutex(progress->mutex); - - progress->min_wait = dmnsn_malloc(sizeof(double)); - *progress->min_wait = 1.0; - - return progress; -} - -/* Join the worker thread and delete `progress'. */ -int -dmnsn_finish_progress(dmnsn_progress *progress) -{ - void *ptr; - int retval = -1; - - if (progress) { - /* Get the thread's return value */ - dmnsn_join_thread(progress->thread, &ptr); - if (ptr && ptr != PTHREAD_CANCELED) { - retval = *(int *)ptr; - dmnsn_free(ptr); - } - - /* Free the progress object */ - - dmnsn_free(progress->min_wait); - - dmnsn_destroy_mutex(progress->mutex); - dmnsn_free(progress->mutex); - - dmnsn_destroy_cond(progress->cond); - dmnsn_free(progress->cond); - - dmnsn_destroy_rwlock(progress->rwlock); - dmnsn_free(progress->rwlock); - - dmnsn_free(progress); - } - - return retval; -} - -/* Cancel a background thread */ -void -dmnsn_cancel_progress(dmnsn_progress *progress) -{ - pthread_cancel(progress->thread); -} - -/* Get the current progress of the worker thread, in [0.0, 1.0] */ -double -dmnsn_get_progress(const dmnsn_progress *progress) -{ - double prog; - - dmnsn_read_lock(progress->rwlock); - prog = (double)progress->progress/progress->total; - dmnsn_unlock_rwlock(progress->rwlock); - - return prog; -} - -/* Wait until dmnsn_get_progress(progress) >= prog */ -void -dmnsn_wait_progress(const dmnsn_progress *progress, double prog) -{ - dmnsn_lock_mutex(progress->mutex); - while (dmnsn_get_progress(progress) < prog) { - /* Set the minimum waited-on value */ - if (prog < *progress->min_wait) - *progress->min_wait = prog; - - dmnsn_cond_wait(progress->cond, progress->mutex); - } - dmnsn_unlock_mutex(progress->mutex); -} - -/* Set the total number of loop iterations */ -void -dmnsn_set_progress_total(dmnsn_progress *progress, size_t total) -{ - dmnsn_write_lock(progress->rwlock); - progress->total = total; - dmnsn_unlock_rwlock(progress->rwlock); -} - -/* Increment the number of completed loop iterations */ -void -dmnsn_increment_progress(dmnsn_progress *progress) -{ - /* Allow a thread to be canceled whenever it increments a progress object -- - this is close to PTHREAD_CANCEL_ASYNCHRONOUS but allows consistent state - on cancellation */ - pthread_testcancel(); - - dmnsn_write_lock(progress->rwlock); - ++progress->progress; - dmnsn_unlock_rwlock(progress->rwlock); - - dmnsn_lock_mutex(progress->mutex); - if (dmnsn_get_progress(progress) >= *progress->min_wait) { - *progress->min_wait = 1.0; - - dmnsn_cond_broadcast(progress->cond); - } - dmnsn_unlock_mutex(progress->mutex); -} - -/* Immediately set to 100% completion */ -void -dmnsn_done_progress(dmnsn_progress *progress) -{ - dmnsn_write_lock(progress->rwlock); - progress->progress = progress->total; - dmnsn_unlock_rwlock(progress->rwlock); - - dmnsn_lock_mutex(progress->mutex); - dmnsn_cond_broadcast(progress->cond); - dmnsn_unlock_mutex(progress->mutex); -} diff --git a/libdimension/raytrace.c b/libdimension/raytrace.c index 4ee451a..e5d2a15 100644 --- a/libdimension/raytrace.c +++ b/libdimension/raytrace.c @@ -32,7 +32,7 @@ /** Payload type for passing arguments to worker threads. */ typedef struct { - dmnsn_progress *progress; + dmnsn_future *future; dmnsn_scene *scene; dmnsn_prtree *prtree; } dmnsn_raytrace_payload; @@ -41,8 +41,8 @@ typedef struct { void dmnsn_raytrace_scene(dmnsn_scene *scene) { - dmnsn_progress *progress = dmnsn_raytrace_scene_async(scene); - if (dmnsn_finish_progress(progress) != 0) { + dmnsn_future *future = dmnsn_raytrace_scene_async(scene); + if (dmnsn_future_join(future) != 0) { dmnsn_error("Error occured while raytracing."); } } @@ -51,19 +51,19 @@ dmnsn_raytrace_scene(dmnsn_scene *scene) static int dmnsn_raytrace_scene_thread(void *ptr); /* Raytrace a scene in the background */ -dmnsn_progress * +dmnsn_future * dmnsn_raytrace_scene_async(dmnsn_scene *scene) { - dmnsn_progress *progress = dmnsn_new_progress(); + dmnsn_future *future = dmnsn_new_future(); dmnsn_raytrace_payload *payload = dmnsn_malloc(sizeof(dmnsn_raytrace_payload)); - payload->progress = progress; - payload->scene = scene; + payload->future = future; + payload->scene = scene; - dmnsn_new_thread(progress, dmnsn_raytrace_scene_thread, payload); + dmnsn_new_thread(future, dmnsn_raytrace_scene_thread, payload); - return progress; + return future; } /** Worker thread callback. */ @@ -84,8 +84,8 @@ dmnsn_raytrace_scene_thread(void *ptr) payload->prtree = dmnsn_new_prtree(payload->scene->objects); dmnsn_stop_timer(&payload->scene->bounding_timer); - /* Set up the progress object */ - dmnsn_set_progress_total(payload->progress, payload->scene->canvas->height); + /* Set up the future object */ + dmnsn_future_set_total(payload->future, payload->scene->canvas->height); /* Time the render itself */ dmnsn_start_timer(&payload->scene->render_timer); @@ -168,7 +168,7 @@ dmnsn_raytrace_scene_concurrent(void *ptr, unsigned int thread, unsigned int nthreads) { const dmnsn_raytrace_payload *payload = ptr; - dmnsn_progress *progress = payload->progress; + dmnsn_future *future = payload->future; dmnsn_scene *scene = payload->scene; dmnsn_prtree *prtree = payload->prtree; @@ -197,7 +197,7 @@ dmnsn_raytrace_scene_concurrent(void *ptr, unsigned int thread, dmnsn_set_pixel(scene->canvas, x, y, color); } - dmnsn_increment_progress(progress); + dmnsn_future_increment(future); } return 0; diff --git a/libdimension/tests/prtree.c b/libdimension/tests/prtree.c index 2e33368..d89a18d 100644 --- a/libdimension/tests/prtree.c +++ b/libdimension/tests/prtree.c @@ -23,7 +23,7 @@ #include "../prtree.c" #include "../threads.c" -#include "../progress.c" +#include "../future.c" #include <stdio.h> #include <stdlib.h> diff --git a/libdimension/tests/render.c b/libdimension/tests/render.c index ee63d06..af22691 100644 --- a/libdimension/tests/render.c +++ b/libdimension/tests/render.c @@ -353,11 +353,11 @@ main(void) /* Render the scene */ printf("Rendering scene\n"); - dmnsn_progress *progress = dmnsn_raytrace_scene_async(scene); + dmnsn_future *future = dmnsn_raytrace_scene_async(scene); /* Display the scene as it's rendered */ if (display) { - while (dmnsn_get_progress(progress) < 1.0) { + while (dmnsn_future_progress(future) < 1.0) { if (dmnsn_gl_write_canvas(scene->canvas) != 0) { dmnsn_delete_display(display); dmnsn_delete_scene(scene); @@ -368,7 +368,7 @@ main(void) } } - if (dmnsn_finish_progress(progress) != 0) { + if (dmnsn_future_join(future) != 0) { dmnsn_delete_display(display); dmnsn_delete_scene(scene); fprintf(stderr, "--- Raytracing failed! ---\n"); diff --git a/libdimension/threads.c b/libdimension/threads.c index 2200671..0aed16d 100644 --- a/libdimension/threads.c +++ b/libdimension/threads.c @@ -30,7 +30,7 @@ typedef struct dmnsn_thread_payload { dmnsn_thread_fn *thread_fn; void *arg; - dmnsn_progress *progress; + dmnsn_future *future; } dmnsn_thread_payload; /** Clean up after a thread. */ @@ -38,10 +38,10 @@ static void dmnsn_thread_cleanup(void *arg) { dmnsn_thread_payload *payload = arg; - dmnsn_progress *progress = payload->progress; + dmnsn_future *future = payload->future; dmnsn_free(payload); - dmnsn_done_progress(progress); + dmnsn_future_done(future); } /** pthread callback -- call the real thread callback. */ @@ -59,15 +59,14 @@ dmnsn_thread(void *arg) } void -dmnsn_new_thread(dmnsn_progress *progress, dmnsn_thread_fn *thread_fn, - void *arg) +dmnsn_new_thread(dmnsn_future *future, dmnsn_thread_fn *thread_fn, void *arg) { dmnsn_thread_payload *payload = dmnsn_malloc(sizeof(dmnsn_thread_payload)); payload->thread_fn = thread_fn; payload->arg = arg; - payload->progress = progress; + payload->future = future; - if (pthread_create(&progress->thread, NULL, dmnsn_thread, payload) != 0) { + if (pthread_create(&future->thread, NULL, dmnsn_thread, payload) != 0) { dmnsn_error("Couldn't start thread."); } } diff --git a/libdimension/threads.h b/libdimension/threads.h index 7a599de..ea9fb2a 100644 --- a/libdimension/threads.h +++ b/libdimension/threads.h @@ -34,11 +34,11 @@ typedef int dmnsn_thread_fn(void *ptr); /** * Create a thread that cleans up after itself on errors. - * @param[in,out] progress The progress object to associate with the thread. + * @param[in,out] future The future object to associate with the thread. * @param[in] thread_fn The thread callback. * @param[in,out] arg The pointer to pass to the thread callback. */ -DMNSN_INTERNAL void dmnsn_new_thread(dmnsn_progress *progress, +DMNSN_INTERNAL void dmnsn_new_thread(dmnsn_future *future, dmnsn_thread_fn *thread_fn, void *arg); /** |