X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=utils%2Fthread_util.h;h=6dff8de688bc6d67463123c5a96a5e8e8500ee80;hb=aa75c7476aeaa84cf3abc192b376a8b03c325213;hp=33dac8f1f2101ede5086ecf41d7d4b71f94ec2aa;hpb=8afc01a67be4fbf3f1cc0fce9adf01b5289a21c6;p=xscreensaver diff --git a/utils/thread_util.h b/utils/thread_util.h index 33dac8f1..6dff8de6 100644 --- a/utils/thread_util.h +++ b/utils/thread_util.h @@ -1,5 +1,5 @@ /* -*- mode: c; tab-width: 4; fill-column: 78 -*- */ -/* vi: set ts=4 tw=128: */ +/* vi: set ts=4 tw=78: */ /* thread_util.h, Copyright (c) 2014 Dave Odell @@ -83,12 +83,21 @@ implied warranty. # include #endif -#ifdef HAVE_COCOA +#if defined HAVE_JWXYZ # include "jwxyz.h" #else # include #endif +#if HAVE_PTHREAD +int threads_available(Display *dpy); +#else +# define threads_available(dpy) (-1) +#endif +/* > 0: Threads are available. This is normally _POSIX_VERSION. + -1: Threads are not available. +*/ + unsigned hardware_concurrency(Display *dpy); /* This is supposed to return the number of available CPU cores. This number isn't necessarily constant: a system administrator can hotplug or @@ -271,13 +280,15 @@ struct threadpool_class size_t size; /* Create the thread private object. Called in sequence for each thread - (effectively) from threadpool_create. self: A pointer to size bytes of - memory, allocated to hold the thread object. pool: The threadpool object - that owns all the threads. If the threadpool is nested in another struct, - try GET_PARENT_OBJ. id: The ID for the thread; numbering starts at zero - and goes up by one for each thread. Return 0 on success. On failure, - return a value from errno.h; this will be returned from - threadpool_create. */ + (effectively) from threadpool_create. + self: A pointer to size bytes of memory, allocated to hold the thread + object. + pool: The threadpool object that owns all the threads. If the threadpool + is nested in another struct, try GET_PARENT_OBJ. + id: The ID for the thread; numbering starts at zero and goes up by one + for each thread. + Return 0 on success. On failure, return a value from errno.h; this will + be returned from threadpool_create. */ int (*create)(void *self, struct threadpool *pool, unsigned id); /* Destroys the thread private object. Called in sequence (though not always @@ -295,14 +306,133 @@ void threadpool_destroy(struct threadpool *self); void threadpool_run(struct threadpool *self, void (*func)(void *)); void threadpool_wait(struct threadpool *self); +/* + io_thread is meant to wrap blocking I/O operations in a one-shot worker + thread, with cancel semantics. + + Unlike threadpool_*, io_thread will not 'fake it'; it is up to the caller + to figure out what to do if the system doesn't have threads. In + particular, the start_routine passed to io_thread_create will never be + called. + + Clients of io_thread implement four functions: + - state *process_start(...); + Starts the worker thread. + - bool process_is_done(state *); + Returns true if the I/O operation is complete. + - void process_cancel(state *); + "Cancels" the I/O operation. The thread will continue to run, but it + will detach, and clean itself up upon completion. + - int process_finish(state *, ...) + Waits for the I/O operation to complete, returns results, and cleans up. + + Or: /---\ + \/ | /--> cancel + start -> is_done --+ + \--> finish + + These functions follow a basic pattern: + - start: + 1. Allocate a thread state object with thread_alloc. This state object + contains an io_thread member. + 2. Save parameters from the start parameters to the state object. + 3. Start the thread with _io_thread_create. The thread receives the state + object as its parameter. + - On the worker thread: + 1. Do the I/O. + 2. Call io_thread_return. + 2a. If the result != 0, free the state object. + - is_done: + 1. Just call _io_thread_is_done. + - cancel: + 1. Call io_thread_cancel. + 1a. If the result != 0, free the state object. + - finish: + 1. Call io_thread_finish. + 2. Copy results out of the state object as needed. + 3. Free the state object...or return it to the caller. + + Incidentally, there may sometimes be asynchronous versions of blocking I/O + functions (struct aiocb and friends, for example); these should be + preferred over io_thread when performance is a concern. + */ + +enum _io_thread_status +{ + _io_thread_working, _io_thread_done, _io_thread_cancelled +}; + +struct io_thread +{ +#if HAVE_PTHREAD + /* Common misconception: "volatile" should be applied to atomic variables, + such as 'status', below. This is false, see + . */ + enum _io_thread_status status; + pthread_t thread; +#else + char gcc_emits_a_warning_when_the_struct_has_no_members; +#endif +}; + +#if HAVE_PTHREAD + +void *io_thread_create(struct io_thread *self, void *parent, void *(*start_routine)(void *), Display *dpy, unsigned stacksize); +/* + Create the thread, returns NULL on failure. Failure is usually due to + ENOMEM, or the system doesn't support threads. + self: The io_thread object to be initialized. + parent: The parameter to start_routine. The io_thread should be + contained within or be reachable from this. + start_routine: The start routine for the worker thread. + dpy: The X11 Display, so that '*useThreads' is honored. + stacksize: The stack size for the thread. Set to 0 for the system + default. + A note about stacksize: Linux, for example, uses a default of 2 MB of + stack per thread. Now, this memory is usually committed on the first + write, so lots of threads won't waste RAM, but it does mean that on a + 32-bit system, there's a limit of just under 1024 threads with the 2 MB + default due to typical address space limitations of 2 GB for userspace + processes. And 1024 threads might not always be enough... + */ + +int io_thread_return(struct io_thread *self); +/* Called at the end of start_routine, from above. Returns non-zero if the + thread has been cancelled, and cleanup needs to take place. */ + +int io_thread_is_done(struct io_thread *self); +/* Call from the main thread. Returns non-zero if the thread finished. */ + +int io_thread_cancel(struct io_thread *self); +/* Call from the main thread if the results from the worker thread are not + needed. This cleans up the io_thread. Returns non-zero if cleanup needs + to take place. */ + +void io_thread_finish(struct io_thread *self); +/* Call from the main thread to wait for the worker thread to finish. This + cleans up the io_thread. */ + +#else + +#define IO_THREAD_STACK_MIN 0 + +#define io_thread_create(self, parent, start_routine, dpy, stacksize) NULL +#define io_thread_return(self) 0 +#define io_thread_is_done(self) 1 +#define io_thread_cancel(self) 0 +#define io_thread_finish(self) + +#endif + #if HAVE_PTHREAD -# define THREAD_DEFAULTS \ - "*useThreads: True", +# define THREAD_DEFAULTS "*useThreads: True", +# define THREAD_DEFAULTS_XLOCK "*useThreads: True\n" # define THREAD_OPTIONS \ {"-threads", ".useThreads", XrmoptionNoArg, "True"}, \ {"-no-threads", ".useThreads", XrmoptionNoArg, "False"}, #else # define THREAD_DEFAULTS +# define THREAD_DEFAULTS_XLOCK # define THREAD_OPTIONS #endif