C_p678
C_p678

Reputation: 467

C - Mysterious Segmentation Fault with Threading

this is a simple program I've been working on that listens to a socket, and starts a new thread to handle each connection to said socket. In my while loop I get a Segmentation Fault, that has something to do with pthread_create (if I comment that line out the program loops properly). My knowledge of pointers is mediocre at best, and debugging with gdb didn't yield anything of value. This is gdb's output:

#0  0x0000000000000000 in ?? ()
#1  0x000000080064f4f1 in pthread_getprio () from /lib/libthr.so.3
#2  0x0000000000000000 in ?? ()
Error accessing memory address 0x7fffffbff000: Bad address.

The program gets through the while loop once successfully, and properly receives and responds to a connection at the socket, but then before getting into the second while loop, the program fails on a Segmentation Fault error.

Here's a condensed version of my program:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/un.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <pthread.h>

#define UNIX_PATH_MAX 100
#define SOCK_PATH "/tmp/demo_socket"

/*===============> CONNECTION HANDLER FUNCTION <=================*/
void *connection_handler(int connection_fd)
{
    int nbytes;
    char buffer[256];

nbytes = read(connection_fd, buffer, 256); 
buffer[nbytes] = 0;
printf("\tMESSAGE FROM CLIENT: %s\n", buffer);

nbytes = snprintf(buffer, 256, "Hello from the server!");
write(connection_fd, buffer, nbytes);

close(connection_fd);

return;
}


/*==========================> MAIN <=============================*/
int main(void)
{
    struct sockaddr_un addr; //socket address information
    int sock_fd, conn_fd; //socket file descriptors
    socklen_t addr_len = sizeof(struct sockaddr_un); //size of sockaddr_un structure
    pid_t child_pid; //pid holder
    pthread_t thread; // thread identifier 


sock_fd = socket(AF_UNIX, SOCK_STREAM, 0); 
if (sock_fd < 0)
    return 1;
unlink(SOCK_PATH);



memset(&addr, 0, addr_len); 

addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, SOCK_PATH, sizeof(addr.sun_path) - 1); // Copies up to sizeof(addr.sun_path)-1 bytes from SOCK_PATH into addr.sun_path 
printf("> Socket sun_family = %d (AF_UNIX), Socket sun_path = %s ...\n", addr.sun_family, addr.sun_path);


/*----------------------FAIL CHECKS-------------------------*/
if (bind(sock_fd, (struct sockaddr *) &addr, addr_len) != 0) 
    return 1;
if (listen(sock_fd, 5) != 0)
    return 1;

printf("> Listening to socket bound at %s ...\n\n", SOCK_PATH);


/*--------------------WHILE LOOP----------------------------*/
while ( (conn_fd = accept(sock_fd, (struct sockaddr *) &addr, &addr_len)) > -1) {

    pthread_create(&thread , NULL, connection_handler(conn_fd), NULL); 

    printf("> Closing connection at %d inside server process ...\n", conn_fd);
    close(conn_fd);
    printf("> Reached bottom of loop!\n");
    }


/*---------------------------FIN------------------------------*/
close(sock_fd);
unlink(SOCK_PATH);
printf("> Socket closed and unlinked from path ... Done!\n ");
return 0;
}

Any help would be greatly appreciated!

Upvotes: 1

Views: 3267

Answers (4)

NPE
NPE

Reputation: 500357

This is wrong:

pthread_create(&thread , NULL, connection_handler(conn_fd), NULL);

pthread_create requires the address of the function to run in the new thread. What your code does is call connection_handler in the main thread and then pass the result of connection_handler to pthread_create as the function address.

What you need is the following:

pthread_create(&thread , NULL, connection_handler, (void*)conn_fd);

You'll also need to change connection_handler to take void* instead of int:

void *connection_handler(void* arg)
{
  intptr_t connection_fd = (intptr_t)arg;
  ...
}

Upvotes: 3

Hasturkun
Hasturkun

Reputation: 36402

Your usage of pthread_create is incorrect. the third argument should be a pointer to a function of type void *(*start_routine) (void *), instead you are passing the return of connection_handler.

Change connection_handler to receive a void * argument (and make sure it returns an actual value), eg.

#include <stdint.h>

void *connection_handler(void *arg)
{
    intptr_t connection_fd = (intptr_t)arg;
    ...
    return NULL;
}

and change your call to something like the following

pthread_create(&thread, NULL, &connection_handler, (void *)conn_fd);

You should also make sure to either start the thread detached, detach the thread with pthread_detach or join it later with pthread_join

Upvotes: 1

bluefalcon
bluefalcon

Reputation: 4245

Most likely because you are closing the socket connection in two places. There is good chance that in one run of the thread its not yet got around to the write but in your parent thread already closed the connection.

Why do you need to create so many threads? Wouldn't one worker thread suffice? you can pile up the jobs on to this worker thread...

Upvotes: 0

Karoly Horvath
Karoly Horvath

Reputation: 96266

buffer[nbytes] = 0;

This will overflow if you've read 256 bytes. Increase buffer size or decrease read size by one.

Upvotes: 0

Related Questions