00001 /* Part of the culibs project, <http://www.eideticdew.org/culibs/>. 00002 * Copyright (C) 2006--2009 Petter Urkedal <urkedal@nbi.dk> 00003 * 00004 * This program is free software: you can redistribute it and/or modify 00005 * it under the terms of the GNU General Public License as published by 00006 * the Free Software Foundation, either version 3 of the License, or 00007 * (at your option) any later version. 00008 * 00009 * This program is distributed in the hope that it will be useful, 00010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 * GNU General Public License for more details. 00013 * 00014 * You should have received a copy of the GNU General Public License 00015 * along with this program. If not, see <http://www.gnu.org/licenses/>. 00016 */ 00017 00018 #ifndef CUFLOW_WORKERS_H 00019 #define CUFLOW_WORKERS_H 00020 00021 #include <cuflow/fwd.h> 00022 #include <cu/clos.h> 00023 #include <time.h> 00024 #include <unistd.h> 00025 #include <atomic_ops.h> 00026 00027 CU_BEGIN_DECLARATIONS 00028 00029 extern AO_t cuflowP_pending_work; 00030 extern AO_t cuflowP_workers_waiting_count; 00031 void cuflowP_workers_broadcast(void); 00032 void cuflowP_yield(void); 00033 00034 /** \defgroup cuflow_workers_h cuflow/workers.h: Auxiliary Worker Threads 00035 ** @{ \ingroup cuflow_smp_mod 00036 ** 00037 ** This header provides an API to launch one or more worker threads and run 00038 ** functions within them, either as soon as one is idle or at a given time. 00039 ** The main purpose of this is to unify such work in a common framework, so 00040 ** that the same thread(s) can be used, rather than adding new threads for 00041 ** each task. The functions defined here is aimed to be the bare minimum to 00042 ** achieve this. For instance, if you need to know when a callback is 00043 ** finised, you may need to supply it's closure struct with an additional 00044 ** mutex and possibly a condition variable to communicate with consumer(s) of 00045 ** the computation. 00046 ** 00047 ** This is implemenated purely with POSIX threads, mutices and condition 00048 ** variables, and in particular avoid timers or signals which may cause 00049 ** trouble in a multi-threaded environment. 00050 **/ 00051 00052 /** The current number of worker threads. */ 00053 int cuflow_workers_count(void); 00054 00055 /** Spawn or drop worker threads until there are exactly \a target_count left. 00056 ** This is not safe to call within worker threads. */ 00057 void cuflow_workers_spawn(int target_count); 00058 00059 /** Spawns worker threads as needed so that there are at least \a target_count 00060 ** of them. */ 00061 void cuflow_workers_spawn_at_least(int target_count); 00062 00063 /** Drops worker threads as needed so that there are at most \a target_count 00064 ** left. */ 00065 void cuflow_workers_spawn_at_most(int target_count); 00066 00067 /** Call \a f in one of the worker threads, as soon as one is ready. */ 00068 void cuflow_workers_call(cu_clop0(f, void)); 00069 00070 /** Call \a f at absolute \c CLOCK_REALTIME \a t_call. */ 00071 void cuflow_workers_call_at(cu_clop(f, void, struct timespec *t_now), 00072 struct timespec *t_call); 00073 00074 /** Arranged for \a sched to be called round-robin along with other registered 00075 ** work. This can happen either when a task is waiting for work to be 00076 ** completed or directly from a worker thread. When called from a worker 00077 ** thread, true is passed for the first argument. Otherwise, false is passed 00078 ** to indicate that \a sched is called from an arbitrary subroutine, in which 00079 ** case \a sched should only do a small amount of work and exit. */ 00080 void cuflow_workers_register_scheduler(cu_clop(sched, void, cu_bool_t)); 00081 00082 /** A call to this function performs some work which have been registered by 00083 ** one of the other functions from this header. This is typcially invoked 00084 ** when waiting for resources or results from other scheduled work. */ 00085 CU_SINLINE void 00086 cuflow_yield(void) 00087 { 00088 if (AO_load(&cuflowP_pending_work)) 00089 cuflowP_yield(); 00090 } 00091 00092 /** Wake up any sleeping workers so that they can pick up any new work. You 00093 ** usually don't need to call this, since it's automically called by functions 00094 ** which add new work or unlocks existing work. */ 00095 CU_SINLINE void 00096 cuflow_workers_broadcast(void) 00097 { 00098 if (AO_load(&cuflowP_workers_waiting_count)) 00099 cuflowP_workers_broadcast(); 00100 } 00101 00102 /** Indicate that your scheduler have pending work. Pair it with a call to 00103 ** \ref cuflow_workers_decr_pending when the work is done. All schedulers 00104 ** will run round-robin as long as this function has been called more times 00105 ** than \ref cuflow_workers_decr_pending. */ 00106 CU_SINLINE void 00107 cuflow_workers_incr_pending(void) 00108 { 00109 AO_fetch_and_add1_acquire(&cuflowP_pending_work); 00110 cuflow_workers_broadcast(); 00111 } 00112 00113 /** Indicate that a unit of work signalled with \ref 00114 ** cuflow_workers_incr_pending has been picked up. This is typically called 00115 ** from a custom scheduler. */ 00116 CU_SINLINE void 00117 cuflow_workers_decr_pending(void) 00118 { 00119 AO_fetch_and_sub1(&cuflowP_pending_work); 00120 } 00121 00122 /** @} */ 00123 CU_END_DECLARATIONS 00124 00125 #endif