Appendix Four: Thread-Safe and Process-Safe Reporting and Logging Functions

Here we present the reporting and logging functions referred to in the text of the book on several occasions, especially in Chapter 11. This particular example is POSIX 1 compliant.

All functions comply with the same syntax and semantics for arguments as the printf() function (i.e., variable number of standard arguments). Functions named in all lowercase ( sys_exit() , sys__exit() , msg() , msg_exit() , msg__exit() , and msg() ) write their messages to the standard output (i.e., to the screen of the terminal under normal circumstances), whereas functions whose names begin with an uppercase letter ( Sys_exit() , Sys__exit() , Sys() , Msg_exit() , Msg__exit() , and Msg() ) write into a single log file.

The functions sys() , msg() , Sys() , and Msg() save on entry the current signal mask, block all signals, and than write the message - hence they cannot be " interrupted ". On return, they reinstate the original signal mask.

The functions sys_exit() , sys__exit() , msg_exit() , msg__exit() , Sys_exit() , Sys__exit() , Msg_exit() , and Msg__exit() block all signals on entry. They do not save the original signal mask because they do not return; these functions exit using the system call exit(1) (functions sys_exit() , msg_exit() , Sys_exit() , and Msg_exit() )or _exit(1) (functions sys__exit() , msg__exit() , Sys__exit() , and Msg__exit() ).

Functions msg_exit() , msg__exit() , msg() , Msg_exit() , Msg__exit() , and Msg() just write the requested message and either return ( msg() and Msg() ) or exit using exit(1) ( msg_exit() and Msg_exit() )or _exit(1) ( msg__exit() and Msg__exit() ).

Functions sys_exit() , sys__exit() , sys() , Sys_exit() , Sys__exit() , and Sys() write a more elaborate system error message (together with the requested message) describing the error that happened based on errno . Thus they should be used only after a system call failed. These functions either return ( sys() and Sys() ) or exit using exit(1) ( sys_exit() and Sys_exit() )or _exit(1) ( sys_exit() and Sys__exit() ).

Functions Sys_exit() , Sys__exit() , Sys() , Msg_exit() , Msg__exit() , and Msg() on entry lock the log file for writing (an exclusive lock); upon exit or return, they unlock the log file (using functions writelock() and fileunlock() ). The file locking protects the integrity of the file if different processes try to write into it. If compiled with the _multithread flag, the POSIX mutexes (a mutual exclusion implementation for POSIX threads, a kind of a lock) are installed and used to protect the log.

All the functions use an integer pointer logi (declared in the log.h file), which must point to a location where the next sequential message number is stored. Each function uses that number for its message number and then increments its value by 1 so that the next function can use it. The location could be within the dataspace of the process (as we used it in several examples): first define and initialize the real index, int logindex=0 , and then define logi and point it there - int* logi = &logindex . Or it could be a shared memory segment. If the logging functions are used for many processes for the same log then the message numbering will be out of sequence, for each process will keep track of the message numbering for itself only. If we want a correct numbering for all processes together then they must all use the same index, so we keep it in a shared memory segment. This would be a typical and intended use:

 #include "log.h" ... create_log("mylog"); int logindex=0; int* logi=&logindex; ... 1. register atexit a cleanup function for a removal of a shared    memory segment for logi, provided you are the parent process 2. create a shared memory segment, attach to it, its address is    in logaddr ... logi = loagddr; 3. fork all child processes    they inherit both the shared memory segment and logaddr    so the logging functions will be OK 

Access to the shared memory segment need not be protected (since only the "owner" of the log will access it), so the mutual exclusion is enforced via file locking. For the multithreaded version:

 #include "log.h"    ...    create_log("mylog");    int logindex=0;    int* logi=&logindex;    ...    1. create all threads       they inherit logi       so the logging functions will be OK 

There is likewise no need to safeguard access to logindex since only the "owner" of the log will access it; here the mutual exclusion is enforced using the mutual exclusion of the log.

All reporting and logging functions return a char pointer that consists of a string with a possible error message. If a function executes without an error, a null pointer is returned (meaning no error message). If the pointer returned is not null then the string it points to contains an error message about what has happened. It is up to the programmer who is using these functions to decide what to do if an error has occurred.

The program using the logging functions must #include log.h and must define and initialize the mutex tloc as follows :

 pthread_mutex_t tlock = PTHREAD_MUTEX_INITIALIZER 

Here is log.h file:

 #ifndef _log_h    #define _log_h    extern int *logi;    #ifdef _multithread     extern pthread_mutex_t tlock;    #endif    #ifdef __cplusplus       extern "C"          #include <stdio.h>          #include <string.h>          #include <sys/types.h>          #include <fcntl.h>          #include <errno.h>          #include <stdarg.h>          #include <stdlib.h>          #include <unistd.h>          #include <time.h>          #include <signal.h>          #include <sys/stat.h>          #include <pthread.h>          char* sys_exit(char *fmt, ...);          char* sys__exit(char *fmt, ...);          char* sys(char *fmt, ...);          char* msg_exit(char *fmt, ...);          char* msg__exit(char *fmt, ...);          char* msg(char *fmt, ...);          char* Sys_exit(char *fmt, ...);          char* Sys__exit(char *fmt, ...);          char* Msg_exit(char *fmt, ...);          char* Msg__exit(char *fmt, ...);          char* Sys(char *fmt, ...);          char* Msg(char *fmt, ...);          char* create_log(char*);    #else      #include <stdio.h>      #include <string.h>      #include <sys/types.h>      #include <fcntl.h>      #include <errno.h>      #include <stdarg.h>      #include <stdlib.h>      #include <unistd.h>      #include <time.h>      #include <signal.h>      #include <sys/stat.h>      #include <pthread.h>      extern char* sys_exit(char *fmt, ...);      extern char* sys__exit(char *fmt, ...);      extern char* sys(char *fmt, ...);      extern char* msg_exit(char *fmt, ...);      extern char* msg__exit(char *fmt, ...);      extern char* msg(char *fmt, ...);      extern char* Sys_exit(char *fmt, ...);      extern char* Sys__exit(char *fmt, ...);      extern char* Msg_exit(char *fmt, ...);      extern char* Msg__exit(char *fmt, ...);      extern char* Sys(char *fmt, ...);      extern char* Msg(char *fmt, ...);      extern char* create_log(char*);    #endif    #endif /* _log_h */ 

To view or download the complete code, please visit Prof. Franek's website: www.cas.mcmaster.ca/ ~ franek .



Memory as a Programming Concept in C and C++
Memory as a Programming Concept in C and C++
ISBN: 0521520436
EAN: 2147483647
Year: 2003
Pages: 64

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net