thread_pool_create()
Create a thread pool handle
Synopsis:
#include <sys/iofunc.h>
#include <sys/resmgr.h>
#include <sys/dispatch.h>
thread_pool_t * thread_pool_create (
thread_pool_attr_t * pool_attr,
unsigned flags );
Arguments:
- pool_attr
- A pointer to a thread_pool_attr_t structure that
specifies the attributes that you want to use for the thread pool.
For more information, see
Thread-pool attributes,
below. - flags
- Flags (defined in <sys/dispatch.h>) that affect
what happens to the thread that's creating the pool:
- POOL_FLAG_CALL_BLOCK_ERRF — call the error function on an error returned by the blocking function (i.e. if the blocking function returns NULL).
- POOL_FLAG_CALL_HANDLE_ERRF — call the error function on an error returned by the handler function (i.e. if the handler function returns -1).
- POOL_FLAG_EXIT_SELF — when the pool is started, exit the thread that called thread_pool_start().
- POOL_FLAG_USE_SELF — when the pool is started, use the thread that called thread_pool_start() as part of the pool.
If neither POOL_FLAG_EXIT_SELF nor POOL_FLAG_USE_SELF is set, the thread_pool_start() function will return, with new threads being created as required.
Library:
libc
Use the -l c option to qcc to link against this library. This library is usually included automatically.
Description:
The thread_pool_create() function creates a thread pool handle, which you can then pass to thread_pool_start() in order to start a thread pool. You can use these functions to create and manage a pool of worker threads.
The worker threads work in the following way:
- When a new worker thread is created, a context is allocated, which the thread uses to do its work.
- The thread then calls the blocking function. This function blocks until the thread has work to do. For example, the blocking function could call MsgReceive() to wait for a message.
- After the blocking function returns, the worker thread calls the handler function, which performs the actual work.
- When the handler function returns, the thread calls the blocking function again.
The thread continues to block and handle events until the thread pool decides this worker thread is no longer needed. Finally, when the worker thread exits, it releases the allocated context.
The thread pool manages these worker threads so that there's a certain number of them in the blocked state. Thus, as threads become busy in the handler function, the thread pool creates new threads to keep a minimum number of threads in a state where they can accept requests from clients. By the same token, if the demand on the thread pool goes down, the thread pool lets some of these blocked threads exit.
Thread-pool attributes
The pool_attr is a pointer to a thread_pool_attr_t structure in which you specify:
- the functions to call when a new thread is started or one dies, in order to allocate and free contexts used by threads
- blocking and handler functions
- parameters of the thread pool, such as the number of worker threads
pool_attr.tid_name = "fsys_resmgr";
but using a local or automatic variable like this isn't:
{
char name[32];
snprintf(name, sizeof(name), "cam %d:%d", cam.path, cam.target);
pool_attr.tid_name = name;
...
return;
}
The thread_pool_attr_t structure is defined in <sys/dispatch.h> as:
typedef struct _thread_pool_attr {
THREAD_POOL_HANDLE_T *handle;
THREAD_POOL_PARAM_T *(*block_func)
(THREAD_POOL_PARAM_T *ctp);
void (*unblock_func)
(THREAD_POOL_PARAM_T *ctp);
int (*handler_func)
(THREAD_POOL_PARAM_T *ctp);
THREAD_POOL_PARAM_T *(*context_alloc)
(THREAD_POOL_HANDLE_T *handle);
void (*context_free)
(THREAD_POOL_PARAM_T *ctp);
pthread_attr_t *attr;
unsigned short lo_water;
unsigned short increment;
unsigned short hi_water;
unsigned short maximum;
const char *tid_name;
void (*error_func)
(unsigned flags, int err_value);
unsigned reserved[5];
} thread_pool_attr_t;
#include
<sys/resmgr.h> before
<sys/dispatch.h>.
The members include:
- handle
- A handle that gets passed to the context_alloc function.
- block_func
- The function that's called when a worker thread is ready to block, waiting for work.
This function returns the same type of pointer that's passed to handler_func.
If a non-NULL pointer is returned, it could differ from the one
passed in, as the ctp object might have changed.
If this happens, the old ctp is no longer valid.
This function may return NULL to report an error, but if NULL is returned for any reason, the old pointer must still be valid.
- unblock_func
- The function that's called to unblock threads.
If you use
dispatch_block()
as the block_func, use
dispatch_unblock()
as the unblock_func.
Note:If you use dispatch_unblock() as the unblock_func, calling thread_pool_destroy() usually results in multiple handler_func failures, since each thread is unblocked by an unhandled pulse.
- handler_func
- This function is called after block_func returns to do some work and is passed the pointer returned by block_func. This function may return -1 to report an error.
- context_alloc
- The function that's called when a new thread is created by the thread pool. It is passed handle. The function returns a pointer, which is then passed to the blocking function, block_func. This function may return NULL to report an error.
- context_free
- The function that's called when a worker thread exits, to free the context allocated with context_alloc. The actual call to this function can happen after thread_pool_destroy() returns, so the function code must not reference thread pool memory, which could have been freed.
- attr
- A pointer to a pthread_attr_t structure (see pthread_attr_init()) that's passed to pthread_create() to specify the stack size, priority, etc. of the worker threads. If this member is NULL, default values are used.
- lo_water
- The minimum number of threads that the pool should keep in the blocked state (i.e., threads that are ready to do work).
- increment
- The number of new threads created at one time.
- hi_water
- The maximum number of threads to keep in a blocked state.
- maximum
- The maximum number of threads that the pool can create.
- tid_name
- NULL, or a pointer to a null-terminated name for the threads in the pool. If set, this string is passed to pthread_setname_np() when the thread pool creates a new thread.
- error_func
- The worker thread always calls this function when the context_alloc function fails. It also calls this function when the block_func or handler_func functions fail, provided the appropriate flags values have been passed to thread_pool_create().
Returns:
A thread pool handle, or NULL if an error occurs (errno is set).
Errors:
- ENOMEM
- Insufficient memory to allocate internal data structures.
Examples:
Here's a simple, multithreaded resource manager:
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <sys/iofunc.h>
#include <sys/dispatch.h>
#include <sys/neutrino.h>
#define DEV_COUNT 3
static resmgr_connect_funcs_t connect_funcs;
static resmgr_io_funcs_t io_funcs;
static iofunc_attr_t attrs[DEV_COUNT];
int
main(int argc, char **argv) {
thread_pool_attr_t pool_attr;
thread_pool_t *tpp;
dispatch_t *dpp;
if((dpp = dispatch_create()) == NULL) {
fprintf( stderr,
"%s: Unable to allocate dispatch handle.\n",
argv[0] );
return EXIT_FAILURE;
}
memset( &pool_attr, 0, sizeof pool_attr );
pool_attr.handle = dpp;
pool_attr.context_alloc = (void *) dispatch_context_alloc;
pool_attr.block_func = (void *) dispatch_block;
pool_attr.unblock_func = (void *) dispatch_unblock;
pool_attr.handler_func = (void *) dispatch_handler;
pool_attr.context_free = (void *) dispatch_context_free;
pool_attr.lo_water = 2;
pool_attr.hi_water = 4;
pool_attr.increment = 1;
pool_attr.maximum = 50;
pool_attr.tid_name = "my_thread_pool";
if((tpp = thread_pool_create( &pool_attr,
POOL_FLAG_EXIT_SELF)) == NULL ) {
fprintf(stderr,
"%s: Unable to initialize thread pool.\n",
argv[0]);
return EXIT_FAILURE;
}
iofunc_func_init( _RESMGR_CONNECT_NFUNCS,
&connect_funcs,
_RESMGR_IO_NFUNCS, &io_funcs );
for(int i = 0; i < DEV_COUNT; i++ ) {
char attach_path[64];
iofunc_attr_init( &attrs[i], S_IFNAM | 0666, 0, 0 );
sprintf(attach_path, "/dev/mydevice%d", i );
if(resmgr_attach( dpp, NULL, attach_path, _FTYPE_ANY,
0, &connect_funcs, &io_funcs,
&attrs[i] ) == -1) {
fprintf( stderr,
"%s: Unable to attach name.\n", argv[0] );
return EXIT_FAILURE;
}
}
/* Never returns */
thread_pool_start( tpp );
return EXIT_SUCCESS;
}
For more examples using the dispatch interface, see
dispatch_create(),
message_attach(), and
resmgr_attach().
For more information on designing and implementing multithreaded resource managers,
see the Multithreaded
Resource Managers
chapter of Writing a Resource Manager.
Classification:
Safety: | |
---|---|
Cancellation point | No |
Signal handler | No |
Thread | Yes |