/* * Based on the example by Max Vilimpoc see: https://vilimpoc.org/research/raii-in-c/ * * The modified code is part of the "Simplified error handling without goto" blog post on * www.softwareprofessional.nl. * * The example has been modified to avoid the use of 'goto' while keeping the same * Resource-Allocation-Is-Initialization (RAII) principles alive. NOTE that I do not * consider this example best practice for error handling. Please read the blog post for * a better method. Cheers! * * Jack Goossen */ #include #include #include #include #include #include #include #include #include /* Generate errors 25% of the time. */ int randBomb(void) { static int seeded = 0; if (!seeded) { srand(time(NULL)); seeded = 1; } /* Flip a coin. */ return (rand() % 5) / 4; } static char *shmRegion; static sem_t st; int main(int argc, char **argv) { int retVal = 0; int sock = 0; FILE *fp = NULL; int shmId = 0; char *memBuf = NULL; enum { state_generic = 0 , state_malloc , state_file_opened , state_socket_opened , state_shmem_created , state_semaphore_created, state_last } state = state_generic; for ( ; state < state_last; ++state ) { if ( state == state_generic ) { printf("Allocate a memory buffer.\n"); memBuf = (char *) malloc(256 * 1024); if (NULL == memBuf) { fprintf(stderr, "malloc() failed.\n"); retVal = -1; break; } } if ( state == state_malloc ) { /* Fifty-bomb. */ if (randBomb()) { fprintf(stderr, "Random exception after malloc()!\n"); retVal = -1; break; } printf("Open a file.\n"); FILE *fp = fopen("dummy-file.txt", "w"); if (NULL == fp) { fprintf(stderr, "fopen() failed.\n"); retVal = -1; break; } } if ( state == state_file_opened ) { if (randBomb()) { fprintf(stderr, "Random exception after fopen()!\n"); retVal = -1; break; } printf("Open a socket.\n"); sock = socket(AF_INET, SOCK_DGRAM, 0); if (-1 == sock) { fprintf(stderr, "socket() failed.\n"); retVal = -1; break; } } if ( state == state_socket_opened ) { if (randBomb()) { fprintf(stderr, "Random exception after socket()!\n"); retVal = -1; break; } printf("Create a shared-memory region.\n"); shmId = shmget(0x1234ABCD, 256*1024, IPC_CREAT); if (-1 == shmId) { fprintf(stderr, "shmget() failed!\n"); retVal = -1; break; } shmRegion = (char *) shmat(shmId, NULL, 0); if ((char *) -1 == shmRegion) { fprintf(stderr, "shmat() failed.\n"); retVal = -1; break; } } if ( state == state_shmem_created ) { if (randBomb()) { fprintf(stderr, "Random exception after shmat()!\n"); retVal = -1; break; } printf("Create/grab a semaphore.\n"); /* semaphore's only available to this process and children. */ if (-1 == sem_init(&st, 0, 1)) { fprintf(stderr, "sem_init() failed.\n"); retVal = -1; break; } } if ( state == state_semaphore_created ) { if (randBomb()) { fprintf(stderr, "Random exception after sem_init()!\n"); retVal = -1; break; } } } printf("--> All Resources Allocated!\n"); printf("--> Perform some normal operations, sing a song, take a dump.\n"); /* Here's the normal control flow return. */ for ( ; state > 0; --state) { switch( state ) { case state_semaphore_created : sem_destroy(&st); printf("Semaphore destroyed!\n"); break; case state_shmem_created : shmctl(shmId, IPC_RMID, NULL); printf("Shared memory region destroyed!\n"); break; case state_socket_opened : close(sock); printf("Socket closed!\n"); break; case state_file_opened : fclose(fp); printf("File handle closed!\n"); break; case state_malloc : free(memBuf); printf("Memory buffer freed!\n"); default : break; } } printf("retVal: %d\n", retVal); return retVal; }