Search code examples
cpthreadspthread-join

Why is pthread_join() never called?


I am quite inexperienced with multithreading in C, so I would really appreciate some input on this piece of client-side code (extraneous parts have been stripped out for simplicity's sake).

// client_read(): read from server
void *client_read(int sockfd) {
    char text[MAX_TEXT_SIZE + 2];
    while (1) {
        // clean out buffer
        bzero(text, sizeof(text));
        // read from server
        if (read(sockfd, text, sizeof(text)) <= 0) {
            fprintf(stderr, "failed to read\n");
            exit(1);
        }
        // print message from server to stdout
        fprintf(stdout, "%s", text);
        // quit conversation
        if (strncmp(text, "bye\n", 4) == 0) {
            fprintf(stdout, "end to conversation\n");
            close(sockfd);
            exit(0);
        }
    }
}

// client_write(): write to server
void *client_write(int sockfd) {
    char text[MAX_TEXT_SIZE + 2];
    int c, d, i;
    size_t text_len;
    while (1) {
        // clean out buffer
        bzero(text, sizeof(text));
        i = 0;
        // read from stdin
        while ((c = getchar()) != '\n' && c != EOF && i < MAX_TEXT_SIZE) {
            text[i++] = c;
        }
        // clean out stdin if MAX_TEXT_SIZE exceeded
        if (i == MAX_TEXT_SIZE && c != '\n') {
            while ((d = getchar()) != EOF && d != '\n') {
    }
        }
        text[i++] = '\n';
        text[i] = '\0';
        text_len = strlen(text);
        // write to server
        if (write(sockfd, text, text_len) != text_len) {
            fprintf(stderr, "sent wrong number of bytes\n");
            exit(1);
         }
        // quit conversation
        if (strncmp(text, "bye\n", 4) == 0) {
            fprintf(stdout, "end to conversation\n");
            close(sockfd);
            exit(0);
        }
    }
}

int main(int argc, char **argv) {
    char keyword[MAX_KEY_SIZE + 1], *server_IP;
    in_port_t server_port = 6000;
    int r_val, rtn_val, sockfd, w_val;
    pthread_t th_r, th_w;
    struct sockaddr_in server_addr;

    // checking number of command line arguments
    if (argc != 3 && argc != 5) {
        fprintf(stderr, "Usage: ./client server_IP [-p server_port] key\n");
        exit(1);
    }
    // Usage: ./client server_IP key
    else if (argc == 3) {
        if (strlen(argv[2]) > MAX_KEY_SIZE) {
            fprintf(stderr, "maximum key length exceeded\n");
            exit(1);
        }
        else {
            strcpy(keyword, argv[2]);
        }
    }
    // Usage: ./client server_IP -p server_port key
    else {
        if (strcmp("-p", argv[2]) != 0) {
            fprintf(stderr, "client: illegal option %s\n", argv[2]);
            exit(1);
        }
        else {
            server_port = atoi(argv[3]);
        }
        if (strlen(argv[4]) > MAX_KEY_SIZE) {
            fprintf(stderr, "maximum key length exceeded\n");
            exit(1);
        }
        else {
            strcpy(keyword, argv[4]);
        }
    }
    server_IP = argv[1];
    // creating reliable stream socket using TCP
    if ((sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
        fprintf(stderr, "unable to create TCP socket\n");
        exit(1);
    }
    // Constructing server address structure
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    // Converting address into proper format
    if ((rtn_val = inet_pton(AF_INET, server_IP, &server_addr.sin_addr.s_addr)) == 0) {
        fprintf(stderr, "invalid IP address\n");
        exit(1);
    }
    else if (rtn_val < 0) {
        fprintf(stderr, "failed to convert IP string\n");
        exit(1);
    }
    // server port
    server_addr.sin_port = htons(server_port);
    // connecting to server
    if (connect(sockfd, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0) {
        fprintf(stderr, "failed to connect to server\n");
        exit(1);
    }
    // beginning chat
    else {
        fprintf(stdout, "successfully connected to server\n");
        // starting separate thread for reading from server
        if ((r_val = pthread_create(&th_r, NULL, (void *) client_read, (void *) sockfd)) != 0) {
            fprintf(stderr, "failed to create read thread\n");
            exit(1);
        }
        // starting separate thread for writing to server
        if ((w_val = pthread_create(&th_w, NULL, (void *) client_write, (void *) sockfd)) != 0) {
            fprintf(stderr, "failed to create write thread\n");
            exit(1);
        }
        // Waiting until threads close to return resources
        printf("hello1\n");
        pthread_join(th_r, NULL);
        printf("hello2\n");
        pthread_join(th_w, NULL);
        printf("hello3\n");
    }
    return 0;
}

For those of you familiar with client-server programming, you should see that besides the use of POSIX threads there is nothing out of the ordinary about the client code in terms of processing user input and reading and writing to a server. I decided to use a separate thread for reading and writing; otherwise, the client would have to write something, wait for the server to reply and then write something again.

I use the pthread_join() methods at the very end of main(). These functions are supposed to wait until the read (th_r) and write (th_w) threads to finish and relinquish the resources they use. The problem I have is that these methods are apparently never called.

When I start the client, I see it reaches "hello1" right away, but when either the client or server terminates the communication stream, I see neither "hello2" or "hello3," which means neither is ever reached.

Could someone please shed some light on what I am missing?


Solution

  • You aren't supposed to call exit in your threads, you are supposed to return NULL when the thread is finished. Calling exit just terminates the whole program.