00001 #ifndef _CP_THREAD_H 00002 #define _CP_THREAD_H 00003 00004 /** 00005 * @addtogroup cp_thread 00006 */ 00007 /** @{ */ 00008 /** 00009 * @file 00010 * definitions for the thread management framework. the 00011 * current implementation is based on the POSIX thread api. 00012 */ 00013 00014 #include "common.h" 00015 00016 __BEGIN_DECLS 00017 00018 #include "config.h" 00019 /* synchronization api abstraction (a bunch of pthread-like macros basically) 00020 * is in collection.h 00021 */ 00022 #include "collection.h" 00023 #include "linked_list.h" 00024 #include "hashlist.h" 00025 #include "vector.h" 00026 00027 /** a thread function */ 00028 typedef void *(*cp_thread_action) (void *); 00029 00030 00031 /** a stop function for client threads */ 00032 typedef int (*cp_thread_stop_fn)(void *); 00033 00034 /** 00035 * cp_pooled_thread is a thread that lives in a thread_pool. The struct holds 00036 * synchronization elements used to control the thread and actuation settings, 00037 * i.e. a thread function and a parameter to be passed for the next starting 00038 * thread. The pool initializes a bunch of threads and sets them in 'wait'. 00039 * 00040 * When a client requests threads from the pool, the next available thread is 00041 * signalled out of 'wait', and runs the thread function requested by the 00042 * client (see cp_thread_pool_get_impl). When the client thread function is 00043 * done, the cp_pooled_thread returns to the thread pool and becomes available 00044 * to pool clients. The cp_pooled_thread only exits when the pool exits, unless 00045 * explicitly stopped (eg pthread_exit) by client code. 00046 */ 00047 typedef CPROPS_DLL struct _cp_pooled_thread 00048 { 00049 long id; ///< integral (implementation specific) thread id 00050 struct _cp_thread_pool *owner; ///< the pool this thread belongs to 00051 cp_thread *worker; ///< the actual thread 00052 cp_thread_action action; ///< thread function for the next assignment 00053 void *action_prm; ///< parameter for the next assignment 00054 cp_thread_stop_fn stop_fn; ///< called on cp_pooled_thread_stop 00055 void *stop_prm; ///< if not set, stop_fn invoked w/ action_prm 00056 00057 cp_mutex *suspend_lock; ///< lock for framework scheduling 00058 cp_cond *suspend_cond; ///< condition variable for framework scheduling 00059 00060 int done; ///< done flag 00061 int wait; ///< wait flag 00062 } cp_pooled_thread; 00063 00064 /** return a thread id for this thread */ 00065 CPROPS_DLL 00066 long cp_pooled_thread_get_id(cp_pooled_thread *thread); 00067 00068 /** thread constructor function */ 00069 CPROPS_DLL 00070 cp_pooled_thread *cp_pooled_thread_create(struct _cp_thread_pool *owner); 00071 00072 /** thread destructor */ 00073 CPROPS_DLL 00074 void cp_pooled_thread_destroy(cp_pooled_thread *t); 00075 00076 /** signal a thread to stop */ 00077 CPROPS_DLL 00078 int cp_pooled_thread_stop(cp_pooled_thread *t); 00079 00080 /** retrieve an integer type thread id for this thread */ 00081 CPROPS_DLL 00082 long cp_pooled_thread_get_id(cp_pooled_thread *t); 00083 00084 /** sets the action and prm for this thread, then invokes 'action' */ 00085 CPROPS_DLL 00086 int cp_pooled_thread_run_task(cp_pooled_thread *pt, 00087 cp_thread_action action, 00088 void *prm); 00089 00090 /** perform action with stop function */ 00091 CPROPS_DLL 00092 int cp_pooled_thread_run_stoppable_task(cp_pooled_thread *pt, 00093 cp_thread_action action, 00094 void *action_prm, 00095 cp_thread_stop_fn stop_fn, 00096 void *stop_prm); 00097 00098 /** framework thread function */ 00099 CPROPS_DLL 00100 void *cp_pooled_thread_run(void *prm); 00101 00102 00103 /** 00104 * cp_thread_pool holds a list of free threads (in wait mode). The list grows 00105 * up to max_size, after which subsequent calls to cp_thread_pool_get will 00106 * block, and calls to cp_thread_pool_get_nb will return NULL - until clients 00107 * return their threads to the pool. 00108 */ 00109 typedef CPROPS_DLL struct _cp_thread_pool 00110 { 00111 int size; ///< current size 00112 00113 int min_size; ///< initial size 00114 int max_size; ///< size limit 00115 00116 int running; 00117 00118 cp_mutex *pool_lock; ///< to sync thread assignment and release 00119 cp_cond *pool_cond; ///< to sync thread assignment and release 00120 00121 cp_list *free_pool; ///< holder for unused threads 00122 cp_hashlist *in_use; ///< holder for running threads 00123 } cp_thread_pool; 00124 00125 /** cp_thread_pool constructor */ 00126 CPROPS_DLL 00127 cp_thread_pool *cp_thread_pool_create(int min_size, int max_size); 00128 00129 /** cp_thread_pool destructor */ 00130 CPROPS_DLL 00131 void cp_thread_pool_destroy(cp_thread_pool *pool); 00132 00133 /** wait for threads to finish processing client requests */ 00134 CPROPS_DLL 00135 int cp_thread_pool_wait(cp_thread_pool *pool); 00136 00137 /** signal all threads in this pool to stop */ 00138 CPROPS_DLL 00139 int cp_thread_pool_stop(cp_thread_pool *pool); 00140 00141 /** 00142 * request a thread from the pool. If no threads are available, this function 00143 * will block until a thread becomes available. 00144 */ 00145 CPROPS_DLL 00146 cp_thread *cp_thread_pool_get(cp_thread_pool *pool, 00147 cp_thread_action action, 00148 void *prm); 00149 00150 /** 00151 * request a thread from the pool. If no threads are available, this function 00152 * will block until a thread becomes available. 00153 */ 00154 CPROPS_DLL 00155 cp_thread *cp_thread_pool_get_stoppable(cp_thread_pool *pool, 00156 cp_thread_action action, 00157 void *action_prm, 00158 cp_thread_stop_fn stop_fn, 00159 void *stop_prm); 00160 00161 /** 00162 * request a thread from the pool - non-blocking version. Returns a pointer 00163 * to the requested thread if one is available or NULL if the pool is empty. 00164 */ 00165 CPROPS_DLL 00166 cp_thread *cp_thread_pool_get_nb(cp_thread_pool *pool, 00167 cp_thread_action action, 00168 void *prm); 00169 /** 00170 * request a thread from the pool - non-blocking version. Returns a pointer 00171 * to the requested thread if one is available or NULL if the pool is empty. 00172 */ 00173 CPROPS_DLL 00174 cp_thread *cp_thread_pool_get_stoppable_nb(cp_thread_pool *pool, 00175 cp_thread_action action, 00176 void *action_prm, 00177 cp_thread_stop_fn stop_fn, 00178 void *stop_prm); 00179 00180 /** returns the number of available threads in the pool. */ 00181 CPROPS_DLL 00182 int cp_thread_pool_count_available(cp_thread_pool *pool); 00183 00184 /* ************************************************************************** 00185 * * 00186 * thread management framework * 00187 * * 00188 ************************************************************************** */ 00189 00190 00191 /** 00192 * Definitions for thread management framework follow. The framework is based 00193 * on the cp_thread_pool and cp_pooled_thread types. 00194 * <br> 00195 * The pooled thread scheduler interface is meant for use by clients who 00196 * require a variable number of threads. Each such component should create 00197 * an instance of cp_pooled_thread_client_interface and use the api functions 00198 * to get threads from the underlying cp_thread_pool. Here is some example 00199 * code. 00200 * <p><tt><code><pre> 00201 * cp_pooled_thread_scheduler *main_scheduler; 00202 * 00203 * ... 00204 * 00205 * component_a_start(component_a *a, ...) 00206 * { 00207 * a->scheduler_interface = 00208 * cp_pooled_thread_client_interface_create(main_scheduler, a, 2, 10, 00209 * component_a_report_load, component_a_stop_thread, 00210 * component_a_thread_run, a); 00211 * 00212 * ... 00213 * 00214 * for (i = 0; i < a->scheduler_interface->min; i++) 00215 * cp_pooled_thread_client_get(a->scheduler_interface); 00216 * } 00217 * 00218 * component_b_start(component_b *b, ...) 00219 * { 00220 * b->scheduler_interface = 00221 * cp_pooled_thread_client_interface_create(main_scheduler, b, 2, 10, 00222 * component_a_report_load, component_a_stop_thread, 00223 * component_a_thread_run, b); 00224 * 00225 * ... 00226 * 00227 * for (i = 0; i < b->scheduler_interface->min; i++) 00228 * cp_pooled_thread_client_get(b->scheduler_interface); 00229 * } 00230 * 00231 * </tt></code></pre><br> 00232 * In this example, the threads for component_a and component_b will be 00233 * managed jointly, since their cp_pooled_thread_client_interface *'s have the 00234 * same cp_pooled_thread_scheduler *. <p> 00235 * 00236 * 00237 * See cp_pooled_thread_client_negociate for details. 00238 */ 00239 typedef CPROPS_DLL struct cp_pooled_thread_scheduler 00240 { 00241 cp_thread_pool *pool; ///< pool to operate on 00242 cp_vector *client_list; ///< list of clients 00243 int bypass; ///< when bypass flag set 'negociate' function becomes nop 00244 } cp_pooled_thread_scheduler; 00245 00246 CPROPS_DLL struct _cp_pooled_thread_client_interface; 00247 00248 typedef int (*cp_pooled_thread_report_load) //~~ change prm to void *client 00249 (struct _cp_pooled_thread_client_interface *s); 00250 typedef void (*cp_pooled_thread_shrink) //~~ change prm to void *client 00251 (struct _cp_pooled_thread_client_interface *); 00252 00253 /** 00254 * cp_pooled_thread_client_interface acts as the link to the 00255 * cp_pooled_thread_scheduler for clients that require a variable number of 00256 * threads. This interface holds 3 functions pointers that must be supplied 00257 * by a client: <br> 00258 * <li> report_load - should return the number of open requests the client has 00259 * to handle 00260 * <li> shrink - will be called by the framework to stop one client thread 00261 * <li> action - the thread function for this client 00262 */ 00263 typedef CPROPS_DLL struct _cp_pooled_thread_client_interface 00264 { 00265 int max; ///< thread count upper limit 00266 int min; ///< thread count bottom limit 00267 00268 int count; ///< actual number of threads serving this client 00269 00270 void *client; ///< pointer to client 00271 cp_pooled_thread_scheduler *owner; ///< pointer to thread_pool_scheduler 00272 cp_pooled_thread_report_load report_load; ///< client function to report load 00273 cp_pooled_thread_shrink shrink; ///< client function to stop 1 thread 00274 cp_thread_action action; ///< client thread function 00275 void *action_prm; ///< parameter to client thread function (usually == client) 00276 cp_thread_stop_fn stop_fn; ///< stop callback 00277 void *stop_prm; ///< parameter to stop callback - if NULL action_prm will be used 00278 } cp_pooled_thread_client_interface; 00279 00280 /** cp_pooled_thread_client_interface constructor */ 00281 CPROPS_DLL 00282 cp_pooled_thread_client_interface * 00283 cp_pooled_thread_client_interface_create 00284 (cp_pooled_thread_scheduler *owner, 00285 void *client, 00286 int min_threads, 00287 int max_threads, 00288 cp_pooled_thread_report_load report_load, 00289 cp_pooled_thread_shrink shrink, 00290 cp_thread_action action, 00291 void *action_prm, 00292 cp_thread_stop_fn stop_fn, 00293 void *stop_prm); 00294 00295 /** cp_pooled_thread_client_interface destructor */ 00296 CPROPS_DLL 00297 void cp_pooled_thread_client_interface_destroy 00298 (cp_pooled_thread_client_interface *client); 00299 00300 /** 00301 * threads should call negociate when a change in the number of threads a 00302 * client owns is required. 00303 * Two possible scheduling approaches are - 00304 * 00305 * (1) centralized: 00306 * Clients report their load factor to the thread manager. The thread 00307 * manager grants requests for new threads to clients with higher loads 00308 * first. 00309 * 00310 * (2) distributed: 00311 * clients negociate with other clients requesting a thread based on their 00312 * load factors. The client with the lower load factor releases a thread 00313 * to the client with the higher load factor. 00314 * 00315 * The distributed approach saves some bookkeeping over head in the thread 00316 * manager, reduces the number steps involved in acquiring a new thread or 00317 * releasing an unused one, and makes a dedicated synchronization thread 00318 * unnecessary. <p> 00319 * 00320 * In the current implementation, the scheduler will randomly choose one 00321 * other client to negociate with. If the load factors are different enough, 00322 * one thread will be switched to the busier client. 00323 */ 00324 CPROPS_DLL 00325 void cp_pooled_thread_client_negociate(cp_pooled_thread_client_interface *c); 00326 00327 /** cp_pooled_thread_scheduler constructor */ 00328 CPROPS_DLL 00329 cp_pooled_thread_scheduler *cp_pooled_thread_scheduler_create(cp_thread_pool *pool); 00330 00331 /** cp_pooled_thread_scheduler destructor */ 00332 CPROPS_DLL 00333 void cp_pooled_thread_scheduler_destroy(cp_pooled_thread_scheduler *scheduler); 00334 00335 /** register client as a client of this scheduler */ 00336 CPROPS_DLL 00337 void cp_pooled_thread_scheduler_register_client 00338 (cp_pooled_thread_scheduler *scheduler, 00339 cp_pooled_thread_client_interface *client); 00340 00341 /** 00342 * convenience to abstract cp_thread_pool based implementation, see 00343 * cp_pooled_thread_get and cp_pooled_thread_get_nb 00344 */ 00345 CPROPS_DLL 00346 void cp_pooled_thread_client_get(cp_pooled_thread_client_interface *c); 00347 00348 /** 00349 * convenience to abstract cp_thread_pool based implementation, see 00350 * cp_pooled_thread_get and cp_pooled_thread_get_nb 00351 */ 00352 CPROPS_DLL 00353 void cp_pooled_thread_client_get_stoppable(cp_pooled_thread_client_interface *c); 00354 00355 /** 00356 * convenience to abstract cp_thread_pool based implementation, see 00357 * cp_pooled_thread_get and cp_pooled_thread_get_nb 00358 */ 00359 CPROPS_DLL 00360 void cp_pooled_thread_client_get_nb(cp_pooled_thread_client_interface *c); 00361 00362 /** 00363 * convenience to abstract cp_thread_pool based implementation, see 00364 * cp_pooled_thread_get and cp_pooled_thread_get_nb 00365 */ 00366 CPROPS_DLL 00367 void cp_pooled_thread_client_get_stoppable_nb(cp_pooled_thread_client_interface *c); 00368 00369 __END_DECLS 00370 00371 /** @} */ 00372 00373 #endif /* _CP_THREAD */ 00374