Subject: slow accept() in multithreaded code
To: None <netbsd-help@netbsd.org>
From: None <sigsegv@rambler.ru>
List: netbsd-help
Date: 08/05/2004 03:57:07
This is a multi-part message in MIME format.
--------------000702070409040908010405
Content-Type: text/plain; charset=us-ascii; format=flowed
Content-Transfer-Encoding: 7bit

Greetings, I am not sure if this question is appropriate on this list, 
but I thought somebody might know the answer. I wrote a simple TCP/IP 
server, which listens for client requests and then prints out a text 
message from the client. The server uses Posix threads to service 
requests from clients concurrently. I start a client, which creates many 
concurrent connections to the server and for each connection it sends a 
text message. The server pre threads a certain number of threads when it 
starts, which then process client requests, the problem is, after N 
requests have been processed, there is a 3 second wait, and then the 
next N requests are processed, then there is another 3 second wait, etc. 
To me it seems that accept() blocks for longer than it should. I did 
tests and found out that the number of requests processed before a 3 
second wait is proportional to backlog number, as in when server calls 
listen(int sockfd, int backlog). A similar non threaded server (the one 
that simply forks new children instead of creating threads) behaves much 
better, even for small backlog numbers. I looked in my code to see where 
the error could be, but I just can't see it.

I am writing and testing this code on NetBSD-2.0F, if anyone has any 
hints, I would really appreciate your help. If you're interested I've 
attached the sources for the server.

--------------000702070409040908010405
Content-Type: text/plain;
 name="tcpserv_thr.c"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="tcpserv_thr.c"

/*
** Multi-threaded implementation of a simple server
*/

#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <pthread.h>

#define SERV_PORT 8000
#define BACKLOGNUM 2
#define NUM_THREADS 64 

void *serv_req(void *);

/* Structure for thread synchronization */
struct {
	pthread_mutex_t mutex;
	int listenfd;
} tsync;

int main(int argc, char *argv[]) {
	struct sockaddr_in servaddr;
	int pthread_retval, i;
	pthread_t tid[NUM_THREADS];
	int tnum[NUM_THREADS]; /* thread number */
	
	/* stdio is unbuffed */
	setbuf(stdout, NULL);

	pthread_retval = pthread_mutex_init(&(tsync.mutex), NULL);
	if(pthread_retval != 0) {
		fprintf(stderr, "pthread_mutex_init() error: errno %d, current line %d, file %s\n", pthread_retval, __LINE__, __FILE__);
		exit(-1);
	}
	
	if((tsync.listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		fprintf(stderr, "socket() error: errno %d, current line %d, file %s\n", errno, __LINE__, __FILE__);
		exit(-1);
	}
	memset(&servaddr, 0, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	/* servaddr.sin_addr.s_addr = htonl(INADDR_ANY); */
	inet_pton(AF_INET, "10.0.0.16", &servaddr.sin_addr); 
	servaddr.sin_port = htons(SERV_PORT);
	
	if(bind(tsync.listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
		fprintf(stderr, "bind() error: errno %d, current line %d, file %s\n", errno, __LINE__, __FILE__);
		exit(-1);
	}
	
	if(listen(tsync.listenfd, BACKLOGNUM) < 0) {
		fprintf(stderr, "listen() error: errno %d, current line %d, file %s\n", errno, __LINE__, __FILE__);
		exit(-1);
	}
	
	printf("...Creating %d threads\n", NUM_THREADS);
	for(i=0; i<NUM_THREADS; i++) {
		tnum[i] = i;
		pthread_retval = pthread_create(&tid[i], NULL, serv_req, &tnum[i]);
		if(pthread_retval != 0) {
			fprintf(stderr, "pthread_create() error: errno %d, current line %d, file %s\n", pthread_retval, __LINE__, __FILE__);
			exit(-1);
		}
	}

	printf("...Done\n");
	for(i=0; i<NUM_THREADS; i++)
		pthread_join(tid[i], NULL);
	pthread_exit(NULL);
}

/*
** Function to process client requests
*/
void *serv_req(void *arg) {
	ssize_t nread;
	char buf[64];
	char peer_addrstr[INET6_ADDRSTRLEN];
	struct sockaddr_in cliaddr;
	int connfd, clilen;
	int pthread_retval;

	while(1) {
		clilen = sizeof(cliaddr);
		
		/* Lock the mutex, so that only one thread calls accept() at a time */
		pthread_retval = pthread_mutex_lock(&(tsync.mutex));
		if(pthread_retval != 0) {
			fprintf(stderr, "pthread_mutex_lock() error: errno %d, current line %d, file %s\n", pthread_retval, __LINE__, __FILE__);
			exit(-1);
		}
		
		if((connfd = accept(tsync.listenfd, (struct sockaddr *)&cliaddr, &clilen)) < 0) {
			fprintf(stderr, "accept() error: errno %d, current line %d, file %s\n", errno, __LINE__, __FILE__);
			exit(-1);
		}

		pthread_retval = pthread_mutex_unlock(&(tsync.mutex));
		if(pthread_retval != 0) {
			fprintf(stderr, "pthread_mutex_unlock() error: errno %d, current line %d, file %s\n", pthread_retval, __LINE__, __FILE__);
			exit(-1);
		}

		inet_ntop(AF_INET, &cliaddr.sin_addr, peer_addrstr, sizeof(peer_addrstr));
		printf("Thread %d accepted connection on fd %d from %s, port %d\n", *((int *)arg), connfd, peer_addrstr, ntohs(cliaddr.sin_port)); 
		/* Read what client sent us into buffer and then print it out */
		while((nread = read(connfd, buf, sizeof(buf))) > 0)
			printf("Thread %d received: %s", *((int *)arg), buf);
		if(nread < 0) {
			fprintf(stderr, "Thread %d read() error: errno %d, current line %d, file %s\n", *((int *)arg), errno, __LINE__, __FILE__);
			exit(-1);
		}
		
		close(connfd);
		printf("Thread %d closed socket descriptor\n", *((int *)arg));
	}
}


--------------000702070409040908010405--