Appendix: Sample Programs

This appendix contains the complete versions of some of the sample programs discussed in this book:

atoz.c

For more information about this program, see the example in the Returning directory entries section of the Resource Managers chapter.

/*
 *  atoz.c
 *
 *  /dev/atoz using the resource manager library
*/

#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <errno.h>
#include <dirent.h>
#include <limits.h>
#include <sys/iofunc.h>
#include <sys/dispatch.h>

#define ALIGN(x) (((x) + 3) & ~3)
#define NUM_ENTS            26

static  iofunc_attr_t   atoz_attrs [NUM_ENTS];

static int
my_open (resmgr_context_t *ctp, io_open_t *msg, iofunc_attr_t *attr, void *extra)
{
    if (msg -> connect.path [0] == 0)
    {   // the directory (/dev/atoz)
        return (iofunc_open_default (ctp, msg, attr, extra));
    } else if (msg -> connect.path [1] == 0 &&
              (msg -> connect.path [0] >= 'a' &&
               msg -> connect.path [0] <= 'z'))
    {   // the file (/dev/atoz/[a-z])
        return (iofunc_open_default (ctp, msg,
                  atoz_attrs + msg -> connect.path [0] - 'a', extra));
    } else {
        return (ENOENT);
    }
}

int
dirent_size (char *fname)
{
    return (ALIGN (sizeof (struct dirent) - 4 + strlen (fname)));
}

struct dirent *
dirent_fill (struct dirent *dp, int inode, int offset, char *fname)
{
    dp -> d_ino = inode;
    dp -> d_offset = offset;
    strcpy (dp -> d_name, fname);
    dp -> d_namelen = strlen (dp -> d_name);
    dp -> d_reclen = ALIGN (sizeof (struct dirent) - 4 + dp -> d_namelen);
    return ((struct dirent *) ((char *) dp + dp -> d_reclen));
}

static int
my_read_dir (resmgr_context_t *ctp, io_read_t *msg, iofunc_ocb_t *ocb)
{
    int     nbytes;
    int     nleft;
    struct  dirent *dp;
    char    *reply_msg;
    char    fname [_POSIX_PATH_MAX];

    // allocate a buffer for the reply
    reply_msg = calloc (1, msg -> i.nbytes);
    if (reply_msg == NULL) {
        return (ENOMEM);
    }

    // assign output buffer
    dp = (struct dirent *) reply_msg;

    // we have "nleft" bytes left
    nleft = msg -> i.nbytes;
    while (ocb -> offset < NUM_ENTS) {

        // create the filename
        sprintf (fname, "%c", ocb -> offset + 'a');

        // see how big the result is
        nbytes = dirent_size (fname);

        // do we have room for it?
        if (nleft - nbytes >= 0) {

            // fill the dirent, and advance the dirent pointer
            dp = dirent_fill (dp, ocb -> offset + 1, ocb -> offset, fname);

            // move the OCB offset
            ocb -> offset++;

            // account for the bytes we just used up
            nleft -= nbytes;
        } else {

            // don't have any more room, stop
            break;
        }
    }

    // return info back to the client
    MsgReply (ctp -> rcvid, (char *) dp - reply_msg, reply_msg,
              (char *) dp - reply_msg);

    // release our buffer
    free (reply_msg);

    // tell resource manager library we already did the reply
    return (_RESMGR_NOREPLY);
}

static int
my_read_file (resmgr_context_t *ctp, io_read_t *msg, iofunc_ocb_t *ocb)
{
    int     nbytes;
    int     nleft;
    char    string;

    // we don't do any xtypes here...
    if ((msg -> i.xtype & _IO_XTYPE_MASK) != _IO_XTYPE_NONE) {
        return (ENOSYS);
    }

    // figure out how many bytes are left
    nleft = ocb -> attr -> nbytes - ocb -> offset;

    // and how many we can return to the client
    nbytes = min (nleft, msg -> i.nbytes);

    if (nbytes) {
        // create the output string
        string = ocb -> attr -> inode - 1 + 'A';

        // return it to the client
        MsgReply (ctp -> rcvid, nbytes, &string + ocb -> offset, nbytes);

        // update flags and offset
        ocb -> attr -> flags |= IOFUNC_ATTR_ATIME | IOFUNC_ATTR_DIRTY_TIME;
        ocb -> offset += nbytes;
    } else {
        // nothing to return, indicate End Of File
        MsgReply (ctp -> rcvid, EOK, NULL, 0);
    }

    // already done the reply ourselves
    return (_RESMGR_NOREPLY);
}

static int
my_read (resmgr_context_t *ctp, io_read_t *msg, iofunc_ocb_t *ocb)
{
    int     sts;

    // use the helper function to decide if valid
    if ((sts = iofunc_read_verify (ctp, msg, ocb, NULL)) != EOK) {
        return (sts);
    }

    // decide if we should perform the "file" or "dir" read
    if (S_ISDIR (ocb -> attr -> mode)) {
        return (my_read_dir (ctp, msg, ocb));
    } else if (S_ISREG (ocb -> attr -> mode)) {
        return (my_read_file (ctp, msg, ocb));
    } else {
        return (EBADF);
    }
}

int
main (int argc, char **argv)
{
    dispatch_t              *dpp;
    resmgr_attr_t           resmgr_attr;
    dispatch_context_t      *ctp;
    resmgr_connect_funcs_t  connect_func;
    resmgr_io_funcs_t       io_func;
    iofunc_attr_t           attr;
    int                     i;

    // create the dispatch structure
    if ((dpp = dispatch_create ()) == NULL) {
        perror ("Unable to dispatch_create\n");
        exit (EXIT_FAILURE);
    }

    // initialize the various data structures
    memset (&resmgr_attr, 0, sizeof (resmgr_attr));
    resmgr_attr.nparts_max = 1;
    resmgr_attr.msg_max_size = 2048;

    // bind default functions into the outcall tables
    iofunc_func_init (_RESMGR_CONNECT_NFUNCS, &connect_func,
                      _RESMGR_IO_NFUNCS, &io_func);

    // create and initialize the attributes structure for the directory
    iofunc_attr_init (&attr, S_IFDIR | 0555, 0, 0);
    attr.inode = NUM_ENTS + 1;  // 1-26 are reserved for 'a' through 'z' files
    attr.nbytes = NUM_ENTS;     // 26 entries contained in this directory

    // and for the "a" through "z" names
    for (i = 0; i < NUM_ENTS; i++) {
        iofunc_attr_init (&atoz_attrs [i], S_IFREG | 0444, 0, 0);
        atoz_attrs [i].inode = i + 1;
        atoz_attrs [i].nbytes = 1;
    }

    // add our functions; we're only interested in io_open and io_read
    connect_func.open = my_open;
    io_func.read = my_read;

    // establish a name in the pathname space
    if (resmgr_attach (dpp, &resmgr_attr, "/dev/atoz", _FTYPE_ANY,
                      _RESMGR_FLAG_DIR, &connect_func, &io_func,
                      &attr) == -1) {
        perror ("Unable to resmgr_attach\n");
        exit (EXIT_FAILURE);
    }

    // allocate a context
    ctp = dispatch_context_alloc (dpp);

    // wait here forever, handling messages
    while (1) {
        if ((ctp = dispatch_block (ctp)) == NULL) {
            perror ("Unable to dispatch_block\n");
            exit (EXIT_FAILURE);
        }
        dispatch_handler (ctp);
    }

    // you'll never get here
    return (EXIT_SUCCESS);
}

time1.c

For more information about this program, see Server-maintained timeouts in the Clocks, Timers, and Getting a Kick Every So Often chapter.

/*
 *  time1.c
 *
 *  Example of a server that receives periodic messages from
 *  a timer, and regular messages from a client.
 *
 *  Illustrates using the timer functions with a pulse.
*/

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <signal.h>
#include <errno.h>
#include <unistd.h>
#include <sys/siginfo.h>
#include <sys/neutrino.h>

// message send definitions

// messages
#define MT_WAIT_DATA        2       // message from client
#define MT_SEND_DATA        3       // message from client

// pulses
#define CODE_TIMER          1       // pulse from timer

// message reply definitions
#define MT_OK               0       // message to client
#define MT_TIMEDOUT         1       // message to client

// message structure
typedef struct
{
    int messageType;                // contains both message to and from client
    int messageData;                // optional data, depending upon message
} ClientMessageT;

typedef union
{
    ClientMessageT  msg;            // a message can be either from a client, or
    struct _pulse   pulse;          // a pulse
} MessageT;

// client table
#define MAX_CLIENT 16               // maximum number of simultaneous clients

struct
{
    int in_use;                     // is this client entry in use?
    int rcvid;                      // receive ID of client
    int timeout;                    // timeout left for client
}   clients [MAX_CLIENT];           // client table

int     chid;                       // channel ID (global)
int     debug = 1;                  // set debug value, 1 == enabled, 0 == off
char    *progname = "time1.c";

// forward prototypes
static  void setupPulseAndTimer (void);
static  void gotAPulse (void);
static  void gotAMessage (int rcvid, ClientMessageT *msg);

int
main (void)                         // ignore command-line arguments
{
    int rcvid;                      // process ID of the sender
    MessageT msg;                   // the message itself

    if ((chid = ChannelCreate (0)) == -1) {
        fprintf (stderr, "%s:  couldn't create channel!\n", progname);
        perror (NULL);
        exit (EXIT_FAILURE);
    }

    // set up the pulse and timer
    setupPulseAndTimer ();

    // receive messages
    for (;;) {
        rcvid = MsgReceive (chid, &msg, sizeof (msg), NULL);

        // determine who the message came from
        if (rcvid == 0) {
            // production code should check "code" field...
            gotAPulse ();
        } else {
            gotAMessage (rcvid, &msg.msg);
        }
    }

    // you'll never get here
    return (EXIT_SUCCESS);
}

/*
 *  setupPulseAndTimer
 *
 *  This routine is responsible for setting up a pulse so it
 *  sends a message with code MT_TIMER.  It then sets up a periodic
 *  timer that fires once per second.
*/

void
setupPulseAndTimer (void)
{
    timer_t             timerid;    // timer ID for timer
    struct sigevent     event;      // event to deliver
    struct itimerspec   timer;      // the timer data structure
    int                 coid;       // connection back to ourselves

    // create a connection back to ourselves
    coid = ConnectAttach (0, 0, chid, 0, 0);
    if (coid == -1) {
        fprintf (stderr, "%s:  couldn't ConnectAttach to self!\n", progname);
        perror (NULL);
        exit (EXIT_FAILURE);
    }

    // set up the kind of event that we want to deliver -- a pulse
    SIGEV_PULSE_INIT (&event, coid, SIGEV_PULSE_PRIO_INHERIT, CODE_TIMER, 0);

    // create the timer, binding it to the event
    if (timer_create (CLOCK_REALTIME, &event, &timerid) == -1) {
        fprintf (stderr, "%s:  couldn't create a timer, errno %d\n", 
                 progname, errno);
        perror (NULL);
        exit (EXIT_FAILURE);
    }

    // setup the timer (1s delay, 1s reload)
    timer.it_value.tv_sec = 1;
    timer.it_value.tv_nsec = 0;
    timer.it_interval.tv_sec = 1;
    timer.it_interval.tv_nsec = 0;

    // and start it!
    timer_settime (timerid, 0, &timer, NULL);
}

/*
 *  gotAPulse
 *
 *  This routine is responsible for handling the fact that a timeout
 *  has occurred.  It runs through the list of clients to see
 *  which client has timed-out, and replies to it with a timed-out
 *  response.
 */

void
gotAPulse (void)
{
    ClientMessageT  msg;
    int             i;

    if (debug) {
        time_t  now;

        time (&now);
        printf ("Got a Pulse at %s", ctime (&now));
    }

    // prepare a response message
    msg.messageType = MT_TIMEDOUT;

    // walk down list of clients
    for (i = 0; i < MAX_CLIENT; i++) {

        // is this entry in use?
        if (clients [i].in_use) {

            // is it about to time out?
            if (--clients [i].timeout == 0) {

                // send a reply
                MsgReply (clients [i].rcvid, EOK, &msg, sizeof (msg));

                // entry no longer used
                clients [i].in_use = 0;
            }
        }
    }
}

/*
 *  gotAMessage
 *
 *  This routine is called whenever a message arrives.  We look at the
 *  type of message (either a "wait for data" message, or a "here's some
 *  data" message), and act accordingly.  For simplicity, we'll assume
 *  that there is never any data waiting.  See the text for more discussion
 *  about this.
*/

void
gotAMessage (int rcvid, ClientMessageT *msg)
{
    int i;

    // determine the kind of message that it is
    switch (msg -> messageType) {

    // client wants to wait for data
    case    MT_WAIT_DATA:

        // see if we can find a blank spot in the client table
        for (i = 0; i < MAX_CLIENT; i++) {

            if (!clients [i].in_use) {

                // found one -- mark as in use, save rcvid, set timeout
                clients [i].in_use = 1;
                clients [i].rcvid = rcvid;
                clients [i].timeout = 5;
                return;
            }
        }

        fprintf (stderr, "Table full, message from rcvid %d ignored, "
                         "client blocked\n", rcvid);
        break;

    // client with data
    case    MT_SEND_DATA:

        // see if we can find another client to reply to with this 
        // client's data
        for (i = 0; i < MAX_CLIENT; i++) {

            if (clients [i].in_use) {

                // found one -- reuse the incoming message as an 
                // outgoing message
                msg -> messageType = MT_OK;

                // reply to BOTH CLIENTS!
                MsgReply (clients [i].rcvid, EOK, msg, sizeof (*msg));
                MsgReply (rcvid, EOK, msg, sizeof (*msg));

                clients [i].in_use = 0;
                return;
            }
        }

        fprintf (stderr, "Table empty, message from rcvid %d ignored, "
                         "client blocked\n", rcvid);
        break;
    }
}

tp1.c

For more information about this program, see Controlling the number of threads in the Processes and Threads chapter.

/*
 *	tp1.c
 *
 *	Thread Pool Example (1)
 *
 *	1999 06 26 R. Krten
*/

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

char *progname = "tp1";

void
tag (char *name)
{
	time_t	t;
	char	buffer [BUFSIZ];

	time (&t);
	strftime (buffer, BUFSIZ, "%T ", localtime (&t));
	printf ("%s %3d %-20.20s:  ", buffer, pthread_self (), name);
}
	                                     
THREAD_POOL_PARAM_T *
blockfunc (THREAD_POOL_PARAM_T *ctp)
{
	tag ("blockfunc"); printf ("ctp %p\n", ctp);
	tag ("blockfunc"); printf ("sleep (%d);\n", 15 * pthread_self ());
	sleep (pthread_self () * 15);
	tag ("blockfunc"); printf ("done sleep\n");
	tag ("blockfunc"); printf ("returning 0x%08X\n", 0x10000000 + pthread_self ());
	return ((void *) (0x10000000 + pthread_self ()));		// passed to handlerfunc
}

THREAD_POOL_PARAM_T *
contextalloc (THREAD_POOL_HANDLE_T *handle)
{
	tag ("contextalloc"); printf ("handle %p\n", handle);
	tag ("contextalloc"); printf ("returning 0x%08X\n", 0x20000000 + pthread_self ());
	return ((void *) (0x20000000 + pthread_self ()));		// passed to blockfunc
}

void
contextfree (THREAD_POOL_PARAM_T *param)
{
	tag ("contextfree"); printf ("param %p\n", param);
}

void
unblockfunc (THREAD_POOL_PARAM_T *ctp)
{
	tag ("unblockfunc"); printf ("ctp %p\n", ctp);
}

int
handlerfunc (THREAD_POOL_PARAM_T *ctp)
{
	static int i = 0;

	tag ("handlerfunc"); printf ("ctp %p\n", ctp);
	if (i++ > 15) {
		tag ("handlerfunc"); printf ("exceeded 15 operations, return 0\n");
		return (0);
	}
	tag ("handlerfunc"); printf ("sleep (%d)\n", pthread_self () * 25);
	sleep (pthread_self () * 25);
	tag ("handlerfunc"); printf ("done sleep\n");

/*
	i = 0;
	if (i++ & 1) {
		tag ("handlerfunc"); printf ("returning 0\n");
		return (0);
	} else {
*/
		tag ("handlerfunc"); printf ("returning 0x%08X\n", 0x30000000 + pthread_self ());
		return (0x30000000 + pthread_self ());
/*
	}
*/
}


main ()
{
	thread_pool_attr_t	tp_attr;
	void				*tpp;

	memset (&tp_attr, 0, sizeof (tp_attr));
	tp_attr.handle = (void *) 0x12345678;		// passed to contextalloc
	tp_attr.block_func = blockfunc;
	tp_attr.unblock_func = unblockfunc;
	tp_attr.context_alloc = contextalloc;
	tp_attr.context_free = contextfree;
	tp_attr.handler_func = handlerfunc;

	tp_attr.lo_water = 3;
	tp_attr.hi_water = 7;
	tp_attr.increment = 2;
	tp_attr.maximum = 10;

    tpp = thread_pool_create (&tp_attr, POOL_FLAG_USE_SELF);
    if (tpp == NULL) {
        fprintf (stderr, 
                 "%s:  can't thread_pool_create, errno %s\n",
                 progname, strerror (errno));
        exit (EXIT_FAILURE);
    }

	thread_pool_start (tpp);

	fprintf (stderr, "%s:  thread_pool_start returned; errno %s\n",
             progname, strerror (errno));
	sleep (3000);
	exit (EXIT_FAILURE);
}

tt1.c

For more information about this program, see Kernel timeouts with pthread_join() in the Clocks, Timers, and Getting a Kick Every So Often chapter.

/*
 * tt1.c
*/

#include <stdio.h>
#include <pthread.h>
#include <inttypes.h>
#include <errno.h>
#include <sys/neutrino.h>

#define SEC_NSEC 1000000000LL // 1 billion nanoseconds in a second

void *
long_thread (void *notused)
{
    printf ("This thread runs for more than 10 seconds\n");
    sleep (20);
}

int
main (void) // ignore arguments
{
    uint64_t        timeout;
    struct sigevent event;
    int             rval;
    pthread_t       thread_id;

    // set up the event -- this can be done once
    
    // This or event.sigev_notify = SIGEV_UNBLOCK:
    SIGEV_UNBLOCK_INIT (&event);

    // create a thread
    pthread_create (&thread_id, NULL, long_thread, NULL);

    // set up for 10 second timeout
    timeout = 10LL * SEC_NSEC;

    TimerTimeout (CLOCK_REALTIME, _NTO_TIMEOUT_JOIN, &event, &timeout, NULL);

    rval = pthread_join (thread_id, NULL);
    if (rval == ETIMEDOUT) {
        printf ("Thread %d is still running after 10 seconds!\n",
                thread_id);
    }

    sleep (5);
    
   
    TimerTimeout (CLOCK_REALTIME, _NTO_TIMEOUT_JOIN, &event, &timeout, NULL);
    rval = pthread_join (thread_id, NULL);
    if (rval == ETIMEDOUT) {
        printf ("Thread %d is still running after 25 seconds (bad)!\n",
                thread_id);
    } else {
        printf ("Thread %d finished (expected!)\n", thread_id);
    }
}