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 );

Since:

BlackBerry 10.0.0

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_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.

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. 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 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 argument sets the:

  • functions that get called, when a new thread is started or one dies, to allocate and free contexts used by threads
  • blocking and handler functions
  • parameters of the thread pool such as the number of worker threads, etc.

The thread_pool_attr_t structure that the pool_attr argument points to 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;
    unsigned                reserved[7];
} thread_pool_attr_t;

The members include:

handle
A handle that gets passed to the context_alloc function.
block_func
The function that's called when the worker thread is ready to block, waiting for work. This function returns a pointer that's passed to handler_func.
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.
handler_func
The function that's called after block_func returns to do some work. The function is passed the pointer returned by block_func.
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.
context_free
The function that's called when the worker thread exits, to free the context allocated with context_alloc.
attr
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.
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.

The scope of the tid_name string must match the lifetime of the thread pool itself. For example, this is valid:

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;
}

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:

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

#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.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; 
    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 );
    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 );
    return EXIT_SUCCESS;
}

For more examples using the dispatch interface, see dispatch_create(), message_attach(), and resmgr_attach().

Classification:

QNX Neutrino

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

Last modified: 2014-06-24



Got questions about leaving a comment? Get answers from our Disqus FAQ.

comments powered by Disqus