Related
So I'm trying to code a multi-threading server. I've spent an enormous time on the internet figuring out the correct way to do this and the answer as always seems to be it depends. Whenever I execute my code, the client successfully connects, and executes but when the thread terminates and returns to the while loop the whole program segfaults.
I probably could use a good spanking on a few other things as well such as my usage of global variables. The entirety of code is below, sorry for the inconsistent space/tabbing.
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdbool.h>
#include <signal.h>
#include <math.h>
#include <pthread.h>
#include <sys/stat.h>
#include <fcntl.h>
/* ---------------------------------------------------------------------
This is a basic whiteboard server. You can query it, append to it and
clear in it. It understands both encrypted and unencrypted data.
--------------------------------------------------------------------- */
struct whiteboard {
int line;
char type;
int bytes;
char string[1024];
} *Server;
int serverSize, threadcount, id[5];
bool debug = true;
struct whiteboard *Server;
pthread_mutex_t mutex;
pthread_t thread[5];
/* -------------------------------------------
function: sigint_handler
Opens a file "whiteboard.all" in writemode
and writes all white board information in
command mode.
------------------------------------------- */
void sigint_handler(int sig)
{
if (debug) printf("\nInduced SIGINT.\n");
FILE *fp;
fp=fopen("whiteboard.all","w");
int x=0;
for (x;x<serverSize;x++) // Loop Responsible for iterating all the whiteboard entries.
{
if (debug) printf("#%d%c%d\n%s\n",Server[x].line,Server[x].type,Server[x].bytes,Server[x].string);
fprintf(fp,"#%d%c%d\n%s\n",Server[x].line,Server[x].type,Server[x].bytes,Server[x].string);
}
if (debug) printf("All values stored.\n");
free(Server); // Free dynamically allocated memory
exit(1);
}
/* -------------------------------------------
function: processMessage
Parses '!' messages into their parts -
returns struct in response.
------------------------------------------- */
struct whiteboard processMessage(char * message)
{
int lineNumber, numBytes;
char stringType, entry[1028];
if (debug) printf("Update Statement!\n");
// Read line sent by Socket
sscanf(message,"%*c%d%c%d\n%[^\n]s",&lineNumber,&stringType,&numBytes,entry);
if (debug) printf("Processed: Line: %d, Text: %s\n",lineNumber,entry);
// Parse information into local Struct
struct whiteboard Server;
Server.line = lineNumber;
Server.type = stringType;
Server.bytes = numBytes;
strcpy(Server.string,entry);
// If there is no bytes, give nothing
if (numBytes == 0)
{
strcpy(Server.string,"");
}
return Server;
}
/* -------------------------------------------
function: handleEverything
Determines type of message recieved and
process and parses accordingly.
------------------------------------------- */
char * handleEverything(char* message, struct whiteboard *Server, char* newMessage)
{
bool updateFlag = false, queryFlag = false;
// If message is an Entry
if (message[0] == '#')
{
if (debug) printf("Triggered Entry!\n");
// Create Temporary Struct
struct whiteboard messageReturn;
messageReturn = processMessage(message);
// Store Temporary Struct in Correct Heap Struct
Server[messageReturn.line] = messageReturn;
sprintf(newMessage,"!%d%c%d\n%s\n",messageReturn.line, messageReturn.type, messageReturn.bytes, messageReturn.string);
return newMessage;
}
// If message is a query
if (message[0] == '?')
{
if (debug) printf("Triggered Query!\n");
int x;
queryFlag = true;
sscanf(message,"%*c%d",&x); // Parse Query
if (x > serverSize) // Check if Query out of Range
{
strcpy(newMessage,"ERROR: Query out of Range.\n");
return newMessage;
}
sprintf(newMessage,"!%d%c%d\n%s\n",Server[x].line,Server[x].type,Server[x].bytes,Server[x].string);
if (debug) printf("newMessage as of handleEverything:%s\n",newMessage);
return newMessage;
}
}
/* -------------------------------------------
function: readFile
If argument -f given, read file
process and parse into heap memory.
------------------------------------------- */
void readFile(char * filename)
{
FILE *fp;
fp=fopen(filename,"r");
int line, bytes, count = 0, totalSize = 0;
char type, check, string[1028], individualLine[1028];
// Loop to determine size of file. **I know this is sloppy.
while (fgets(individualLine, sizeof(individualLine), fp))
{
totalSize++;
}
// Each line shoud have totalSize - 2 (to account for 0)
// (answer) / 2 to account for string line and instruction.
totalSize = (totalSize - 2) / 2;
serverSize = totalSize+1;
if (debug) printf("Total Size is: %d\n",serverSize);
// Open and Allocate Memory
fp=fopen(filename,"r");
if (debug) printf("File Mode Calloc Initialize\n");
Server = calloc(serverSize+2, sizeof(*Server));
// Write to Heap Loop
while (fgets(individualLine, sizeof(individualLine), fp)) {
if (individualLine[0] == '#') // Case of Header Line
{
sscanf(individualLine,"%c%d%c%d",&check,&line,&type,&bytes);
if (debug) printf("Count: %d, Check:%c, Line:%d, Type: %c, Bytes:%d \n",count,check,line,type,bytes);
Server[count].line = line;
Server[count].type = type;
Server[count].bytes = bytes;
count++;
}
else
{
// For case of no data
if (individualLine[0] == '\n')
{
strcpy(string,"");
}
// Then scan data line
sscanf(individualLine,"%[^\n]s",string);
if (debug) printf("String: %s\n",string);
strcpy(Server[count-1].string,string);
}
}
return;
}
void *threadFunction(int snew)
{
char tempmessage[1024], message[2048];
// Compile and Send Server Message
strcpy(tempmessage, "CMPUT379 Whiteboard Server v0\n");
send(snew, tempmessage, sizeof(tempmessage), 0);
// Recieve Message
char n = recv(snew, message, sizeof(message), 0);
pthread_mutex_lock(&mutex);
if (debug) printf("Attempt to Malloc for newMessage\n");
char * newMessage = malloc(1024 * sizeof(char));
if (debug) printf("goto: handleEverything\n");
newMessage = handleEverything(message, Server, newMessage);
if (debug) printf("returnMessage:%s\n",newMessage);
strcpy(message,newMessage);
free(newMessage);
pthread_mutex_unlock(&mutex);
if (debug) printf("message = %s\n", message);
send(snew, message, sizeof(message), 0);
printf("End of threadFunction\n");
return;
}
/* -------------------------------------------
function: main
Function Body of Server
------------------------------------------- */
int main(int argc, char * argv[])
{
int sock, fromlength, outnum, i, socketNumber, snew;
bool cleanMode;
// Initialize Signal Handling
struct sigaction act;
act.sa_handler = sigint_handler;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGINT, &act, 0);
// For correct number of arguments.
if (argc == 4)
{
// If "-n" parameter (cleanMode)
if (strcmp(argv[2], "-n") == 0)
{
// Get size + 1
cleanMode = true;
sscanf(argv[3],"%d",&serverSize);
serverSize += 1;
if (debug) printf("== Clean Mode Properly Initiated == \n");
if (debug) printf("serverSize: %d\n",serverSize);
if (debug) printf("Clean Mode Calloc\n");
Server = calloc(serverSize, sizeof(*Server));
int i = 0;
for (i; i < serverSize; i++) // Initialize allocated Memory
{
Server[i].line = i;
Server[i].type = 'p';
Server[i].bytes = 0;
strcpy(Server[i].string,"");
}
}
// If "-f" parameter (filemode)
else if (strcmp(argv[2], "-f") == 0)
{
// Read File
cleanMode = false;
readFile(argv[3]);
if (debug) printf("== Statefile Mode Properly Initiated == \n");
if (debug) printf("serverSize: %d\n",serverSize);
}
// Otherwise incorrect parameter.
else
{
printf("Incorrect Argument. \n");
printf("Usage: wbs279 pornumber {-n number | -f statefile}\n");
exit(1);
}
sscanf(argv[1],"%d",&socketNumber);
}
// Send Error for Incorrect Number of Arguments
if (argc != 4)
{
printf("Error: Incorrect Number of Input Arguments.\n");
printf("Usage: wbs279 portnumber {-n number | -f statefile}\n");
exit(1);
}
// == Do socket stuff ==
char tempmessage[1024], message[2048];
struct sockaddr_in master, from;
if (debug) printf("Assrt Socket\n");
sock = socket (AF_INET, SOCK_STREAM, 0);
if (sock < 0)
{
perror ("Server: cannot open master socket");
exit (1);
}
master.sin_family = AF_INET;
master.sin_addr.s_addr = INADDR_ANY;
master.sin_port = htons (socketNumber);
if (bind (sock, (struct sockaddr*) &master, sizeof (master)))
{
perror ("Server: cannot bind master socket");
exit (1);
}
// == Done socket stuff ==
listen (sock, 5);
int threadNumber = 0;
while(1)
{
printf("But what about now.\n");
if (debug) printf("-- Wait for Input --\n");
printf("Enie, ");
fromlength = sizeof (from);
printf("Meanie, ");
snew = accept (sock, (struct sockaddr*) & from, & fromlength);
printf("Miney, ");
if (snew < 0)
{
perror ("Server: accept failed");
exit (1);
}
printf("Moe\n");
pthread_create(&thread[threadNumber],NULL,threadFunction(snew), &id[threadNumber]);
//printf("Can I join?!\n");
//pthread_join(thread[0],NULL);
//printf("Joined?!\n");
threadNumber++;
close (snew);
}
}
I'm also curious as to how exactly to let multiple clients use the server at once. Is how I've allocated the whiteboard structure data appropriate for this process?
I'm very sorry if these don't make any sense.
You seem to somehow expect this:
pthread_create(&thread[threadNumber],NULL,threadFunction(snew), &id[threadNumber]);
/* ... */
close (snew);
To make sense, while it clearly doesn't.
Instead of starting a thread that runs threadFunction, passing it snew, you call the thread function and pass the return value to pthread_create(), which will interpret it as a function pointer. This will break, especially considering that the thread function incorrectly ends with:
return;
This shouldn't compile, since it's declared to return void *.
Also assuming you managed to start the thread, passing it snew to use as its socket: then you immediately close that socket, causing any reference to it from the thread to be invalid!
Please note that pthread_create() does not block and wait for the thread to exit, that would be kind of ... pointless. It starts off the new thread to run in parallel with the main thread, so of course you can't yank the carpet away from under it.
This signal handler is completely unsafe:
void sigint_handler(int sig)
{
if (debug) printf("\nInduced SIGINT.\n");
FILE *fp;
fp=fopen("whiteboard.all","w");
int x=0;
for (x;x<serverSize;x++) // Loop Responsible for iterating all the whiteboard entries.
{
if (debug) printf("#%d%c%d\n%s\n",Server[x].line,Server[x].type,Server[x].bytes,Server[x].string);
fprintf(fp,"#%d%c%d\n%s\n",Server[x].line,Server[x].type,Server[x].bytes,Server[x].string);
}
if (debug) printf("All values stored.\n");
free(Server); // Free dynamically allocated memory
exit(1);
}
Per 2.4.3 Signal Actions of the POSIX standard (emphasis added):
The following table defines a set of functions that shall be
async-signal-safe. Therefore, applications can call them, without
restriction, from signal-catching functions. ...
[list of async-signal-safe functions]
Any function not in the above table may be unsafe with respect to signals. Implementations may make other interfaces
async-signal-safe. In the presence of signals, all functions defined
by this volume of POSIX.1-2008 shall behave as defined when called
from or interrupted by a signal-catching function, with the exception
that when a signal interrupts an unsafe function or equivalent
(such as the processing equivalent to exit() performed after a return
from the initial call to main()) and the signal-catching function
calls an unsafe function, the behavior is undefined. Additional
exceptions are specified in the descriptions of individual functions
such as longjmp().
Your signal handler invokes undefined behavior.
Generalities and functioning of my program
NB : you will be able to test my program (only one file, containing the main function). I give you the entire code at the end of this question.
I wrote a program which can be used to illustrate the producer-consumer algorithm, with UNIX-Processes. The producer creates some value, for example 5, writes it into a RAM shared_buffer and then writes the latter into a file test.txt. The consumer assigns to this shared_buffer the content of the file test.txt and takes some value from the RAM buffer, shared_buffer.
I use functions to convert my shared_buffer into the file, and reciprocally : arrayToFile and fileToArray. Both are presented at the end of this question.
The shared_buffer has a size of 1 + 10 cases : the first one contains the number of full cases (ie. : with a 5 writen) and the 10 others can be filled either with 5 or nothing.
The first case is useful for the producer, to know where to write next value (ie. : in which case).
The file of course has also 1 + 10 cases. The file is needed because I use processes and not threads (not-shared memory, thus).
shared_buffer's initialisation is contained in the main function. shared_buffer's accesses (in reading and in writing) are contained in consumer's function and in producer's function, respectively. These codes are presented at the end of this question.
Access to shared_buffer and overall to the file are of course under mutual exclusion and three semaphores are used. The mutexe impedes producer and consumer to access it at the same time, and the two other semaphores are used to guarantee that the producer won't try to put a new element if there isn't enough place, and that the consumer won't try to take an element if there isn't any element. Well, it's just the producer-consumer algorithm.
Finally, producer's process runs until the end of time, and so does the consumer's process.
The declaration and initialisation of these three semaphores are presented at the end of this question.
My problem
There is only one problem : when both producer's process and consumer's process are running until the end of times while(TRUE), arrayToFile and fileToArray tell me that the file's opening failed. If I erase one or both while(TRUE), this error disapears (but thus, my program doesn't make its job).
So this problem appears only when both while(TRUE) are writen.
I think it's because I don't make good use of the mutexe. But I couldn't give you more explanations.
Source
Code is highly commented.
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/sem.h>
#include <stdlib.h>
#define TRUE 1
#define ERR_NBR_ARGS "ERROR. Argument needed. Use as follows.\n"
int fileToArray(char *, int *, int);
int arrayToFile(char *, int *, int);
void check_if_command_arguments_are_correct(int);
void mutual_exclusion_produce(int, char*, int*, int);
void mutual_exclusion_consume(int, char*, int*, int);
int main(int argc, char* argv[]) {
check_if_command_arguments_are_correct(argc);
// FILE'S PATH
char* file_path = argv[1];
// DECLARATION AND INITIALISATION OF THE SHARED RESOURCE
int shared_buffer_number_of_elements = 10;
int shared_buffer[shared_buffer_number_of_elements + 1];
shared_buffer[0] = 0;
arrayToFile(file_path, shared_buffer, shared_buffer_number_of_elements);
// FILE'S KEY (used to make processes able to use the same semaphores)
key_t key = ftok(argv[0], '0');
if(key == - 1) {
perror("ftok");
exit(EXIT_FAILURE);
}
// DECLARATION AND INITIALISATION OF THE THREE SEMAPHORES
int semid = semget(key, 3, IPC_CREAT|IPC_EXCL|0666); // Declaration of the semaphores
if(semid == -1) {
perror("semget");
exit(EXIT_FAILURE);
}
int array_semaphores_values[3];
array_semaphores_values[0] = 1;
array_semaphores_values[1] = shared_buffer_number_of_elements;
array_semaphores_values[2] = 0;
int sem_controller = semctl(semid, 3, SETALL, array_semaphores_values); // Initialisation of the semaphores - 2th parameter is the array's size
if(sem_controller == -1) {
perror("semctl");
exit(EXIT_FAILURE);
}
// THE TWO FORKS - CREATION OF BOTH PRODUCER AND CONSUMER
pid_t producer = fork();
if(producer == -1) {
perror("fork");
exit(EXIT_FAILURE);
} else if(producer == 0) { // The producer process
mutual_exclusion_produce(semid, file_path, shared_buffer, shared_buffer_number_of_elements);
} else { // The main process
pid_t consumer = fork();
if(consumer == - 1) {
perror("fork");
exit(EXIT_FAILURE);
} else if(consumer == 0) { // The consumer process
mutual_exclusion_consume(semid, file_path, shared_buffer, shared_buffer_number_of_elements);
}
}
semctl(semid, 0, IPC_RMID, 0); // The semaphores are freed
}
void mutual_exclusion_produce(int semid, char* file_path, int* buffer, int size) {
/* The producer does the following :
* 1. It decrements the free cases semaphore ;
* 2. It decrements the mutex ;
* 3. It writes the buffer
* 4. It increments the mutex ;
* 5. It increments the full cases semaphore ;
* */
while(TRUE) {
// DECREMENTS FREE CASES SEMAPHORE AND DECREMENTS MUTEX
struct sembuf operation_decrement_free_cases;
operation_decrement_free_cases.sem_num = 2;
operation_decrement_free_cases.sem_op = -1;
operation_decrement_free_cases.sem_flg = 0;
struct sembuf operation_decrement_mutex;
operation_decrement_mutex.sem_num = 0;
operation_decrement_mutex.sem_op = -1;
operation_decrement_mutex.sem_flg = 0;
semop(semid, &operation_decrement_free_cases, 0);
semop(semid, &operation_decrement_mutex, 0);
// WRITES THE BUFFER INTO THE FILE
buffer[++buffer[0]] = 5;
arrayToFile(file_path, buffer, size);
// INCREMENTS THE MUTEX AND INCREMENTS THE FULL CASES SEMAPHORE
struct sembuf operation_increment_full_cases;
operation_decrement_free_cases.sem_num = 1;
operation_decrement_free_cases.sem_op = +1;
operation_decrement_free_cases.sem_flg = 0;
struct sembuf operation_increment_mutex;
operation_decrement_mutex.sem_num = 0;
operation_decrement_mutex.sem_op = +1;
operation_decrement_mutex.sem_flg = 0;
semop(semid, &operation_increment_mutex, 0);
semop(semid, &operation_increment_full_cases, 0);
}
}
void mutual_exclusion_consume(int semid, char* file_path, int* buffer, int size) {
/*
* The consumer does the following :
* 1. It decrements the full cases semaphore ;
* 2. It decrements the mutex ;
* 3. It reads the buffer ;
* 4. It increments the mutex ;
* 5. It increments the free cases semaphore ;
* */
while(TRUE) {
// DECREMENTS FULL CASES SEMAPHORE AND DECREMENTS MUTEX
struct sembuf operation_decrement_full_cases;
operation_decrement_full_cases.sem_num = 1;
operation_decrement_full_cases.sem_op = -1;
operation_decrement_full_cases.sem_flg = 0;
struct sembuf operation_decrement_mutex;
operation_decrement_mutex.sem_num = 0;
operation_decrement_mutex.sem_op = -1;
operation_decrement_mutex.sem_flg = 0;
semop(semid, &operation_decrement_full_cases, 0);
semop(semid, &operation_decrement_mutex, 0);
// READS THE FILE AND PUT THE CONTENTS INTO THE BUFFER
fileToArray(file_path, buffer, size);
// INCREMENTS THE MUTEX AND INCREMENTS THE FREE CASES SEMAPHORE
struct sembuf operation_increment_free_cases;
operation_decrement_full_cases.sem_num = 2;
operation_decrement_full_cases.sem_op = +1;
operation_decrement_full_cases.sem_flg = 0;
struct sembuf operation_increment_mutex;
operation_decrement_mutex.sem_num = 0;
operation_decrement_mutex.sem_op = +1;
operation_decrement_mutex.sem_flg = 0;
semop(semid, &operation_increment_mutex, 0);
semop(semid, &operation_increment_free_cases, 0);
}
}
void check_if_command_arguments_are_correct(int argc) {
if(argc != 2) {
fprintf(stderr, ERR_NBR_ARGS);
fprintf(stderr, "program_command <file_buffer>\n");
exit(EXIT_FAILURE);
}
}
int fileToArray(char *pathname, int *tab, int size) {
int cible;
if ( (cible = open(pathname,O_RDONLY)) < 0){
fprintf(stderr,"fileToArray - impossible to open the file\n");
return -1;
}
if (read(cible,tab,(size+1) * sizeof(int)) !=(size+1) * sizeof(int)) {
fprintf(stderr,"fileToArray - impossible to read the file\n");
return -1;
}
close(cible);
return 0;
}
int arrayToFile(char *pathname, int *tab, int size) {
int cible;
if ( (cible = open(pathname,O_WRONLY|O_CREAT|O_TRUNC,0666)) < 0){
fprintf(stderr,"arrayToFile - impossible to open the file\n");
return -1;
}
if (write(cible,tab,(size+1) * sizeof(int)) !=(size+1) * sizeof(int)) {
fprintf(stderr,"arrayToFile - impossible to write the file\n");
return -1;
}
close(cible);
return 0;
}
I'm having trouble debugging the following program I wrote. The idea is to have two seperate threads; one thread executes a 5 second countdown while the other waits for key input from the user. Whichever thread completes first should cancel the sibling thread and exit the program. However, the following code just hangs.
Any help would be appreciated, but I would be most grateful for an explanation as to the problem.
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> // For sleep()
#define NUM_THREADS 2
// The stuct to be passed as an argument to the countdown routine
typedef struct countdown_struct {
pthread_t *thread;
signed int num_secs;
} CountdownStruct;
// Struct for passing to the input wait routine
typedef struct wait_struct {
pthread_t *thread;
int *key;
} WaitStruct;
// Countdown routine; simply acts as a timer counting down
void * countdown(void *args)
{
CountdownStruct *cd_str = (CountdownStruct *)args;
signed int secs = cd_str->num_secs;
printf("Will use default setting in %d seconds...", secs);
while (secs >= 0)
{
sleep(1);
secs -= 1;
printf("Will use default setting in %d seconds...", secs);
}
// Cancel the other struct
pthread_cancel(*(cd_str->thread));
return NULL;
}
// Waits for the user to pass input through the tty
void * wait_for_input(void *args)
{
WaitStruct *wait_str = (WaitStruct *) args;
int c = 0;
do {
c = getchar();
} while (!(c == '1' || c == '2'));
*(wait_str->key) = c;
// Cancel the other thread
pthread_cancel(*(wait_str->thread));
return NULL;
}
int main(int argc, char **argv)
{
pthread_t wait_thread;
pthread_t countdown_thread;
pthread_attr_t attr;
int key=0;
long numMillis=5000;
int rc=0;
int status=0;
// Create the structs to be passe as paramaters to both routines
CountdownStruct *cd_str = (CountdownStruct *) malloc(sizeof(CountdownStruct));
if (cd_str == NULL)
{
printf("Couldn't create the countdown struct. Aborting...");
return -1;
}
cd_str->thread = &wait_thread;
cd_str->num_secs = 5;
WaitStruct *wait_str = (WaitStruct *) malloc(sizeof(WaitStruct));
if (wait_str == NULL)
{
printf("Couldn't create the iput wait struct. Aborting...");
return -1;
}
wait_str->thread = &countdown_thread;
wait_str->key = &key;
// Create the joinable attribute
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
// Create both threads
rc = pthread_create(&countdown_thread, &attr, countdown, (void *) cd_str);
if (rc) { printf("Error with the thread creation!"); exit(-1); }
rc = pthread_create(&wait_thread, &attr, wait_for_input, (void *) wait_str);
if (rc) { printf("Error with the thread creation!"); exit(-1); }
// Destroy the pthread_attribute
pthread_attr_destroy(&attr);
// now join on the threads and wait for main
pthread_join(wait_thread, NULL);
pthread_join(countdown_thread, NULL);
// Call pthread_exit
pthread_exit(NULL);
// Free the function structs
free(cd_str);
free(wait_str);
}
Getchar is not required to be a cancellation point. Select and pselect are. Even if you want to continue to use a countdown thread you could still provide a cancellation point in the opposing thread by use of select.
I had reasonable behavior with the following modified wait_for_input()
// Waits for the user to pass input through the tty
void * wait_for_input(void *args)
{
WaitStruct *wait_str = (WaitStruct *) args;
int c = 0;
fd_set readFds;
int numFds=0;
FD_ZERO(&readFds);
do {
struct timeval timeout={.tv_sec=8,.tv_usec=0};
/* select here is primarily to serve as a cancellation
* point. Though there is a possible race condition
* still between getchar() getting called right as the
* the timeout thread calls cancel.().
* Using the timeout option on select would at least
* cover that, but not done here while testing.
*******************************************************/
FD_ZERO(&readFds);
FD_SET(STDOUT_FILENO,&readFds);
numFds=select(STDOUT_FILENO+1,&readFds,NULL,NULL,&timeout);
if(numFds==0 )
{
/* must be timeout if no FD's selected */
break;
}
if(FD_ISSET(STDOUT_FILENO,&readFds))
{
printf("Only get here if key pressed\n");
c = getchar();
}
} while (!(c == '1' || c == '2'));
*(wait_str->key) = c;
// Cancel the other thread
pthread_cancel(*(wait_str->thread));
return NULL;
}
I am working on a school project where we have to make a multithreaded web server. I am having a problem where when I call sem_wait on my semaphore (which should be initialized to 0 but already seems to be sem_post()ed to 1). I get a SIGABRT.
I am attaching my code below, and I put a comment on the line that is causing my problem. I've spent a few hours with the debugger with little luck.
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string>
#include <string.h>
#include <iostream>
#include <fcntl.h>
#include <errno.h>
#include <pthread.h>
#include <vector>
#include <semaphore.h>
#include <stdio.h>
#include <cstdlib>
#include <strings.h>
#define PORTNUM 7000
#define NUM_OF_THREADS 5
#define oops(msg) { perror(msg); exit(1);}
#define FCFS 0
#define SJF 1;
void bindAndListen();
void acceptConnection(int socket_file_descriptor);
void* dispatchJobs(void*);
void* replyToClient(void* pos);
//holds ids of worker threads
pthread_t threads[NUM_OF_THREADS];
//mutex variable for sleep_signal_cond
pthread_mutex_t sleep_signal_mutex[NUM_OF_THREADS];
//holds the condition variables to signal when the thread should be unblocked
pthread_cond_t sleep_signal_cond[NUM_OF_THREADS];
//mutex for accessing sleeping_thread_list
pthread_mutex_t sleeping_threads_mutex = PTHREAD_MUTEX_INITIALIZER;
//list of which threads are sleeping so they can be signaled and given a job
std::vector<bool> *sleeping_threads_list = new std::vector<bool>();
//number of threads ready for jobs
sem_t available_threads;
sem_t waiting_jobs;
//holds requests waiting to be given to one of the threads for execution
//request implemented as int[3] with int[0]== socket_descriptor int[1]== file_size int[2]== file_descriptor of requested file
//if file_size == 0 then HEAD request
std::vector<std::vector<int> >* jobs = new std::vector<std::vector<int> >();
pthread_mutex_t jobs_mutex = PTHREAD_MUTEX_INITIALIZER;
int main (int argc, char * const argv[]) {
//holds id for thread responsible for removing jobs from ready queue and assigning them to worker thread
pthread_t dispatcher_thread;
//initializes semaphores
if(sem_init(&available_threads, 0, NUM_OF_THREADS) != 0){
oops("Error Initializing Semaphore");
}
if(sem_init(&waiting_jobs, 0, 0) !=0){
oops("Error Initializing Semaphore");
}
//initializes condition variables and guarding mutexes
for(int i=0; i<NUM_OF_THREADS; i++){
pthread_cond_init(&sleep_signal_cond[i], NULL);
pthread_mutex_init(&sleep_signal_mutex[i], NULL);
}
if(pthread_create(&dispatcher_thread, NULL, dispatchJobs, (void*)NULL) !=0){
oops("Error Creating Distributer Thread");
}
for (int i=0; i<NUM_OF_THREADS; i++) {
pthread_mutex_lock(&sleeping_threads_mutex);
printf("before");
sleeping_threads_list->push_back(true);
printf("after");
pthread_mutex_unlock(&sleeping_threads_mutex);
}
printf("here");
for (int i=0; i<NUM_OF_THREADS; i++) {
//creates threads and stores ID in threads
if(pthread_create(&threads[i], NULL, replyToClient, (void*)i) !=0){
oops("Error Creating Thread");
}
}
/*
if(sem_init(&available_threads, 0, NUM_OF_THREADS) !=0){
oops("Error Initializing Semaphore");
}
if(sem_init(&waiting_jobs, 0, 0) !=0){ //this is the semaphore thats used in the sem_wait
oops("Error Initializing Semaphore");
}*/
bindAndListen();
}
//binds to socket and listens for connections
//being done by main thead
void bindAndListen(){
struct sockaddr_in saddr;
struct hostent *hp;
char hostname[256];
int sock_id, sock_fd;
gethostname(hostname, 256);
hp = gethostbyname(hostname);
bzero(&saddr, sizeof(saddr));
//errno = 0;
bcopy(hp->h_addr, &saddr.sin_addr, hp->h_length);
saddr.sin_family = AF_INET;
saddr.sin_port = htons(PORTNUM);
saddr.sin_addr.s_addr = INADDR_ANY;
sock_id = socket(AF_INET, SOCK_STREAM, 0);
if(sock_id == -1){
oops("socket");
printf("socket");
}
if(bind(sock_id, (const sockaddr*)&saddr, sizeof(saddr)) ==0){
if(listen(sock_id, 5) ==-1){
oops("listen");
}
//each time a new connection is accepted, get file info and push to ready queue
while(1){
int addrlen = sizeof(saddr);
sock_fd = accept(sock_id, (sockaddr*)&saddr, (socklen_t*)&addrlen);
if (sock_fd > 0) {
acceptConnection(sock_fd);
}else {
oops("Error Accepting Connection");
}
}
}else{
oops("there was an error binding to socket");
}
}// end of bindAndListen()
//accepts connection and gets file info of requested file
//being done by main thread
void acceptConnection(int sock_fd){
printf("**Server: A new client connected!");
//only using loop so on error we can break out on error
while(true){
//used to hold input from client
char* inputBuff = new char[BUFSIZ];
int slen = read(sock_fd, inputBuff, BUFSIZ);
//will sit on space between HEAD/GET and path
int pos1 = 0;
//will sit on space between path and HTTP version
int pos2 = 0;
//need duplicate ptr so we can manipulate one in the loop
char* buffPtr = inputBuff;
//parses client input breaks up query by spaces
for(int i=0; i<slen; i++){
if(*buffPtr == ' '){
if (pos1 == 0) {
pos1 = i;
}else {
pos2 = i;
break;
}
}
buffPtr++;
}
if((pos1 - pos2) >=0){
std::string str = "Invalid Query";
write(sock_fd, str.c_str(), strlen(str.c_str()));
break;
}
printf("slen length %d\n", slen);
std::string* method = new std::string(inputBuff, pos1);
printf("method length %lu\n",method->length());
//increment the ptr for buff to the starting pos of the path
inputBuff += ++pos1;
printf("pos2 - pos1 %d\n", (pos2 - pos1));
printf("pos1 = %d pos2 = %d\n", pos1, pos2);
std::string* path = new std::string(inputBuff, (pos2 - pos1));
printf("path length %lu\n", path->length());
printf("part1 %s\n", method->c_str());
printf("part2 %s\n", path->c_str());
//opens file requested by client
int fd = open(path->c_str(), O_RDONLY);
if(fd < 0){
std::string* error = new std::string("Error Opening File");
*error += *path + std::string(strerror(errno), strlen(strerror(errno)));
write(sock_fd, error->c_str(), strlen(error->c_str()));
break;
}
int file_size;
if(method->compare("GET") == 0){
//gets file info and puts the resulting struct in file_info
struct stat file_info;
if(fstat(fd, &file_info) !=0){
oops("Error getting file info");
}
file_size = file_info.st_size;
}else if(method->compare("HEAD")){
file_size = 0;
}else{
write(sock_fd, "Invalid Query", strlen("Invalid Query"));
break;
}
//job to be pushed to ready queue
std::vector<int> job;
job.push_back(sock_fd);
job.push_back(file_size);
job.push_back(fd);
//check mutex guarding the ready queue
pthread_mutex_lock(&jobs_mutex);
//push job to back of ready queue
jobs->push_back(job);
//unlock mutex guarding the ready queue
pthread_mutex_unlock(&jobs_mutex);
//increment number of jobs in ready queue
sem_post(&waiting_jobs);
} //end of while(true)
// we only end up here if there was an error
fflush(stdout);
close(sock_fd);
}// end of acceptConnection()
//routine run by dispather thread
void *dispatchJobs(void*){
while(true){
//wait for a thread to be available to execute a job
sem_wait(&available_threads);
//wait for a job to be waiting in the ready queue
sem_wait(&waiting_jobs); //this is the line thats crashing
//aquire lock to check which threads are waiting
pthread_mutex_lock(&sleeping_threads_mutex);
//go through list of threads to see which is waiting
for(int i=0; i<sleeping_threads_list->size(); i++){
if(sleeping_threads_list->at(i)){
//unlocks lock for access to list of waiting threads
pthread_mutex_unlock(&sleeping_threads_mutex);
//allows us access to the list of condition variables to signal the thread to resume execution
pthread_mutex_lock(&sleep_signal_mutex[i]);
pthread_cond_signal(&sleep_signal_cond[i]);
pthread_mutex_unlock(&sleep_signal_mutex[i]);
}
}
}//end of while(true)
}//end of dispatchJobs()
//sends file or metadata to client
//run by worker thread
//pos is position of condition variable that it waits to be signaled in the sleep_signal_cond[] array
void* replyToClient(void* pos){
int position = (long)pos;
while(true){
//waits for dispather thread to signal it
pthread_mutex_lock(&sleep_signal_mutex[position]);
pthread_cond_wait(&sleep_signal_cond[position], &sleep_signal_mutex[position]);
pthread_mutex_unlock(&sleep_signal_mutex[position]);
//lock mutex to get job to be executed
pthread_mutex_lock(&jobs_mutex);
std::vector<int> job = jobs->front();
//removes job from front of vector
jobs->erase(jobs->begin());
//releases mutex
pthread_mutex_unlock(&jobs_mutex);
//socket file descriptor, used for writing to socket
int sock_fd =job[0];
int file_size = job[1];
//file descriptor for requested job
int fd = job[2];
//holds output to be written to socket
char* outputBuffer = new char[BUFSIZ];
//GET request, send file
if(file_size !=0){
int readResult = 0;
while ((readResult = read(fd, outputBuffer, BUFSIZ)) > 0) {
if(write(sock_fd, outputBuffer, readResult) != readResult){
printf("We may have a write error");
}
}
if(readResult < 0){
oops("Error Reading File");
}
if(readResult == 0){
printf("finished sending file");
}
}else{ // HEAD request
}
//increment number of available threads
sem_post(&available_threads);
}
}// end of replyToClient()
Check again the whole logic of the code - it is possible to reach here:
pthread_mutex_lock(&jobs_mutex);
std::vector<int> job = jobs->front();
//removes job from front of vector
jobs->erase(jobs->begin());
//releases mutex
pthread_mutex_unlock(&jobs_mutex);
with jobs->size () == 0, in which case front() and erase() invoke undefined behavior, which may well result in the effects you observe.
Check whether your program still crashes after the following change:
//lock mutex to get job to be executed
pthread_mutex_lock(&jobs_mutex);
if (jobs->size () == 0)
{
pthread_mutex_unlock (&jobs_mutex);
continue;
}
std::vector<int> job = jobs->front();
//removes job from front of vector
jobs->erase(jobs->begin());
//releases mutex
pthread_mutex_unlock(&jobs_mutex);
I haven't used POSIX semaphores, but I believe this is what is happening. I'm only familiar with Linux kernel semaphores, and you don't mention your system. The init function's 3rd parameter probably sets the count variable. You set it to 0 (= busy but no other processes waiting). The wait function probably invokes down(), which begins by decreasing the count variable by 1: to -1, which means the semaphore you mean to use is locked now. There is nothing in your program to ever unlock it I believe (from browsing your code - it's pretty long), so you are in trouble. Try setting it to 1 in init. This might be all that is needed.
I have been trying to debug my code whenever I had free-time for the past day and a half and I don't know what is wrong with my code. When I add the close() function to a recursive call, the program gives me an invalid pointer. But when I remove the close() function call the program runs fine, except it does not do what it is supposed to do, which is:
add up all the file sizes in a user
input directory
open sub-directories, if any, and add
up all the files inside the
sub-directory
Instead, it adds up all the file sizes in the input directory and is able to open the last sub-directory and add the files within that directory to the total file size count.
I am trying to do this with threads. The main() function creates one main thread from the user input directory and runs opendirectory() off the bat.
/*
* Iterates through given directory
*/
void *opendirectory(void *t)
{
pthread_mutex_lock(&dirlock);
DIR *dpntr;
struct dirent *dentry;
char new_directory[512], dir = t;
printf("OPENING DIRECTORY ... %s\n", t);
/* Checks if given directory can be opened */
if((dpntr = opendir(t)) == NULL) {
printf("DIRECTORY FAILED ...%s\n",t);
perror("ERROR -- COULD NOT OPEN DIR");
pthread_exit(NULL);
}
printf("DIRECTORY OPENED: %s\n", t);
/* Read each file in current directory */
while ((dentry = readdir(dpntr)) != NULL ) {
/* Ignore special directories */
if(strcmp(dentry -> d_name, ".") == 0 || strcmp(dentry -> d_name, "..") == 0) {
continue;
} else {
compilelist( t, dentry->d_name );
}
}
pthread_mutex_unlock(&dirlock);
/* Checks if directory can be closed */
if(closedir(dpntr) < 0)
printf("ERROR CLOSING %s.\n", t);
}
This is the function that will determine if a new thread should be created and is supposed to run recursively.
/*
* Determines if current file is a directory
* Creates a new thread if true
*/
void compilelist (const char* dirname, const char *filename)
{
pthread_mutex_lock(&filelock);
struct stat statdata;
char *filepathname, *dpntr;
/* Allocate memory for filepathname */
if((filepathname = (char *) malloc(sizeof(char) * strlen(dirname))) == NULL)
{
printf("CANNOT ALLOCATE MEMORY FOR FILE PATH NAME.");
pthread_exit(NULL);
}
/* Concats directory name with file name */
if(dirname[strlen(dirname) -1] == '/')
{
pthread_mutex_lock(&pathlock);
sprintf(filepathname, "%s%s", dirname, filename);
pthread_mutex_unlock(&pathlock);
}else
{
pthread_mutex_lock(&pathlock);
sprintf(filepathname, "%s/%s", dirname, filename);
pthread_mutex_unlock(&pathlock);
}
lstat(filepathname, &statdata);
/* Calls print_statdata() if current item is a file */
if(!(S_ISDIR(statdata.st_mode)))
{
printf("FILE: %s\n", filepathname);
if(!stat( filepathname, &statdata))
{
print_statdata( filename, &statdata );
}
else {
fprintf (stderr, "GETTING STAT FOR %s", filepathname);
perror( "ERROR IN STATDATA WHILE GETTING STAT");
}
}
/* Recursive call to opendirectory() */
else {
pthread_mutex_lock(&dircountlock);
dirCount++;
pthread_mutex_unlock(&dircountlock);
dpntr = filepathname;
free(filepathname);
printf("SUB-DIRECTORY THREAD: %s\nTHREAD ID NUMBER: %d\n", dpntr, dirCount);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
pthread_create(&threads[dirCount-1], &attr, opendirectory, (void *)dpntr);
}
pthread_mutex_unlock(&filelock);
}
Here is the main()
/*
* Main function prompts user for a directory
*/
int main(int argc, char *argv[])
{
int i;
char *dPtr;
// pthread_attr_t attr;
printf("ENTER A DIRECTORY:\n\t");
scanf("%s", directory);
dPtr = directory;
/* Initialize mutex and condition variable objects */
pthread_mutex_init(&mutex, NULL);
pthread_mutex_init(&filelock, NULL);
pthread_mutex_init(&dirlock, NULL);
pthread_mutex_init(&dircountlock, NULL);
pthread_cond_init (&count_threshold_cv, NULL);
/* For portability, explicitly create threads in a joinable state */
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
pthread_create(&threads[0], &attr, opendirectory, (void *)dPtr);
/* Wait for all threads to complete */
for (i = 0; i < dirCount; i++) {
pthread_join(threads[i], NULL);
}
printf("TOTAL DIRECTORY SIZE: %d\n", dirSize);
/* Clean up and exit */
pthread_attr_destroy(&attr);
pthread_mutex_destroy(&mutex);
pthread_mutex_destroy(&filelock);
pthread_mutex_destroy(&dirlock);
pthread_mutex_destroy(&dircountlock);
pthread_cond_destroy(&count_threshold_cv);
pthread_exit (NULL);
}
And the global variables ...
pthread_mutex_t mutex;
pthread_mutex_t dirlock;
pthread_mutex_t filelock;
pthread_mutex_t dircountlock;
pthread_mutex_t threadlock;
pthread_cond_t count_threshold_cv;
pthread_attr_t attr;
pthread_t threads[128]; // handles up to 128 threads (i.e. 128 directories, change accordingly)
char directory[512];
int dirSize = 0;
int dirCount = 1; // user's input directory
I feel that the pthread_create() called at the bottom of the compilelist() function is not working properly. The threads[] refers to a global array of threads that has a default size of 20, assuming that there will be no more than 20 total directories. dirCount starts off at 1 because of the user's input directory and increases as new directories are encountered.
Your code:
dpntr = opendir(t)
...
if(closedir(t) < 0)
should be:
if(closedir(dpntr) < 0)
Here I found 2 problems of your code:
As wrang-wrang metioned, closedir(t) leads segfault.
"char filepathname[512];" of compilelist() is a local memory buffer, but you pass it to your thread (opendirectory) and use it continuously. You should use copying or dynamic-allocation instead.
Effo Upd#2009nov17:
After fixing above 2 points, it works fine on my FC9 x86_64 so far. Btw: threads number 20 is really not enough.
First problem:
whenever I had free-time for the past day and a half
Don't do that, your brain isn't built for it. Allocate a time, tell your workmates/wife-and-kids that, if they bother you during this time, there will be gunshots and police involvement :-)
Other problems: no idea (hence the community wiki).