This appendix contains the complete versions of some of the sample programs discussed in this book:
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); }
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; } }
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); }
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); } }