~aritra1911/mc_server

eeecda00df1c5b0092a4344191f5c08ec42ddcc3 — Aritra Sarkar 1 year, 11 months ago d753acf
Don't rely on `poll()`'s `EINTR` for exit
2 files changed, 84 insertions(+), 69 deletions(-)

M Makefile
M main.c
M Makefile => Makefile +5 -4
@@ 7,11 7,11 @@ CFLAGS				=	-std=iso9899:1999 -pedantic -pedantic-errors -Wall -Wextra \
					-Wno-unknown-warning-option -Wno-reserved-id-macro \
					-Wno-reserved-identifier -Wno-disabled-macro-expansion \
					-Wno-missing-prototypes -Wno-thread-safety-analysis \
					-Wno-return-type -Wno-padded -Wno-cast-align -Wno-comma \
					-Wno-padded -Wno-cast-align -Wno-comma \
					-g -O0 -fno-fast-math -fno-builtin

CFLAGS_x86_64-linux-gcc		=	-m64
CFLAGS_i686-linux-gcc		=	-m32
CFLAGS_x86_64-linux-gcc		=	-Wno-return-type -m64
CFLAGS_i686-linux-gcc		=	-Wno-return-type -m32
CFLAGS_x86_64-linux-clang	=	-Weverything -m64
CFLAGS_i686-linux-clang		=	-Weverything -m32
CFLAGS_amd64-freebsd-clang	=	-Weverything -m64


@@ 22,7 22,8 @@ CPPFLAGS			=	-I/usr/local/include -I/usr/pkg/include \
					-D_XOPEN_SOURCE=600 -D_POSIX_PTHREAD_SEMANTICS \
					-D_LARGEFILE64_SOURCE

LDFLAGS				=	-L/usr/local/lib -L/usr/pkg/lib -pthread
LDFLAGS				=	-L/usr/local/lib -L/usr/pkg/lib -pthread \
					-Wl,-R/usr/local/lib -Wl,-R/usr/pkg/lib

server_SRCS			=	main.c session.c response.c tpool.c events.c helpers.c


M main.c => main.c +79 -65
@@ 47,7 47,12 @@ typedef struct {
    char    port[8];
} prefs_t;

static void signal_handler(int);
/* XXX: I hate global variables */
static session_t    session;
static tpool_t      tpool;

static void signal_handler(int) __attribute__((noreturn));
static int  cleanup(session_t *, tpool_t *);
static int  get_listener(prefs_t *);
static void set_defaults(prefs_t *);
static void print_usage(const char *program);


@@ 56,25 61,64 @@ static int  parse_prefs(int argc, char **argv, prefs_t *);
static void
signal_handler(int signum)
{
    printf("\nINFO : Received signal %i.\n", signum);
    printf("\n"
           "INFO : Received signal %i.\n"
           "     : Attempting a graceful shutdown...\n", signum);
    exit(cleanup(&session, &tpool));
}

static int
get_listener(prefs_t *prefs)
cleanup(session_t *sessionp, tpool_t *tpoolp)
{
    /* 
     * In order to set up a socket connection, we invoke the following functions
     * in this order:
     * 1. getaddrinfo()
     * 2. socket()
     * 3. bind()
     */
    int     exit_code=EXIT_SUCCESS;
    char    err_str[STRERROR_BUFLEN];

    if ( pthread_rwlock_unlock(&sessionp->lock) ) {
        get_err_str(errno, err_str);
        fprintf(stderr, " ERR : %s:%i => `pthread_rwlock_unlock()`: %s\n",
                __FILE__, __LINE__ - 3, err_str);
        exit_code = EXIT_FAILURE;
    }

    printf("INFO : Destroying thread pool...\n");
    if ( tpool_destroy(tpoolp, 1) == -1 ) {
        fprintf(stderr, " ERR : %s:%i => `tpool_destroy()`: Failed to destroy"
                              " thread pool!\n"
                        "     : Watch out for those memory leaks!\n",
                __FILE__, __LINE__ - 4);
        exit_code = EXIT_FAILURE;
    }

    printf("INFO : Destroying session...\n");
    if ( session_destroy(sessionp) == -1 ) {
        fprintf(stderr, " ERR : %s:%i => `session_destroy()`: Failed to"
                              " destroy session!\n"
                        "     : Watch out for those memory leaks!\n",
                __FILE__, __LINE__ - 4);
        exit_code = EXIT_FAILURE;
    }

    return exit_code;
}

static int
get_listener(prefs_t *prefs)
{
    struct  addrinfo hints, *res, *p;
    int     status, listener=-1, yes;
    char    err_str[STRERROR_BUFLEN];

    /*
     * In order to set up a socket connection, we invoke the following
     * functions in this order:
     *
     *     1. getaddrinfo()
     *     2. socket()
     *     3. bind()
     *
     */

    /*
     * `memset()` `hints` to zeroes, and set a few necessary fields.
     * This way `getaddrinfo()` will be able to set the other necessary
     * fields for us.


@@ 93,10 137,13 @@ get_listener(prefs_t *prefs)
        return -1;  /* We'd like to exit */
    }

    /* res should now point to a linked list of addresses. In case it's not: */
    /*
     * `res` should now point to a linked list of addresses. In case
     * it's not:
     */
    if ( !res ) {
        fprintf(stderr, " ERR : %s:%i => `getaddrinfo()` returned an empty "
                                        "list\n", __FILE__, __LINE__ - 10);
        fprintf(stderr, " ERR : %s:%i => `getaddrinfo()` returned an empty"
                                       " list\n", __FILE__, __LINE__ - 10);
        return -1;
    }



@@ 113,8 160,10 @@ get_listener(prefs_t *prefs)
        /* Try to get a socket file descriptor */
        if ( (listener = socket(p->ai_family, p->ai_socktype,
                                              p->ai_protocol)) == -1 )
            /* We'll continue, perhaps the next address in the list will
             * succeed */
            /*
             * We'll continue, perhaps the next address in the list
             * will succeed.
             */
            continue;

        /*


@@ 261,12 310,10 @@ print_usage_ret_error:
int
main(int argc, char **argv)
{
    int         listener, args_parsed, num_events, exit_code;
    int         listener, args_parsed, num_events;
    char        err_str[STRERROR_BUFLEN];
    struct      sigaction sa;
    prefs_t     prefs;
    session_t   session;
    tpool_t     tpool;

    /* Set up signal handler for ^C (SIGINT) TODO: and ^\ (SIGQUIT) */
    memset(&sa, 0, sizeof(struct sigaction));


@@ 311,17 358,12 @@ main(int argc, char **argv)
                             MAX_QUEUE_SZ, 0) ) {
        fprintf(stderr, " ERR : %s:%i => `tpool_init()`: Failed to initialize"
                              " thread pool!\n", __FILE__, __LINE__ - 3);
        exit_code = EXIT_FAILURE;
        goto destroy_session_and_exit;
        goto destroy_session_and_exit_fail;
    }

    printf("INFO : Initialized thread pool with %zu worker threads.\n",
                   tpool.num_threads);

    printf("INFO : Listening for connections on port %s\n", prefs.port);

    exit_code = EXIT_SUCCESS;   /* Let's hope for the best */

    /*
     * Now we gotta keep `poll()`ing and take care of everything
     * everytime `poll()` returns until the server receives a SIGINT.


@@ 331,27 373,18 @@ main(int argc, char **argv)
        get_err_str(errno, err_str);
        fprintf(stderr, " ERR : %s:%i => `pthread_rwlock_rdlock()`: %s\n",
                __FILE__, __LINE__ - 3, err_str);
        exit_code = EXIT_FAILURE;
        goto destroy_tpool_and_exit;
        goto destroy_tpool_session_and_exit_fail;
    }

    printf("INFO : Listening for connections on port %s\n", prefs.port);

    while ( 1 ) {
        if ( (num_events = poll(session.pfds,
                                (nfds_t) session.fd_count, -1)) == -1 ) {
            if ( errno == EINTR ) {
                /*
                 * We got Ctrl-C'ed, we need to gracefully release any
                 * allocated memory in heap and then exit.
                 */
                printf("DBUG : `poll()` interrupted!\n");
                break;  /* bail out */
            }

            get_err_str(errno, err_str);
            fprintf(stderr, " ERR : %s:%i => `poll()`: %s\n",
                    __FILE__, __LINE__ - 11, err_str);
            exit_code = EXIT_FAILURE;
            goto destroy_tpool_and_exit;
            break;  /* bail out */
        }

        printf("DBUG : Handling events...\n");


@@ 363,47 396,28 @@ main(int argc, char **argv)
            printf(" ERR : %s:%i => `process_events()`:"
                                  " Failed to handle events!\n",
                   __FILE__, __LINE__ - 3);
            exit_code = EXIT_FAILURE;
            goto destroy_tpool_and_exit;
            break;  /* bail out */
        }
        printf("DBUG : All events handled!\n");
    }

    if ( pthread_rwlock_unlock(&session.lock) ) {
        get_err_str(errno, err_str);
        fprintf(stderr, " ERR : %s:%i => `pthread_rwlock_unlock()`: %s\n",
                __FILE__, __LINE__ - 3, err_str);
        exit_code = EXIT_FAILURE;
        goto destroy_tpool_and_exit;
    }

    /*
     * ^C breaks `poll()` with EINTR which then we use to break the
     * outer infinite `while(1)` loop and hence we can finally reach
     * this code, do the necessary cleanup and exit gracefully.
     */
    cleanup(&session, &tpool);
    goto exit_fail;

    printf("INFO : Gracefully shutting down...\n");

destroy_tpool_and_exit:
    printf("INFO : Destroying thread pool...\n");
    if ( tpool_destroy(&tpool, 1) == -1 ) {
destroy_tpool_session_and_exit_fail:
    if ( tpool_destroy(&tpool, 1) == -1 )
        fprintf(stderr, " ERR : %s:%i => `tpool_destroy()`: Failed to destroy"
                              " thread pool!\n"
                        "     : Watch out for those memory leaks!\n",
                __FILE__, __LINE__ - 4);
        exit_code = EXIT_FAILURE;
    }

destroy_session_and_exit:
    printf("INFO : Destroying session...\n");
    if ( session_destroy(&session) == -1 ) {
destroy_session_and_exit_fail:
    if ( session_destroy(&session) == -1 )
        fprintf(stderr, " ERR : %s:%i => `session_destroy()`: Failed to"
                              " destroy session!\n"
                        "     : Watch out for those memory leaks!\n",
                __FILE__, __LINE__ - 4);
        exit_code = EXIT_FAILURE;
    }

    return exit_code;
exit_fail:
    return EXIT_FAILURE;
}