I'm new to using the pthread library in C and I have an assignment for my class to write a simple program using them. The basic description of the program is it takes 1 or more input files containing website names and 1 output file name. I then need to create 1 thread per input file to read in the website names and push them onto a queue. Then I need to create a couple of threads to pull those names off of the queue, find their IP Address, and then write that information out to the output file. The command line arguments are expected as follows:
./multi-lookup [one or more input files] [single output file name]
My issue is this. Whenever I run the program with only 1 thread to push information to the output file then everything works properly. When I make it two threads then the program hangs and none of my testing "printf" statements are even printed. My best guess is that deadlock is occurring somehow and that I'm not using my mutexes properly but I can't figure out how to fix it. Please help!
If you need any information that I'm not providing then just let me know. Sorry for the lack of comments in the code.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include "util.h"
#include "queue.h"
#define STRING_SIZE 1025
#define INPUTFS "%1024s"
#define USAGE "<inputFilePath> <outputFilePath>"
#define NUM_RESOLVERS 2
queue q;
pthread_mutex_t locks[2];
int requestors_finished;
void* requestors(void* input_file);
void* resolvers(void* output_file);
int main(int argc, char* argv[])
{
FILE* inputfp = NULL;
FILE* outputfp = NULL;
char errorstr[STRING_SIZE];
pthread_t requestor_threads[argc - 2];
pthread_t resolver_threads[NUM_RESOLVERS];
int return_code;
requestors_finished = 0;
if(queue_init(&q, 10) == QUEUE_FAILURE)
fprintf(stderr, "Error: queue_init failed!\n");
if(argc < 3)
{
fprintf(stderr, "Not enough arguments: %d\n", (argc - 1));
fprintf(stderr, "Usage:\n %s %s\n", argv[0], USAGE);
return 1;
}
pthread_mutex_init(&locks[0], NULL);
pthread_mutex_init(&locks[1], NULL);
int i;
for(i = 0; i < (argc - 2); i++)
{
inputfp = fopen(argv[i+1], "r");
if(!inputfp)
{
sprintf(errorstr, "Error Opening Input File: %s", argv[i]);
perror(errorstr);
break;
}
return_code = pthread_create(&(requestor_threads[i]), NULL, requestors, inputfp);
if(return_code)
{
printf("ERROR: return code from pthread_create() is %d\n", return_code);
exit(1);
}
}
outputfp = fopen(argv[i+1], "w");
if(!outputfp)
{
sprintf(errorstr, "Errord opening Output File: %s", argv[i+1]);
perror(errorstr);
exit(1);
}
for(i = 0; i < NUM_RESOLVERS; i++)
{
return_code = pthread_create(&(resolver_threads[i]), NULL, resolvers, outputfp);
if(return_code)
{
printf("ERROR: return code from pthread_create() is %d\n", return_code);
exit(1);
}
}
for(i = 0; i < (argc - 2); i++)
pthread_join(requestor_threads[i], NULL);
requestors_finished = 1;
for(i = 0; i < NUM_RESOLVERS; i++)
pthread_join(resolver_threads[i], NULL);
pthread_mutex_destroy(&locks[0]);
pthread_mutex_destroy(&locks[1]);
return 0;
}
void* requestors(void* input_file)
{
char* hostname = (char*) malloc(STRING_SIZE);
FILE* input = input_file;
while(fscanf(input, INPUTFS, hostname) > 0)
{
while(queue_is_full(&q))
usleep((rand()%100));
if(!queue_is_full(&q))
{
pthread_mutex_lock(&locks[0]);
if(queue_push(&q, (void*)hostname) == QUEUE_FAILURE)
fprintf(stderr, "Error: queue_push failed on %s\n", hostname);
pthread_mutex_unlock(&locks[0]);
}
hostname = (char*) malloc(STRING_SIZE);
}
printf("%d\n", queue_is_full(&q));
free(hostname);
fclose(input);
pthread_exit(NULL);
}
void* resolvers(void* output_file)
{
char* hostname;
char ipstr[INET6_ADDRSTRLEN];
FILE* output = output_file;
int is_empty = queue_is_empty(&q);
//while(!queue_is_empty(&q) && !requestors_finished)
while((!requestors_finished) || (!is_empty))
{
while(is_empty)
usleep((rand()%100));
pthread_mutex_lock(&locks[0]);
hostname = (char*) queue_pop(&q);
pthread_mutex_unlock(&locks[0]);
if(dnslookup(hostname, ipstr, sizeof(ipstr)) == UTIL_FAILURE)
{
fprintf(stderr, "DNSlookup error: %s\n", hostname);
strncpy(ipstr, "", sizeof(ipstr));
}
pthread_mutex_lock(&locks[1]);
fprintf(output, "%s,%s\n", hostname, ipstr);
pthread_mutex_unlock(&locks[1]);
free(hostname);
is_empty = queue_is_empty(&q);
}
pthread_exit(NULL);
}
Although I'm not familiar with your "queue.h" library, you need to pay attention to the following:
When you check whether your queue is empty you are not acquiring the mutex, meaning that the following scenario might happen:
Some requestors thread checks for emptiness (let's call it thread1) and just before it executes pthread_mutex_lock(&locks[0]); (and after if(!queue_is_full(&q)) ) thread1 gets contex switched
Other requestors threads fill the queue up and when out thread1 finally gets hold of the mutex if will try to insert to the full queue. Now if your queue implementation crashes when one tries to insert more elements into an already full queue thread1 will never unlock the mutex and you'll have a deadlock.
Another scenario:
Some resolver thread runs first requestors_finished is initially 0 so (!requestors_finished) || (!is_empty) is initially true.
But because the queue is still empty is_empty is true.
This thread will reach while(is_empty) usleep((rand()%100)); and sleep forever, because you pthread_join this thread your program will never terminate because this value is never updated in the loop.
The general idea to remember is that when you access some resource that is not atomic and might be accessed by other threads you need to make sure you're the only one performing actions on this resource.
Using a mutex is OK but you should consider that you cannot anticipate when will a context switch occur, so if you want to chech e.g whether the queue is empty you should do this while having the mutex locked and not unlock it until you're finished with it otherwise there's no guarantee that it'll stay empty when the next line executes.
You might also want to consider reading more about the consumer producer problem.
To help you know (and control) when the consumers (resolver) threads should run and when the producer threads produce you should consider using conditional variables.
Some misc. stuff:
pthread_t requestor_threads[argc - 2]; is using VLA and not in a good way - think what will happen if I give no parameters to your program. Either decide on some maximum and define it or create it dynamically after having checked the validity of the input.
IMHO the requestors threads should open the file themselves
There might be some more problems but start by fixing those.
Related
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <pthread.h>
#include <math.h>
#include <stdbool.h>
#include <string.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
char mutual[100];
int firstWrote = 0;
int secondWrote = 0;
int firstFinished = 0;
int secondFinished = 0;
void* reader(void* arg)
{
FILE* file = fopen((const char*)arg,"r");
if(file == NULL)
{
printf("Problem with opening file %s",(const char*)arg);
exit(4);
}
else
{
char which[25];
while(fgets(mutual,100,file))
{
pthread_mutex_lock(&mutex);
strcpy(which,(const char*)arg);
if(strcmp(which,"/etc/profile")==0)
{
firstWrote = 1;
//printf("%s\n",mutual);
fflush(stdout);
}
if(strcmp(which,"/etc/passwd")==0)
{
secondWrote = 1;
//printf("%s\n",mutual);
fflush(stdout);
}
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
}
if(strcmp(which,"/etc/profile")==0)
{
firstWrote = 0;
firstFinished= 1;
}
else if(strcmp(which,"etc/passwd")==0)
{
secondWrote = 0;
secondFinished = 1;
}
}
fclose(file);
pthread_exit(NULL);
}
int main(void)
{
FILE* pawel1 = fopen("PAWEL1","w");
FILE* pawel2 = fopen("PAWEL2","w");
if(pawel1 == NULL || pawel2 == NULL)
{
printf("Problem with opening file");
return 1;
}
pthread_t p1,p2;
pthread_attr_t attr;
if(pthread_attr_init(&attr) != 0)
exit(2);
if(pthread_attr_setd2etachstate(&attr,PTHREAD_CREATE_DETACHED))
exit(3);
pthread_create(&p1,&attr,reader,(void*)"/etc/profile");
pthread_create(&p2,&attr,reader,(void*)"/etc/passwd");
while(1)
{
pthread_mutex_lock(&mutex);
if(firstFinished == 1 && secondFinished == 1)
break;
pthread_cond_wait(&cond,&mutex);
if(firstWrote == 1)
{
fprintf(pawel1,"%s",mutual);
firstWrote = 0;
}
if(secondWrote == 1)
{
fprintf(pawel2,"%s",mutual);
secondWrote = 0;
}
pthread_mutex_unlock(&mutex);
}
fclose(pawel1);
fclose(pawel2);
pthread_attr_destroy(&attr);
pthread_cond_destroy(&cond);
pthread_mutex_destroy(&mutex);
return 0;
}
The app task is to made two different thread read from two files (/etc/passwd and /etc/profile) to a global buffer, and print results to proper file (PAWEL1 or PAWEL2) in main function , and it seems that i handle this behaviour with global flags but what i get is all lines from passwd and profile in one file - PAWEL2, and the file PAWEL1 is left empty.
Since you share a one-line buffer among all threads, including both readers, you must ensure that the main thread runs after each reader thread, never one reader immediately after the other (or itself). Otherwise, one reader can overwrite the data and status flags set by the other. You can accomplish this by
using the existing state flags to determine whose turn it is to run, and
broadcasting to the CV instead of merely signalling it, so that all participating threads get an opportunity to evaluate whether it is appropriate for them to perform a unit of work.
In particular, the rules could look like this:
Readers can run if !firstWrote && !secondWrote, which shows that the buffer is available for new data. They set the appropriate one of those variables after successfully reading the line, thus blocking both readers from overwriting the buffer until its contents are consumed, or they set the appropriate Finished variable and terminate if they cannot read a line.
The main thread can run if firstWrote || secondWrote || (firstFinished && secondFinished). After successfully reading out the buffer, it is responsible for resetting the appropriate one of firstWrote and secondWrote to zero.
When I say a thread "can run", I am talking about the condition associated with each thread's CV wait. The proper idiom for CV usage always involves such a condition, and it looks like this (pseudocode):
lock_mutex()
// ... maybe other code ...
while (condition_is_false()) {
wait_on_cv()
}
// ... maybe other code ...
unlock_mutex()
All accesses to the data that contribute to the condition must be performed under protection from the mutex.
The loop around the wait accounts for two main things:
Control arriving at the condition check when the thread is already clear to proceed
Waking from the wait despite not being clear to proceed, either because of receiving a signal or spuriously (which occasionally does happen)
You can add another level of looping around that (and that would be appropriate for your uses).
Note also that you could simplify your logic considerably by using one set of *Wrote and *Finished variables instead of a separate set for each reader. You would then achieve the necessary distinction among program states by using more values for those variables than just 0 and 1.
Trying to implement the critical section of this program to actually swap between both threads correctly as stated later in the description.
I am trying to do a problem for my Operating Systems course. The problem is that I need to input two files, one of each are put into their separate threads, they will read through the file till they hit a line with the number "0". Then the other thread is supposed to run by the same rules.
This program is supposed to take two file input and figure out the message by concatenating both of the inputs from the files in a specific order and then printing out the output after it has deciphered it.
The inputs of these two files as shown below
Person1 Person2
--------- ----------
t 0
0 h
i 0
s 0
0 i
0 s
0 a
t 0
e 0
0 s
t 0
the above inputs should result in a string with this output
Example: “thisisatest”
Currently what is going wrong with the assignment is that it is not swapping correctly between the two threads and sitting in infinite loops.
Like I said above I am trying to solve this assignment by use of Mutexes and Pthreads
Below is the current implementation of my code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static char *charArray[1000];
void *threadPerson1(void *value){
FILE *fPTR;
char buffer[2];
char *fileName = "Person1.txt";
fPTR = fopen(fileName, "r");
if (fPTR == NULL){
printf("was unable to open: %s\n", fileName);
return NULL;
}
while(1){
//Entering the critical section
pthread_mutex_lock(&mutex);
fscanf(fPTR, "%s", buffer);
printf("This is Person1: %s\n", buffer);
if(buffer != "0"){
charArray[count] = buffer;
count++;
}
if(buffer == "0"){
pthread_mutex_unlock(&mutex);
}
//exiting the critical section
}
}
void *threadPerson2(void *value){
FILE *fPTR;
char buffer[2];
char *fileName = "Person2.txt";
fPTR = fopen(fileName, "r");
if (fPTR == NULL){
printf("was unable to open: %s\n", fileName);
return NULL;
}
while(1){
//entering the critical section
pthread_mutex_lock(&mutex);
fscanf(fPTR, "%s", buffer);
printf("This is Person2: %s\n", buffer);
if(buffer != "0"){
charArray[count] = buffer;
count++;
}
if(feof(fPTR)){
printf("read end of file of: Person2\n");
fclose(fPTR);
return NULL;
}
if(buffer == "0"){
pthread_mutex_unlock(&mutex);
}
//exiting the critical section
}
}
int main(int argc, char **argv){
pthread_t thread1;
pthread_t thread2;
pthread_create(&thread1, NULL, threadPerson1, NULL);
pthread_create(&thread2, NULL, threadPerson2, NULL);
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
for(int x = 0; x < 1000; x++){
if(charArray[x] == NULL){
printf("\n");
break;
}else{
printf("%s", charArray[x]);
}
}
return 0;
}
At least two things are incorrect in your program. First of all, if one thread releases the mutex, you are not guaranteed that the scheduler will allow the other thread will run, the releasing thread may very well go on and reacquire the mutex right away. Use a condition variable, read its manpages. Also, here are some examples, in particular 4-8: Multithreaded programming guide
Two, when you reach end of file, you need to release the mutex to clean up. Easiest way to do it is what we call RAII in C++, i.e. use a resource handle that releases the mutex when the handle object goes out of scope. You can do similar things in C e.g. by registering a cleanup function, or a 'goto' to end of function with cleanup code called from there.
i m trying to implement the producer - consumer with threads.In my code below is working well except of the point where i try to print into 2 files.(one for prod and one for cons).I use fclose(stdout) . when i delete it inside producer then the print in producer file is ok but consumers no.when i deleted at all then all of the data printed into the consumer file.My question is what i should try to make the prints correctly( i need consumer to print in cons.out.txt and producer in prod_out.txt) .Also i didnt include check for threads to make it simple but it works well.
#include "head.h"
circular_buffer cb;
pthread_mutex_t lock;
pthread_cond_t cond;
char * data[6]; //is the data taken from cmd(argv[0-6]) first one is the program the other are parameters
int main(int argc , char *argv[]) {
validate(argv,argc); //if command arguments are valid go on
for(int i=0; i < argc; i++){
data[i]=argv[i]; //copy argv array to data array
}
cb_init(&cb,10,sizeof(int)); //initialization of circular buffer for 10 nodes + size of int each node
pthread_t cons, prod;
void *consumer(), *producer();
int rc = pthread_mutex_init(&lock, NULL);
rc = pthread_cond_init(&cond, NULL);
int id[]={1,2}; //prod id is 1 and cons 2
rc = pthread_create(&prod, NULL, producer, &id[0]);
rc = pthread_create(&cons, NULL, consumer, &id[1]);
rc=pthread_join(prod, NULL);
rc=pthread_join(cons, NULL);
rc = pthread_mutex_destroy(&lock);
rc = pthread_cond_destroy(&cond);
cb_free(&cb); //free allocated memory which is alocated in head
return 1;
}
void *consumer(void * id){
int thread_id= *((int*)id); //cast to (int)
int file;
FILE * fp2 = fopen("./cons_out.txt","wb"); //creates new file with "cons_out.txt" name
int i =10;
while(i > 0){
i--;
pthread_mutex_lock(&lock);
while(isEmpty(&cb)){
int rc = pthread_cond_wait(&cond,&lock);
}//end while
int item=0; // item that will be consumed
cb_pop_front(&cb,&item);
file = open("./cons_out.txt", O_APPEND | O_WRONLY, 0777 );//open file as new
if( file == -1 ) {//fail to open file
perror("./cons_out.txt");
return EXIT_FAILURE;
}
fclose(stdout);
dup2( file, STDOUT_FILENO); //change redirections
printf("Consumer %d : %d\n",thread_id,item); //print output to file
pthread_cond_signal(&cond);
pthread_mutex_unlock(&lock);
} //end while
return NULL;
} //end consumer()
void *producer(void * id){
int thread_id= *((int*)id); //cast to (*int)
int file1;
FILE * fp1 = fopen("./prod_in.txt","wb"); //creates new file with "prod_in.txt" name
for(int i=0; i<10; i++){ //it produce 10 numbers
pthread_mutex_lock(&lock);
while(isFull(&cb)){
int rc = pthread_cond_wait(&cond,&lock);
}//end while
int seed=1;
int item=rand(); // item of this producer
cb_push_back(&cb,&item);
file1 = open("./prod_in.txt", O_APPEND | O_WRONLY, 0777 );//open file as new
if( file1 == -1 ) {//fail to open file
perror("./prod_in.txt");
return EXIT_FAILURE;
}
fclose(stdout);
dup2( file1, STDOUT_FILENO); //change output
printf("Producer %i : %i\n",thread_id,item); //print output to file
pthread_cond_signal(&cond);
pthread_mutex_unlock(&lock);
} //end for
return NULL;
} //end Producer();
There is only one open file table for your whole process, shared by all threads in it. There is one entry in that table for the standard output, and your producer and consumer are contending for it. It's not clear why.
I'm inclined to think that the immediate reason for your troubles is that fclose()ing the stdout stream releases the stream-level resources, especially the buffer and state-tracking details. open()ing a file descriptor does not provide those, nor does dup2()ing a file descriptor into a FILE object representing a closed stream.
But it baffles me why you are making it so hard. Why in the world muck with duping file descriptors? Your producer and consumer already fopen() the wanted output files, thereby each obtaining its own stream connected with the appropriate file (fp1 and fp2). It seems to me that all you need to do, then, is to write to those streams via fprintf(), etc., instead of going to a lot of trouble to be able to use printf() instead.
That would be more efficient, too, since you oughtn't to need to keep opening and closing the files. Open each once, at the beginning, as you already do, and close each once, at the end, which you presently fail to do.
I'm new to pthread and multithreading, i have written a code like that.
#include <pthread.h>
#include <unistd.h>
void *nfc_read(void *arg)
{
int fd = -1;
int ret;
uint8_t read_data[24];
while(1){
ret = read_block(fd, 8, read_data);
if(!ret){
return (void)read_data;
}
}
}
int main(int argc, char *argv[])
{
pthread_t my_thread;
void *returnValue;
pthread_create(&my_thread, NULL, nfc_read, NULL);
pthread_join(my_thread, &returnValue);
printf("NFC card value is : %s \n", (char)returnValue);
printf("other process");
return 0;
}
Until the return value from nfc_read function, as I will have other code I need to run and I don't want to block in main. How can i do that?
This is a sample where a read thread runs concurrently to the "main" thread which is doing other work (in this case, printing dots and sleeping).
To keep things simple, a simulated the reading from an input device with copying a constant string character by character. I guess, this is reasonable as the synchronization of threads is focused.
For the synchronization of threads, I used atomic_bool with atomic_store() and atomic_load() which are provided by the Atomic Library (since C11).
My sample application test-concurrent-read.c:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <stdatomic.h>
#include <unistd.h>
/* sampe input */
const char sampleInput[]
= "This is sample input which is consumed as if it was read from"
" a (very slow) external device.";
atomic_bool readDone = ATOMIC_VAR_INIT(0);
void* threadRead(void *pArg)
{
char **pPBuffer = (char**)pArg;
size_t len = 0, size = 0;
int c; const char *pRead;
for (pRead = sampleInput; (c = *pRead++) > 0; sleep(1)) {
if (len + 1 >= size) {
if (!(*pPBuffer = realloc(*pPBuffer, (size + 64) * sizeof(char)))) {
fprintf(stderr, "ERROR! Allocation failed!\n");
break;
}
size += 64;
}
(*pPBuffer)[len++] = c; (*pPBuffer)[len] = '\0';
}
atomic_store(&readDone, 1);
return NULL;
}
int main()
{
/* start thread to read concurrently */
printf("Starting thread...\n");
pthread_t idThreadRead; /* thread ID for read thread */
char *pBuffer = NULL; /* pointer to return buffer from thread */
if (pthread_create(&idThreadRead, NULL, &threadRead, &pBuffer)) {
fprintf(stderr, "ERROR: Failed to start read thread!\n");
return -1;
}
/* start main loop */
printf("Starting main loop...\n");
do {
putchar('.'); fflush(stdout);
sleep(1);
} while (!atomic_load(&readDone));
putchar('\n');
void *ret;
pthread_join(idThreadRead, &ret);
/* after sync */
printf("\nReceived: '%s'\n", pBuffer ? pBuffer : "<NULL>");
free(pBuffer);
/* done */
return 0;
}
Compiled and tested with gcc in cygwin on Windows 10 (64 bit):
$ gcc -std=c11 -pthread -o test-concurrent-read test-concurrent-read.c
$ ./test-concurrent-read
Starting thread...
Starting main loop...
.............................................................................................
Received: 'This is sample input which is consumed as if it was read from a (very slow) external device.'
$
I guess, it is worth to mention why there is no mutex guarding for pBuffer which is used in main() as well as in threadRead().
pBuffer is initialized in main() before pthread_create() is called.
While thread_read() is running, pBuffer is used by it exclusively (via its passed address in pPBuffer).
It is accessed in main() again but not before pthread_join() which grants that threadRead() has ended.
I tried to find a reference by google to confirm that this procedure is well-defined and reasonable. The best, I could find was SO: pthread_create(3) and memory synchronization guarantee in SMP architectures which cites The Open Group Base Specifications Issue 7 - 4.12 Memory Synchronization.
I Work with couple of threads. all running as long as an exit_flag is set to false.
I Have specific thread that doesn't recognize the change in the flag, and therefor not ending and freeing up its resources, and i'm trying to understand why.
UPDATE: After debugging a bit with gdb, i can see that given 'enough time' the problematic thread does detects the flag change.
My conclusion from this is that not enough time passes for the thread to detect the change in normal run.
How can i 'delay' my main thread, long enough for all threads to detect the flag change, without having to JOIN them? (the use of exit_flag was in an intention NOT to join the threads, as i don't want to manage all threads id's for that - i'm just detaching each one of them, except the thread that handles input).
I've tried using sleep(5) in close_server() method, after the flag changing, with no luck
Notes:
Other threads that loop on the same flag does terminate succesfully
exit_flag declaration is: static volatile bool exit_flag
All threads are reading the flag, flag value is changed only in close_server() method i have (which does only that)
Data race that may occur when a thread reads the flag just before its changed, doesn't matter to me, as long as in the next iteration of the while loop it will read the correct value.
No error occurs in the thread itself (according to strerr & stdout which are 'clean' from error messages (for the errors i handle in the thread)
Ths situation also occurs even when commenting out the entire while((!exit_flag) && (remain_data > 0)) code block - so this is not a sendfile hanging issure
station_info_t struct:
typedef struct station_info {
int socket_fd;
int station_num;
} station_info_t;
Problematic thread code:
void * station_handler(void * arg_p)
{
status_type_t rs = SUCCESS;
station_info_t * info = (station_info_t *)arg_p;
int remain_data = 0;
int sent_bytes = 0;
int song_fd = 0;
off_t offset = 0;
FILE * fp = NULL;
struct stat file_stat;
/* validate station number for this handler */
if(info->station_num < 0) {
fprintf(stderr, "station_handler() station_num = %d, something's very wrong! exiting\n", info->station_num);
exit(EXIT_FAILURE);
}
/* Open the file to send, and get his stats */
fp = fopen(srv_params.songs_names[info->station_num], "r");
if(NULL == fp) {
close(info->socket_fd);
free(info);
error_and_exit("fopen() failed! errno = ", errno);
}
song_fd = fileno(fp);
if( fstat(song_fd, &file_stat) ) {
close(info->socket_fd);
fclose(fp);
free(info);
error_and_exit("fstat() failed! errno = ", errno);
}
/** Run as long as no exit procedure was initiated */
while( !exit_flag ) {
offset = 0;
remain_data = file_stat.st_size;
while( (!exit_flag) && (remain_data > 0) ) {
sent_bytes = sendfile(info->socket_fd, song_fd, &offset, SEND_BUF);
if(sent_bytes < 0 ) {
error_and_exit("sendfile() failed! errno = ", errno);
}
remain_data = remain_data - sent_bytes;
usleep(USLEEP_TIME);
}
}
printf("Station %d handle exited\n", info->station_num);
/* Free \ close all resources */
close(info->socket_fd);
fclose(fp);
free(info);
return NULL;
}
I'll be glad to get some help.
Thanks guys
Well, as stated by user362924 the main issue is that i don't join the threads in my main thread, therefore not allowing them enough time to exit.
A workaround to the matter, if for some reason one wouldn't want to join all threads and dynamically manage thread id's, is to use sleep command in the end of the main thread, for a couple of seconds.
of course this workaround is not good practice and not recommended (to anyone who gets here by google)