![]() |
![]() |
![]() |
![]() |
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);
}
}
![]() |
![]() |
![]() |
![]() |