/* * Copyright (c) 2005-2006, FlexiGuided GmbH, Berlin, Germany * Author: Jan Behrens * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the FlexiGuided GmbH nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ /* * File name: example.c * Version: 0.3 * Last changed: 2006-02-21 * * Dependencies: errhdl.h errhdl_pthread.h * [libpthread] [liberrhdl] [liberrhdl_pthread] * * Description: * Example application for demonstrating the usage of the error handling * library. * * WARNING: Using compilers for comiling either the error handling library * or programs using the library, which optimize the generated code, is * dangerous! * USE OPTIMIZED COMPILATION ONLY IF YOU KNOW WHAT YOU ARE DOING! * For disabling optimization for the GNU C compiler search for the -O0 * switch in the manpage of the GNU C compiler. * */ #include #include #include #include #include #include #include #include #include #include int number1, number2; volatile int addition, division; static char *line_input(char *prompt, int maxlen) { ErrHdl; /* * 'ErrHdl' reserves space on the stack for holding an error * message and a pointer to the error class string. * The stack is used for making sure to not overwrite the error * status in functions, calling this function after a 'Try' block. */ char *retval; fprintf(stdout, "%s", prompt); retval = malloc(maxlen+2); if (!retval) MemoryError(); /* * If malloc() returns an error condition, raise (in this case) an * error of class "memory" with a standard error message. */ Try { /* * If an error occures in the block or command following * the 'Try' keyword, executions continues at the block or * command following the 'OnError' keyword. * NOTICE: Deeply nested functions may raise errors too, * but return values of function calls indicating error * conditions DO NOT raise an error in sense of the error * handling library automatically. */ char *lf; if (!fgets(retval, maxlen+2, stdin)) Error("io", "Could not read line from stdin: %s", strerror(errno)); lf = retval+strlen(retval)-1; if (*lf != '\n') Error("io", "Line read from stdin is too long."); *lf = 0; /* * An 'OnError' statement MUST follow here. */ } OnError { /* * This section is executed in case of error only. * If an error occurs in this section itself, it will be * passed to the next outer layer of 'Try'/'OnError'. */ free(retval); Escalate(); /* * If we want to pass the error to the next outer layer of * 'Try'/'OnError', we call 'Escalate' to raise the last * error which occured in THIS function again. The use of * 'Escalate' is not limited to the 'OnError' block. If the * last 'Try' block did not raise an error, the 'Escalate' * command will do nothing. */ } /* * An 'else' statement MAY follow here, which is not the case. */ return retval; } static int str2int(char *str) { int retval; if (sscanf(str, "%i", &retval) != 1) Error("argument", "The given string '%s' is not a valid integer number.", str); return retval; } static int add_numbers(int n1, int n2) { if (n1 > INT_MAX/2 || n2 > INT_MAX/2 || n1 < INT_MIN/2 || n2 < INT_MIN/2) OverflowError(); return n1+n2; } static int divide_numbers(int n1, int n2) { if (!n2) Error("argument", "Cannot devide through zero."); return n1/n2; } static void *add_thread(void *arg) { ErrHdl; ErrHdl_Thread(); /* * If you use multithreading, you have to call 'ErrHdl_Thread' ONCE * in each thread, BEFORE any errors are raised. If you miss to do * so, unpredictible results are the consequence. */ Try addition = add_numbers(number1, number2); OnError { if (!ErrorOfClass("argument")) Escalate(); /* * Only handle errors of class argument, all others are * escalated directly, will in this case not be caught, and * lead to program abortion (and possibly to a memory * dump). */ return (void *)1; } else return NULL; } static void *div_thread(void *arg) { ErrHdl; ErrHdl_Thread(); Try division = divide_numbers(number1, number2); OnError { if (!ErrorOfClass("argument")) Escalate(); return (void *)1; } else return NULL; } int main(int argc, char **argv) { ErrHdl; ErrHdl_Thread(); /* * The main thread has to call 'ErrHdl_Thread' too, if any errors * can be raised, after other threads have been initialized. */ Try { while (1) { pthread_t t1, t2; intptr_t result; char *line; line = line_input("Enter a number: ", 40); Try number1 = str2int(line); OnError; /* * Notice that the semicolon after 'OnError' * indicates that no extra commands are executed in * case of error. * * Execution always reaches this point, if the * 'Try' was reached before. As 'OnError' * (and 'else') are empty here and will therefore * never raise an error, we can do things here, * which should be ensured, regardless of an error * having occured or not. */ free(line); Escalate(); /* * We are freeing the allocated memory in any case, * and raise an error again, in case one occured in * the 'str2int' function. */ line = line_input("Enter another number: ", 40); Try number2 = str2int(line); OnError; free(line); Escalate(); CheckRetval(pthread_create( &t1, NULL, add_thread, NULL)); CheckRetval(pthread_create( &t2, NULL, div_thread, NULL)); CheckRetval(pthread_join(t1, (void **)&result)); if (result) Error("unknown", "add_thread() did not return successfully."); CheckRetval(pthread_join(t2, (void **)&result)); if (result) Error("unknown", "div_thread() did not return successfully."); fprintf(stdout, "Sum = %i\n" "Quotient = %i\n", addition, division); } } OnError { fprintf(stderr, "Program aborted (%s): %s\n", ErrorClass, ErrorMessage); return 1; } return 0; }