The basic skeleton of a resource manager

The following can be used as a template for a resource manager with multiple threads. (We've already seen a template that can be used for a single-threaded resource manager above in “The resource manager library,” when we discussed a /dev/null resource manager).

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

static resmgr_connect_funcs_t   connect_func;
static resmgr_io_funcs_t        io_func;
static iofunc_attr_t            attr;

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 context.\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.handler_func  = (void *) dispatch_handler;
    pool_attr.context_free  = (void *) dispatch_context_free;

    // 1) set up the number of threads that you want
    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_func,
                      _RESMGR_IO_NFUNCS, &io_func);
    iofunc_attr_init (&attr, S_IFNAM | 0777, 0, 0);

    // 2) override functions in "connect_func" and "io_func" as
    // required here

    memset (&resmgr_attr, 0, sizeof (resmgr_attr));
    resmgr_attr.nparts_max = 1;
    resmgr_attr.msg_max_size = 2048;

    // 3) replace "/dev/whatever" with your device name
    if ((id = resmgr_attach (dpp, &resmgr_attr, "/dev/whatever",
                _FTYPE_ANY, 0, &connect_func, &io_func,
                &attr)) == -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 information about the dispatch interface (i.e., the dispatch_create() function), see the documentation in the QNX Neutrino C Library Reference.

Step 1
Here you'd use the thread pool functions to create a pool of threads that will be able to service messages in your resource manager. Generally, I recommend that you start off with a single-threaded resource manager, as we did with the /dev/null example mentioned above. Once you have the basic functionality running, you can then add threads. You'd modify the lo_water, hi_water, increment, and maximum members of the pool_attr structure as described in the Threads & Processes chapter where we discuss the thread pool functions.
Step 2
Here you'd add whatever functions you want to supply. These are the outcalls we just discussed (e.g., read I/O function handler, device control I/O function handler, etc.) For example, to add your own handler for the _IO_READ message that points to a function supplied by you called my_io_read(), you'd add the following line of code:
io_func.io_read = my_io_read;
  

This overrides the POSIX-layer default function that got put into the table by iofunc_func_init() with a pointer to your function, my_io_read().

Step 3
You probably don't want your resource manager to be called /dev/whatever, so you should select an appropriate name. Note that the resmgr_attach() function is where you bind the attributes structure (the attr parameter) to the name—if you wish to have multiple devices handled by your resource manager, you'd call resmgr_attach() multiple times, with different attributes structures (so that you could tell the different registered names apart at runtime).