[Previous] [Contents] [Index] [Next]

thread_pool_create()

Create a thread pool handle

Synopsis:

#include <sys/iofunc.h>
#include <sys/dispatch.h>

thread_pool_t * thread_pool_create (
                  thread_pool_attr_t * pool_attr,
                  unsigned flags );

Library:

libc

Description:

The thread_pool_create() function creates a thread pool handle. This handle is then used to start a thread pool with thread_pool_start(). With the thread pool functions, you can create and manage a pool of worker threads.

How it works

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 will continue 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 such 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 will let some of these blocked threads exit.

The pool_attr argument

The pool_attr argument sets the:

The argument pool_attr should have the members of the thread_pool_attr_t structure filled in:

typedef struct _thread_pool_attr {
   THREAD_POOL_HANDLE_T   *handle;
   THREAD_POOL_PARAM_T    *(*block_func)
                             (THREAD_POOL_PARAM_T *ctp);
   THREAD_POOL_PARAM_T    *(*context_alloc)
                             (THREAD_POOL_HANDLE_T *handle);
   void                   (*unblock_func)
                             (THREAD_POOL_PARAM_T *ctp);
   int                    (*handler_func)
                             (THREAD_POOL_PARAM_T *ctp);
   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;
   unsigned               reserved[8];
} thread_pool_attr_t;

Here's a description of the structure members:

pool_attr->handle
The argument pool_attr->handle is a handle that gets passed to the context_alloc function.
pool_attr->block_func
The function pool_attr->block_func is called when the worker thread is ready to block, waiting for work. It returns a pointer that's passed to pool_attr->handler_func. If the function returns NULL, it indicates to the thread pool that this thread should exist.
pool_attr->context_alloc
The function pool_attr->context_alloc is called when a new thread is created by the thread pool. It is passed pool_attr->handle. The function returns a pointer which is then passed to the blocking function pool_attr->block_func.
pool_attr->handler_func
The function pool_attr->handler_func is called after pool_attr->block_func returns to do some work. The function is passed in the pointer returned by pool_attr->block_func.
pool_attr->context_free
The function pool_attr->context_free is called when the worker thread exits, to free the context allocated with pool_attr->context_alloc.
pool_attr->attr
The argument pool_attr->attr is a pointer to a pthread_attr_*() function that's passed to pthread_create(). The pthread_attr_*() functions set the stack size, priority, etc. of the worker threads. If NULL, default values are used.
pool_attr->lo_water
The argument pool_attr->lo_water is the minimum number of threads that the pool should keep in the blocked state (i.e. threads that are ready to do work).
pool_attr->increment
The argument pool_attr->increment is the number of new threads created at one time.
pool_attr->hi_water
The argument pool_attr->hi_water is the maximum number of threads to keep in a blocked state.
pool_attr->maximum
The argument pool_attr->maximum is the maximum number of threads that the pool can create.

The flags argument

The possible flags (defined in <sys/dispatch.h>) are:

POOL_FLAG_EXIT_SELF
When the pool is started using thread_pool_start(), exit the thread that called this function.
POOL_FLAG_USE_SELF
When the pool is started, use the calling thread as part of the pool.

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 multi-threaded resource manager:

/* Define an appropriate interrupt number: */
#define INTNUM 0     

#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <sys/iofunc.h>
#include <sys/dispatch.h>
#include <sys/neutrino.h>

static resmgr_connect_funcs_t   connect_funcs;
static resmgr_io_funcs_t        io_funcs;
static iofunc_attr_t            attr;

void *interrupt_thread( void *data)    
/* *data isn't used */
{
    struct sigevent event;
    int             id;

    /* fill in "event" structure */
    memset( &event, 0, sizeof(event) );
    event.sigev_notify = SIGEV_INTR;

    /* INTNUM is the desired interrupt level */
    id = InterruptAttachEvent( INTNUM, &event, 0 );
    
    ...

    while (1) {
        InterruptWait( 0, NULL );
        /*
         do something about the interrupt,
         perhaps updating some shared
         structures in the resource manager

         unmask the interrupt when done
        */
        InterruptUnmask( INTNUM, id );
    }
}

int
main(int argc, char **argv) {
    thread_pool_attr_t    pool_attr;
    thread_pool_t         *tpp;
    dispatch_t            *dpp;
    resmgr_attr_t         resmgr_attr;
    int                   id;


    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;
    /* We are only doing resmgr-type attach */
    pool_attr.context_alloc = resmgr_context_alloc;
    pool_attr.block_func = resmgr_block;
    pool_attr.handler_func = resmgr_handler;
    pool_attr.context_free = resmgr_context_free;
    pool_attr.lo_water = 2;
    pool_attr.hi_water = 4;
    pool_attr.increment = 1;
    pool_attr.maximum = 50;

    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 );
    iofunc_attr_init( &attr, S_IFNAM | 0666, 0, 0 );
        
    memset( &resmgr_attr, 0, sizeof resmgr_attr );
    resmgr_attr.nparts_max = 1;
    resmgr_attr.msg_max_size = 2048;

    if((id = resmgr_attach( dpp, &resmgr_attr, 
                            "/dev/mynull", 
                           _FTYPE_ANY, 0, &connect_funcs, 
                            &io_funcs, 
                            &attr )) == -1) {
        fprintf( stderr, 
                 "%s: Unable to attach name.\n", argv[0] );
        return EXIT_FAILURE;
    }

    /* Start the thread which will handle interrupt events. */
    pthread_create ( NULL, NULL, interrupt_thread, NULL );

    /* Never returns */
    thread_pool_start( tpp );
}

For more examples using the dispatch interface, see dispatch_create(), message_attach(), and resmgr_attach(). For information on advanced topics in designing and implementing a resource manager, see "Combine messages" section of the Writing a Resource Manager chapter in the Programmer's Guide.

Classification:

QNX 6

Safety:
Cancellation point Yes
Interrupt handler No
Signal handler No
Thread Yes

See also:

dispatch_create(), pthread_create(), resmgr_attach(), select_attach(), thread_pool_destroy(), thread_pool_start()


[Previous] [Contents] [Index] [Next]