Thread parameter in ANSI C makes a segmentation fault

The creation of thread.

void Client_Constructor ( const char* IPAddr ) {
    pthread_t tid; 
    pthread_attr_t rx;  

    /* Create separate memory for client argument */
    struct ThreadArgs *threadArgs;
    if ( ( threadArgs = ( struct ThreadArgs* ) malloc( sizeof( struct ThreadArgs ) ) ) == NULL )
        exit( -1 );
    threadArgs->pos = 111;

    pthread_rwlock_wrlock( &rwlock );
    node = ....;
    if ( node->state == DISCONNECTED ) {  <-node requires a r/w lock.
        pthread_attr_init( &rx );
        pthread_attr_setdetachstate( &rx, PTHREAD_CREATE_DETACHED );
        pthread_create( &tid, &rx, THREAD_BODY, ( void* )threadArgs );
    }
    pthread_rwlock_unlock( &rwlock );
}

Thread body

static void* THREAD_BODY ( void* v ) {
    int sockfd;
    char IPStr[ 16 ];
    int index = ( ( struct ThreadArgs* ) v )->pos; free( v ); <-segmentation fault here
.....
}

These code is so strange, when I run the program more than few hours, then a segmentation fault comes. I also dump the log and use gdb to analyze it, the information is shown below

Program terminated with signal 6, Aborted.
#0  0xb7de1a16 in ?? () from /lib/libc.so.6
(gdb) where
#0  0xb7de1a16 in ?? () from /lib/libc.so.6
#1  0xb7de3318 in ?? () from /lib/libc.so.6
#2  0xb7e1cfdd in fputws () from /lib/libc.so.6
#3  0xb7e1d114 in ?? () from /lib/libc.so.6
#4  0xb7e287ba in realloc () from /lib/libc.so.6
#5  0xb7e28040 in ?? () from /lib/libc.so.6
#6  0xb7e28339 in ?? () from /lib/libc.so.6
#7  0xb7e2476b in ?? () from /lib/libc.so.6
#8  0x0805e945 in THREAD_BODY (v=0x1838b9b0) at Client.c:167
#9  0xb7efffc0 in pthread_create@@GLIBC_2.1 () from /lib/libpthread.so.0
#10 0xb7e8497e in ?? () from /lib/libc.so.6

It anyone give me some suggestions? Is it related to pthread_rwlock?

Are you working on Linux? If so, can you run your program under valgrind supervision?

1 Like

Thanks. I try it now. However, I can run the program under the text mode only since our system doesn't have a GUI. Is valgrind able to work in text mode?

Try fprintf(stderr, "%p\n", v); to see if the pointer value has become corrupted somehow.

If all you're passing is one integer, why allocate memory at all?

void *value=(void *)42;
int ivalue=(int)(value);

If that's not all you're doing, post a more complete portion of your code so we can see what's actually going on.

1 Like

Thanks, I will use fprintf to catch the pointer value.

Before I post the problem on the forum, I tried to use

 void *value=(void *)42;
  int ivalue=(int)(value);
 

But the problem still happened. At that time, I wrote these codes in another way; it is the reason why I use struct to pass parameters.

I have another idea, is it the problem cause by different compile options?

 gcc -O3 *.c
 
  gcc *.c
  

However, I have 10-20 source files in the project, it is difficult to put all codes in this forum.

Besides, I used outl(...), iopl(...) to control SPI. Are they also affect this threading?

Then the problem has nothing to do with that line.

-O3 by itself doesn't cause it to crash. However it can expose latent bugs in your program.

This is an extremely poor design and I can't guarantee anything about it. Given the way modern systems work I can't even guarantee you're actually talking to I/O ports. Is it possible to use an actual device driver instead?

So that means using -O3 in multi-threading is a bad idea?

No, I didn't say that. -O3 may tend to expose latent bugs in your program, multithreaded or not.

Yes, valgrind work in text mode. Which processor architecture are you using?

Could you run the following command under gdb:

thread apply all bt

Besides that, I am a bit surprised by the "signal 6". You mentioned SIGSEGV, and I believe that would be always 11 on Linux? Perhaps you are looking at the wrong place...

Cheers, Lo�c

Yes, I may looking at a wrong place... I will try it again now.

But signal = SIGABRT (6), how can I mask this signal within a thread?

Hi,

run it on gdb to print the value(address) of v where you allocate it and where you free it to see if its getting corrupted by some other thread. So far what you have pasted looks okay to me. One suggestion in here would be to initialize v=NULL after you free in case it gets freed twice in the code.

Thanks and Regards,
Gaurav.

As you would do for any other signal in multi-threaded program:

  • mask the signal in all thread using pthread_sigblock()
  • dedicated a thread that wait synchronously on the signal to occur.

See example from: SUSv3 - pthread_sigmask()

By the way, SIGABRT is usually caused by abort(). It could be a good idea to check if your program triggers abort() at some places.

HTH, Lo�c

Finally, I found the problem, it is caused by use of select().
And the error message is "too many open file descriptors"; Besides, I used "netstat -an" to check the network status, I saw a lot of ESTABLISHED connections and can't release.
But I always use a close() after the descriptor completed. Why this problem happens in my program?

I post the source code of client and server side.
Client.c

  int Close ( int fd, const char* filename, int line ) {
      int ret;
      if ( ( ret = close( fd ) ) == -1 ) {
  #ifdef LOGGING_ENABLE
          sprintf( log, "%s(%d) -> close() failed: %s", filename, line, strerror( errno ) );
          Log( log );
  #endif
      }
      return ret;
  }
  
  void Client_CloseConnection ( int sockfd ) {
      Close( sockfd, __FILE__, __LINE__ );
  }
  
static void* CLIENT_THREAD ( void* v ) {
    int sockfd;                                 /* Socket descriptor. */
    int index = *( int* )v; /*free( v );*/          /* Position in the @PeerList. */
    ...

    /* Assume the connection is OK by default. */
    Pthread_rwlock_wrlock( &rwlock, __FILE__, __LINE__ );
    node->state = CONNECTED;
    Pthread_rwlock_unlock( &rwlock, __FILE__, __LINE__ );

    while ( 1 ) {
        /* Wait until messages come. */
        Pthread_mutex_lock( &node->mutex, __FILE__, __LINE__ );
        Pthread_rwlock_rdlock( &rwlock, __FILE__, __LINE__ );
        tmp = Queue4SendData_GetCount( & ( node->Q ) );
        Pthread_rwlock_unlock( &rwlock, __FILE__, __LINE__ );
        while ( tmp == 0 ) {
            Pthread_cond_wait( &node->isEmpty, &node->mutex, __FILE__, __LINE__ );
            Pthread_rwlock_rdlock( &rwlock, __FILE__, __LINE__ );
            tmp = Queue4SendData_GetCount( & ( node->Q ) );
            Pthread_rwlock_unlock( &rwlock, __FILE__, __LINE__ );
            if ( tmp == 0 )
                continue;
            break;
        }

        //Get the message which going to send
        Pthread_rwlock_wrlock( &rwlock, __FILE__, __LINE__ );
        data = Queue4SendData_Pop( & ( node->Q ) );
        Pthread_rwlock_unlock( &rwlock, __FILE__, __LINE__ );

        /* Let Producer continue insert items. */
        Pthread_cond_signal( &node->isFull, __FILE__, __LINE__ );
        Pthread_mutex_unlock( &node->mutex, __FILE__, __LINE__ );

        tmp = 1;        /* Status flag (0 = error, 1 = OK). */
        if ( Client_TestConnection( &sockfd, IPStr ) ) {
            Writen( sockfd, data.send_buf, strlen( data.send_buf ), __FILE__, __LINE__ );
            if ( ( n = Readline( index, sockfd, recvline, TCP_MAXLINE ) ) <= 0 ) {
#ifdef LOGGING_ENABLE
                if ( n == 0 )
                    sprintf( log, "%s(%d) -> Server %s terminated prematurely", __FILE__, __LINE__, IPStr );
                else
                    sprintf( log, "%s(%d) -> Readline() on %s failed: %s", __FILE__, __LINE__, IPStr, strerror( errno ) );
                Log( log );
#endif
                tmp = 0;
            } else {
                handler = data.handler;
                handler( recvline, IPStr );     /* Call packet handler. */
            }
        } else {
            tmp = 0;
        }

        //DON'T FORGET THESE CODES.
        Client_CloseConnection( sockfd ); <- close is called here!!
        if ( tmp == 0 )
            break;
    }
    return NULL;
}

This function used to create a connection with a server; in my project, a client needs to connect few servers at the same time.
void Client_Constructor ( const char* IPAddr ) {
    int status;
    pthread_t tid;      /* Thread ID. */
    pthread_attr_t rx;  /* Parameter of thread. */
    int pos = Client_GetPosition( IPAddr );
    struct PeerInfo* node = &PeerList[ pos ];
    
    Pthread_rwlock_rdlock( &rwlock, __FILE__, __LINE__ );
    status = node->state;
    Pthread_rwlock_unlock( &rwlock, __FILE__, __LINE__ );
    
    if ( status == DISCONNECTED ) {   //Server is offline, then connect it!
        Pthread_rwlock_wrlock( &rwlock, __FILE__, __LINE__ );
        strcpy( node->IPStr, IPAddr ); 
        Queue4SendData_Init( &( node->Q ), SEND_QUEUE );
        node->state = CONNECTING;                       
        Pthread_rwlock_unlock( &rwlock, __FILE__, __LINE__ );
        Pthread_attr_init( &rx, __FILE__, __LINE__ ); 
        Pthread_attr_setdetachstate( &rx, PTHREAD_CREATE_DETACHED, __FILE__, __LINE__ );

        Pthread_rwlock_wrlock( &tbRwlock, __FILE__, __LINE__ );
        if ( threadBufPos >= 1024 )
            threadBufPos = 0;
        threadBuf[ threadBufPos ] = pos;
        Pthread_create( &tid, &rx, CLIENT_THREAD, ( void* )&( threadBuf[ threadBufPos ] ), __FILE__, __LINE__ );
        threadBufPos++;
        Pthread_rwlock_unlock( &tbRwlock, __FILE__, __LINE__ );
    }
}

Send messages to a server, IPAddr is the IP address of server.
void Client_Pend ( const char* IPAddr, struct SendDataStruct *data ) {
    struct SendDataStruct local;
    struct PeerInfo* node = &PeerList[ Client_GetPosition( IPAddr ) ];
    enum TCPState TCPStatus;
    int qSize;
    Pthread_rwlock_rdlock( &rwlock, __FILE__, __LINE__ );
    TCPStatus = node->state;                        /* Recent TCP status of the connection. */
    Pthread_rwlock_unlock( &rwlock, __FILE__, __LINE__ );
    
    if ( TCPStatus == CONNECTED ) {
        /* Blocking if the queue is full. */
        Pthread_mutex_lock( &node->mutex, __FILE__, __LINE__ );
        Pthread_rwlock_rdlock( &rwlock, __FILE__, __LINE__ );
        qSize = Queue4SendData_GetCount( & ( node->Q ) );
        Pthread_rwlock_unlock( &rwlock, __FILE__, __LINE__ );
        while ( SEND_QUEUE <= qSize ) {
            Pthread_cond_wait( &node->isFull, &node->mutex, __FILE__, __LINE__ );
            Pthread_rwlock_rdlock( &rwlock, __FILE__, __LINE__ );
            qSize = Queue4SendData_GetCount( & ( node->Q ) );
            Pthread_rwlock_unlock( &rwlock, __FILE__, __LINE__ );
            if ( SEND_QUEUE <= qSize )
                continue;   /* If the Queue is full, then blocking. */
            break;
        }

        Pthread_rwlock_wrlock( &rwlock, __FILE__, __LINE__ );
        local.handler = data->handler;             /* Clone packet handler. */
        strcpy( local.send_buf, data->send_buf );  /* Clone message to temporary array. */
        if ( Queue4SendData_Insert( & ( node->Q ), local ) == -1 ) {
#ifdef LOGGING_ENABLE
            sprintf( log, "%s(%d) -> Queue is full on client %s", __FILE__, __LINE__, node->IPStr );
            Log( log );
#endif
            exit( -1 );
        } else {
        }
        Pthread_rwlock_unlock( &rwlock, __FILE__, __LINE__ );

        /* Let the consumer read the inserted item. */
        Pthread_cond_signal( &node->isEmpty, __FILE__, __LINE__ );
        Pthread_mutex_unlock( &node->mutex, __FILE__, __LINE__ );
    } else if ( TCPStatus == DISCONNECTED ) {
        Client_Constructor( node->IPStr );
    }
}
  

Server.c

void Server_Constructor () {
    ssize_t n;
    int i, maxi, maxfd, listenfd, connfd, sockfd;
    int nready, client[ FD_SETSIZE ];
    fd_set rset, allset;
    char buf[ TCP_MAXLINE ];
    struct sockaddr_in cliaddr, srvAddr;
    socklen_t clilen;
    TIMER t = 0;

    if ( ( listenfd = Socket( AF_INET, SOCK_STREAM, 0, __FILE__, __LINE__ ) ) == -1 )
        return; /* Fail to create a socket. */
    i = 1;
    Setsockopt( listenfd, SOL_SOCKET, SO_REUSEADDR, &i, sizeof( int ), __FILE__, __LINE__ );    /* Reuse the socket. */

    /* Initiate server's address. */
    bzero( &srvAddr, sizeof( srvAddr ) );
    srvAddr.sin_family      = AF_INET;
    srvAddr.sin_addr.s_addr = htonl( INADDR_ANY );
    srvAddr.sin_port        = htons( TCP_PORT );

    if ( Bind( listenfd, ( struct sockaddr* )&srvAddr, sizeof( srvAddr ), __FILE__, __LINE__ ) == -1 )
        return; /* Fail to bind the socket. */
    if ( Listen( listenfd, TCP_MAX_PENGING, __FILE__, __LINE__ ) == -1 )
        return; /* Fail to listen to the socket. */

    /* Initiate select. */
    maxfd = listenfd;   /* Initialize. */
    maxi  = -1;         /* Index into client[] array. */
    for ( i = 0; i < FD_SETSIZE; client[ i ] = -1, i++ );   /* -1 indicates available entry. */
    FD_ZERO( &allset );
    FD_SET( listenfd, &allset );

    for ( ; ; ) {
        t = time2MS();
        rset = allset;      /* Structure assignment. */
        nready = Select( maxfd + 1, &rset, NULL, NULL, NULL, __FILE__, __LINE__ );
        if ( FD_ISSET( listenfd, &rset ) ) {    /* New client connection. */
            clilen = sizeof( cliaddr );
            if ( ( connfd = Accept( listenfd, ( struct sockaddr* )&cliaddr, &clilen, __FILE__, __LINE__ ) ) == -1 )
                continue;


            for ( i = 0; i < FD_SETSIZE ; i++ )
                if ( client[ i ] < 0 ) {
                    client[ i ] = connfd; /* save descriptor. */
                    break;
                }

            if ( i == FD_SETSIZE ) {
#ifdef LOGGING_ENABLE
                sprintf( buf, "%s(%d) -> Too many clients", __FILE__, __LINE__ );
                Log( buf );
#endif
                return;
            }

            FD_SET( connfd, &allset );              /* Add new descriptor to set. */
            if ( connfd > maxfd ) maxfd = connfd;   /* For select. */
            if ( i > maxi ) maxi = i;               /* Max index in client[] array. */
            if ( --nready <= 0 ) continue;          /* No more readable descriptors. */
        }

        /* Check all clients for data. */
        for ( i = 0; i <= maxi ; i++ ) {
            if ( ( sockfd = client[ i ] ) < 0 )
                continue;
            if ( FD_ISSET( sockfd, &rset ) ) {
                if ( readable_timeo( sockfd, 1 ) <= 0 ) {
                    printf( "UNABLE TO READ %d\n", sockfd );
                    exit( -1 );
                } else if ( ( n = Readline( sockfd, buf, TCP_MAXLINE ) ) <= 0 ) {  /* For connection closed by client. */
                    Close( sockfd, __FILE__, __LINE__ ); <- close is called here!!
                    FD_CLR( sockfd, &allset );
                    client[ i ] = -1;
                } else {
                    PacketHandler( sockfd, buf, n );
                }
                if ( --nready <= 0 )  break;    /* No more readable descriptors. */
            }
        }
    }
}

Errors

Server.c(271) -> accept() failed: Too many open files
...
/Client.c(332) -> close() failed: Bad file descriptor
...

Without digging deeper in your code, the first thoughts that come to my mind:

  • How many simultaneous client connection do you have?
  • What are your ulimits?
$ ulimit -aS  // currently set
$ ulimit -aH  // hard limits

HTH, Lo�c

  1. There are a lot of client connections, normally, 70-80ms may have a new client connection comes.
$ ulimit -aS
time(seconds)        unlimited
file(blocks)         unlimited
data(kb)             unlimited
stack(kb)            8192
coredump(blocks)     0
memory(kb)           unlimited
locked memory(kb)    64
process              4096
nofiles              1024
vmemory(kb)          unlimited
locks                unlimited

$  ulimit -aH
time(seconds)        unlimited
file(blocks)         unlimited
data(kb)             unlimited
stack(kb)            unlimited
coredump(blocks)     0
memory(kb)           unlimited
locked memory(kb)    64
process              4096
nofiles              1024
vmemory(kb)          unlimited
locks                unlimited

---------- Post updated at 10:27 PM ---------- Previous update was at 12:31 PM ----------

Ask for a better suggestion

  • Each PC is a TCP server.
  • Each PC needs to connect few servers.
  • Once a connection is established, I want to keep it and don't want to close it.
  • TCP client only sends requests to server when some events trigger

Q: How to keep a client connection and reuse it? Since I used cond_signal and cond_wait to hold the connection, but the TCP server always returns the client has been closed.

The currently solution like that:

Client.c

socket = socket( ... );
while ( 1 ) {
        /* Wait until messages come. */
        Pthread_mutex_lock( &node->mutex );
        while ( QSize == 0 ) {
            Pthread_cond_wait( &node->isEmpty, &node->mutex );
             ....
            if ( Qsize == 0 )
                continue;
            break;
        }
         data = QPop( ... );
        Pthread_cond_signal( &node->isFull );
        Pthread_mutex_unlock( &node->mutex );

        Writen( sockfd, data, strlen( data ) );
}

A function for push data into the queue.

void Client_Pend ( ... ) {
        /* Blocking if the queue is full. */
        Pthread_mutex_lock( &node->mutex );
        while ( MAX_SIZE <= qSize ) {
            Pthread_cond_wait( &node->isFull, &node->mutex );
            if ( MAX_SIZE <= qSize )
                continue;   /* If the Queue is full, then blocking. */
            break;
        }
        .....
        Insert data into the queue
    
        /* Let the consumer read the inserted item. */
        Pthread_cond_signal( &node->isEmpty );
        Pthread_mutex_unlock( &node->mutex );
}

However, I use select() as TCP server, the pesude code like this:

void Server () {
    ssize_t n;
    int i, maxi, maxfd, listenfd, connfd, sockfd;
    int nready, client[ FD_SETSIZE ];
    fd_set rset, allset;
    char buf[ TCP_MAXLINE ];
    struct sockaddr_in cliaddr, srvAddr;
    socklen_t clilen;

    if ( ( listenfd = Socket( AF_INET, SOCK_STREAM, 0) ) == -1 )
        return; /* Fail to create a socket. */
    i = 1;
    Setsockopt( listenfd, SOL_SOCKET, SO_REUSEADDR, &i, sizeof( int ) );  /* Reuse the socket. */

    /* Initiate server's address. */
    bzero( &srvAddr, sizeof( srvAddr ) );
    srvAddr.sin_family      = AF_INET;
    srvAddr.sin_addr.s_addr = htonl( INADDR_ANY );
    srvAddr.sin_port        = htons( TCP_PORT );

    if ( Bind( listenfd, ( struct sockaddr* )&srvAddr, sizeof( srvAddr ) ) == -1 )
        return; /* Fail to bind the socket. */
    if ( Listen( listenfd, TCP_MAX_PENGING ) == -1 )
        return; /* Fail to listen to the socket. */

    /* Initiate select. */
    maxfd = listenfd;   /* Initialize. */
    maxi  = -1;         /* Index into client[] array. */
    for ( i = 0; i < FD_SETSIZE; client[ i ] = -1, i++ );   /* -1 indicates available entry. */
    FD_ZERO( &allset );
    FD_SET( listenfd, &allset );

    for ( ; ; ) {
        rset = allset;      /* Structure assignment. */
        nready = Select( maxfd + 1, &rset, NULL, NULL, NULL );
        if ( FD_ISSET( listenfd, &rset ) ) {    /* New client connection. */
            clilen = sizeof( cliaddr );
            if ( ( connfd = Accept( listenfd, ( struct sockaddr* )&cliaddr, &clilen ) ) == -1 )
                continue;

            for ( i = 0; i < FD_SETSIZE ; i++ )
                if ( client[ i ] < 0 ) {
                    client[ i ] = connfd; /* save descriptor. */
                    break;
                }

            if ( i == FD_SETSIZE ) 
                return;

            FD_SET( connfd, &allset );              /* Add new descriptor to set. */
            if ( connfd > maxfd ) maxfd = connfd;   /* For select. */
            if ( i > maxi ) maxi = i;               /* Max index in client[] array. */
            if ( --nready <= 0 ) continue;          /* No more readable descriptors. */
        }

        /* Check all clients for data. */
        for ( i = 0; i <= maxi ; i++ ) {
            if ( ( sockfd = client[ i ] ) < 0 )
                continue;
            if ( FD_ISSET( sockfd, &rset ) ) {
                if ( readable_timeo( sockfd, 1 ) <= 0 ) {
                    sprintf( returnBuf, "%s(%d) -> Unable read!");
                } else if ( ( n = Readline( sockfd, buf, TCP_MAXLINE ) ) <= 0 ) {  /* For connection closed by client. */
                        fprintf( stderr, "[SERVER] Client closed!" ); This statement always true after a connection from client. Is it no data from client since I used cond_wait()?
                    Close( sockfd );
                    FD_CLR( sockfd, &allset );
                    client[ i ] = -1;
                } else {
                    PacketHandler( sockfd, buf, n );
                }
                if ( --nready <= 0 )  break;    /* No more readable descriptors. */
            }
        }
    }
}