I am performing some physical calculations in my program, in which the output needs to be stored into a temporary buffer and passed through a pipe.
The buffer needs to share different data types: firstly, I need to store the name of the subject I'm studying; secondly the results of my calculations (all float numbers).
The code looks like this:
initialdata.dat
Aston Martin Vantage V12|07.7|090
Ferrari LaFerrari |09.6|111
Lamborghini Aventador |09.6|097
Porsche 911 Turbo S |09.6|092
Tesla Model S P100D |10.0|069
Hennessey Venom GT |10.3|120
Bugatti Chiron |11.2|114
Koenigsegg Agera |10.3|121
Main.c:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <string.h>
#include <time.h>
#include <math.h>
#define READ 0
#define WRITE 1
#define M 2 // Number of subjects.
int main(){
int pipeToChild[2];
if (pipe(pipeToChild) < 0) {
fprintf(stderr,"Error in pipe!");
return -1;
}
pid_t pid[M];
srand (time(NULL));
// Declares the file pointer with the information.
FILE * pFile;
char buffer[34]; // Buffer where subject info is sent to childs.
char tempBuffer[50]; // The buffer that causes problems.
pFile = fopen ("initialdata.dat","r");
for(int i = 0; i < M; i++){
pid[i] = fork();
if (pid[i] < 0){
printf("Fork error!\n");
return -1;
}
else if(pid[i]==0){
// Creates the pipes (one per child) to pass the temporary buffer to the results process (still not created).
int pipeToResults[2];
if (pipe(pipeToResults) < 0) {
fprintf(stderr,"Error in pipe!");
return -1;
}
// Receives the car information from the file through a pipe.
char receivedValue[34];
receivedValue[33] = '\0';
close(pipeToChild[WRITE]);
read(pipeToChild[READ], receivedValue, sizeof(receivedValue));
// Processes the information and tokenizes it.
char name[25];
char CHARacceleration[6];
char CHARmaxSpeed[4];
strcpy(name, strtok(receivedValue, "|"));
strcpy(CHARacceleration, strtok(NULL, "|"));
strcpy(CHARmaxSpeed, strtok(NULL, "|"));
float acceleration = atof(CHARacceleration);
float maxSpeed = atoi(CHARmaxSpeed);
// Adds 0.0X to acceleration.
float randomNum = rand() % 5;
acceleration = acceleration + randomNum/100;
float distance = 0;
float TA = 0; // Time using Uniformly Accelerated Linear Motion.
float TB = 0; // Time using Linear Motion.
float TE = 0.5; // Time increment.
float currentVelocity = 0; // In m/s.
// Applies different physical calculations depending on the case.
while (distance <= 1000){
TA += TE;
if (currentVelocity < maxSpeed){ // Acceleration > 0
distance = (acceleration*pow((TA),2))/2;
currentVelocity = acceleration*TA;
distance = 2*distance;
}
else{ // Acceleration = 0
TB += TE;
currentVelocity = maxSpeed;
distance += maxSpeed*TB;
}
}
// Debug purposes, just for ensuring everything gets processed the right way.
printf("Name: %s\n", name);
printf("Distance: %.2f m\n", distance);
printf("Time: %.2f s\n", TA+TB);
printf("Max speed reached: %.2f km/h\n", currentVelocity*3.6);
printf("Acceleration: %.2f m/s^2\n", acceleration);
printf("\n");
// Comment this if you want to switch between the situations I talked about.
sprintf(tempBuffer, "%s %.2f %.2f %.2f %.2f", name, distance, TA+TB, currentVelocity, acceleration);
printf("Buffer: %s\n\n", tempBuffer);
exit(0);
}
else if(pid[i]>0){
// Generates a random subject from the list. Buggy section, fixed it the best way I could.
int randCar = rand() % 15 + 1;
if (randCar % 2 == 0)
randCar--;
for (int i = 1; i <= randCar; i++){
if (pFile != NULL)
fgets (buffer, sizeof(buffer), pFile);
else
perror ("ERROR reading file!");
}
char toSend[34]; //This will be passed through the `pipeToChild` pipe.
strcpy(toSend, buffer);
// Loads pipe.
close(pipeToChild[READ]);
write(pipeToChild[WRITE], toSend, strlen(toSend));
close(pipeToChild[WRITE]);
}
}
for (int i=0;i<M;i++){
waitpid(pid[i], NULL, 0);
}
fclose(pFile);
return 0;
}
The output, though, is different depending if I use the sprintf or not. For example, for M=2, the output should be:
Case 1: No sprintf:
I'm the child process 1 with PID 12304
Name: Bugatti Chiron
Distance: 1012.61 m
Time: 9.50 s
Max speed reached: 383.72 km/h
Aceleration: 11.22 m/s^2
I'm the child process 2 with PID 12305
Name: Bugatti Chiron
Distance: 1012.61 m
Time: 9.50 s
Max speed reached: 383.72 km/h
Aceleration: 11.22 m/s^2
Case 2: sprintf:
I'm the child process 2 with PID 12307
I'm the child process 1 with PID 12306
Name: Bugatti Chiron
Distance: 1012.61 m
Time: 9.50 s
Max speed reached: 383.72 km/h
Aceleration: 11.22 m/s^2
Buffer: Bugatti Chiron 1012.61 9.50 383.82 11.22
What's the problem with 'sprinting'? Why is this line screwing the whole program?
EDIT: The program is a simple drag racing simulator, where M cars compete in a 1000m straight line. The master process creates M random cars (this function is not correctly implemented, though) and passes some data stored in the .dat file to M child processes through a single pipe.
Every car is a child process, and it's within it where the calculations are made. Once we get the values, every child passes the data stored in a temporary buffer through its own pipe to one results process which stores them in an output file. Note that this function is still not implemented because first I need to manage to create the buffer. My question is only about the buffer issue.
When you fork, you create a new process. Without any type of synchronization, the two child processes that get created run at the same time. So the output of each child may be interleaved with each other, depending on how the OS decided to schedule each of the processes. The presence of the sprintf doesn't change that.
You'll probably need to pass messages over pipes to get the processes to sync with each other to control when each of them prints.
char tempBuffer[50]; was initialized to 50 instead of 34, which is the exact size of the data I sent.
Related
SHORT: My problem is that the following function which sum the content of a given array in a given range, doesn't take the same execution time for the same task as the parent if called from a child. And by same I mean similar values. Because after some tests, the differences is for about a ~40% more for the child to execute.
LONG: I'm studying computer science and I'm working on the final project of a course. The problem is to sum all the cells of an int array by n processes distributing the workload. then confront it to a single calculation made by parent. The point is to demonstrate that, with big arrays, multiple process can reduce the execution time.
However, the sum of all children's times is always more than the parent's even with 50milions data.
Following just the function and the struct I use to pass results.
typedef struct cResults{
double time;
int result;
}cResult;
cResult arraySum(int start, int length, int* dataset)
{
/*Time unit to calculate the computation time*/
clock_t tic, toc = 0;
cResult chunk = {.result = 0, .time =0};
tic = clock(); // Start measure time
for (int i = start; i < start + length; i++)
{
chunk.result += dataset[i];
}
toc = clock(); // Stop measure time
chunk.time = ((double) (toc - tic))/ CLOCKS_PER_SEC;
printf("B: start at: %d, length: %d, sum: %d, in: %f\n", start, length, chunk.result, chunk.time); //! DEBUG
return chunk;
}
WHAT I'VE TRIED SO FAR:
Since the array is dynamically allocated, I've thought that it could be a bottleneck on the memory access. However, this question (Malloc returns same address in parent and child process after fork) lifted all doubt that even if heap allocated, they are not the same, but a copy of the array.
I've double checked that the parent will sum correctly and only once the elapsed time communicated by all the children, and then added the print() statement just to read and sum manually on the terminal all the results. And again, all checks.
I've tried moving the function call by parent from before to after all children were done, but no changes, then I've tried make the parent sleep() right after fork() (this was counterproductive for the purpose of the project but just to make sure) for avoiding resource queue.
The random numbers in the array are produced in a repeatable way through a seed, so I've tried same datasets that of course will give almost identical outputs, and again times will change slightly yet maintaining the single execution faster.
Ultimately I've tried to fork a single child and make it calculate the same range as the parent (so all the array). The time is on average 45% slower on children.
Surely I'm missing a simple thing but i run out of ideas... please be patient I'm learning by my self at my best.
UPDATE 1:
Since I've been asked for a full program, I've refactored mine. However, whereas the project required a single source file I've removed all of non regarding parts of our issue, hence it should be lighter. Unfortunately the frame you'll see that handle the pipe communications is a bit complex I'm afraid but it serve for other purpose that has been removed yet essential to make this works. I do hope it won't be a problem.
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <limits.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
/* ----- PROTOTYPES ----- */
#define MIN 5 // Least number of process and threads
#define MAX 10 // Maximum number of process and threads
#define MSGSIZE 4 // Size of pipe messages
/* =================
*PIPE MESSAGES*
sum = Sum from the next given start to the next given roof.
end = End process
tmr = Return the computation time measured by the process
================= */
/// #brief Struct containing proces id (pid), the result and it's pipe file descriptors
struct process{
pid_t pid;
long int result;
bool done;
double time;
int fd[2];
};
/// #brief Struct that containts the result of the computation
typedef struct cResults{
double time;
long int result;
}cResult;
/// #brief W = write, R = read
enum PIPECONTROLLER{
R = 0,
W = 1
};
/* ----- PROTOTYPES ----- */
cResult arraySum(int start, int length, int* dataset);
int main()
{
/* =================
*USER DEFINED*
================= */
int dataLength = 50000000; // Set the length of the dataset
int nProcess = 1; // How many child process do you want
unsigned int seed = 1; // Change the randomization seed of the data
// System
int* data;
cResult chunk; // Used for storing temporary computational values
// Task related
int taskLenght;
int remainder;
// Pipe related
char pipeMSG[MSGSIZE];
int msgCheck = 1;
/// #brief Processes dashboard
struct process processes[MAX + 1] = { {.pid = 0, .result = 0, .done = false, .time = 0} };
data = malloc(sizeof(int) * dataLength);
srand(seed);
for (int i = 0; i < dataLength; i++)
{
/*Random population between 0-100*/
data[i] = rand() % 100;
}
chunk = arraySum(0, dataLength, data);
processes[nProcess + 1].result = chunk.result;
processes[nProcess + 1].time = chunk.time;
printf("\nCHECK SUM: %ld\n", chunk.result);// ! Debug
#pragma region "START PROCESSES"
/*Calculate how to separate the overhead for the processes*/
taskLenght = dataLength / nProcess;
remainder = dataLength % nProcess;
pid_t myPid = 0;
int startPoint = 0;
processes[nProcess + 1 ].pid = getpid();
/*Open child to parent pipe*/
if (pipe(processes[nProcess + 1 ].fd) == -1)
{
printf("Failed to open pipe on parent\n");
return 1;
}
for (int i = 0; i < nProcess; i++)
{
/*Open new parent to child pipe*/
if (pipe(processes[i].fd) == -1)
{
printf("Failed to open pipe on parent\n");
return 1;
}
myPid = fork();
switch (myPid)
{
case -1: // Error on fork
printf("An error occured while forking the %d process.\n", i);
return 1;
break;
case 0: // Child case
/*Record pid in the dashboard*/
processes[i].pid = getpid();
/*Handle the pipes descriptors*/
close(processes[i].fd[W]);
close(processes[nProcess + 1 ].fd[R]);
i = nProcess;
break;
default: // Parent case
/* Record the pid process into the dashrboard and increment the starting for the next*/
processes[i].pid = myPid;
startPoint += taskLenght;
/*Handle the pipes descriptors*/
close(processes[i].fd[R]);
break;
}
}
/*=========== CHILD PROCESS HANDLER ===========*/
if(myPid == 0)
{
int myID;
bool keepAlive = true;
for(myID = 0; myID < nProcess; myID++)
{
if (processes[myID].pid == getpid())
{
break;
}
}
/*Calculate first iteration of the sum*/
cResult temp = arraySum(startPoint, taskLenght, data);
chunk.result = temp.result;
chunk.time = temp.time;
while(keepAlive)
{
/*Comunicate the id of the process*/
if (write(processes[nProcess + 1 ].fd[W], &myID, sizeof(int)) < 0)
{
printf("An error occured from the process %d while sending message to parent\n", getpid());
return 1;
}
/*Communicate the result of the operation*/
if (write(processes[nProcess + 1 ].fd[W], &chunk.result, sizeof(int)) < 0)
{
printf("An error occured from the process %d while sending message to parent\n", getpid());
return 1;
}
/*Communicate the time elapsed for the operation*/
if (write(processes[nProcess + 1 ].fd[W], &chunk.time, sizeof(double)) < 0)
{
printf("An error occured from the process %d while sending message to parent\n", getpid());
return 1;
}
/*Waits for further instruction*/
msgCheck = read(processes[myID].fd[R], pipeMSG, MSGSIZE);
if(msgCheck < 0)
{
printf("An error occured from the process %d while reading message from parent\n", getpid());
return 1;
}
/*Sum command*/
if(!strcmp(pipeMSG, "sum"))
{
msgCheck = read(processes[myID].fd[R], &startPoint, sizeof(int));
if(msgCheck < 0)
{
printf("An error occured from the process %d while reading message from parent\n", getpid());
return 1;
}
msgCheck = read(processes[myID].fd[R], &taskLenght, sizeof(int));
if(msgCheck < 0)
{
printf("An error occured from the process %d while reading message from parent\n", getpid());
return 1;
}
/*Calculate second iteration for the remaining part*/
temp = arraySum(startPoint, taskLenght, data);
chunk.result += temp.result;
chunk.time += temp.time;
}
/*Close command*/
if(!strcmp(pipeMSG, "end"))
{
keepAlive = false;
}
}
free(data);
close(processes[myID].fd[R]);
exit(0);
}
/*=========== PARENT PROCESS HANDLER ===========*/
if(myPid != 0)
{
/*Close descriptor for writing on main pipe.*/
close(processes[nProcess + 1 ].fd[W]);
int targetProcess = nProcess + 1; // Target self
bool onGoing = true;
chunk.result = 0;
chunk.time = 0;
while(onGoing)
{
/*Listen from processes if someone ended the task*/
msgCheck = read(processes[nProcess + 1 ].fd[R], &targetProcess, sizeof(int));
if(msgCheck < 0)
{
printf("An error occured from the process %d while reading message from parent\n", getpid());
return 1;
}
/*Get result from child process*/
msgCheck = read(processes[nProcess + 1 ].fd[R], &processes[targetProcess].result, sizeof(int));
if(msgCheck < 0)
{
printf("An error occured from the process %d while reading message from parent\n", getpid());
return 1;
}
/*Get elapsed time from child process*/
msgCheck = read(processes[nProcess + 1 ].fd[R], &processes[targetProcess].time, sizeof(double));
if(msgCheck < 0)
{
printf("An error occured from the process %d while reading message from parent\n", getpid());
return 1;
}
processes[targetProcess].done = true;
/*Check if remainder to start new task*/
if(remainder != 0)
{
startPoint = taskLenght * nProcess;
processes[targetProcess].done = false;
if (write(processes[targetProcess].fd[W], "sum", MSGSIZE) < 0)
{
printf("An error occured from the process %d while sending message to parent\n", getpid());
return 1;
}
if (write(processes[targetProcess].fd[W], &startPoint, sizeof(int)) < 0)
{
printf("An error occured from the process %d while sending message to parent\n", getpid());
return 1;
}
if (write(processes[targetProcess].fd[W], &remainder, sizeof(int)) < 0)
{
printf("An error occured from the process %d while sending message to parent\n", getpid());
return 1;
}
remainder = 0; //Avoid looping task
}
/*Check for pending response and process final result*/
for (int i = 0; i < nProcess; i++)
{
if(processes[i].done)
{
chunk.result += processes[i].result;
chunk.time += processes[i].time;
onGoing = false;
continue;
}
onGoing = true;
/*Reset total calculations*/
chunk.result = 0;
chunk.time = 0;
break;
}
/*Reset to self target*/
targetProcess = nProcess + 1;
}
printf("Parent calculated: %ld in = %fs\n", processes[nProcess + 1].result, processes[nProcess + 1].time); //! Debug
printf("Processes calculated: %ld in = %fs\n", chunk.result, chunk.time); //! Debug
}
}
cResult arraySum(int start, int length, int* dataset)
{
/*Time unit to calculate the computation time*/
clock_t tic, toc = 0;
cResult chunk = {.result = 0, .time =0};
tic = clock(); // Start measure time
for (int i = start; i < start + length; i++)
{
chunk.result += dataset[i];
}
toc = clock(); // Stop measure time
chunk.time = ((double) (toc - tic))/ CLOCKS_PER_SEC;
printf("start at: %d, length: %d, sum: %ld, in: %f\n", start, length, chunk.result, chunk.time); //! Debug
return chunk;
}
If you want to try this out you'll find in USER DEFINED some variable to play with.
I'll share some of my results, with seed = 1.
LENGTH
PARENT
1 CHILD
5 CHILDREN
10 CHILDREN
1'000
0.000001
0.000002
0.000003
0.000006
100'000
0.000085
0.000107
0.000115
0.000120
10'000'000
0.008693
0.015143
0.016120
0.015982
100'000'000
0.089563
0.148095
0.146698
0.149421
500'000'000
0.669474
0.801828
0.744381
0.816883
As you can see even repeating the task in a single child process in some cases required twice the time.
As ArkadiuszDrabczyk pointed out, it could be a scheduling issue, still why has always to be the child the slowest one?
UPDATE 2:
Since pipe's problems arose, I wrote another source just to exclude these concerns and, of course, the problem remained.
However I wasn't convinced about the deep-copy of the dataset[] array stored on heap hence some research later I found this: What is copy-on-write?. After this new found knowledge, I've added this useless function to be called right before the arraySum() :
void justCheck(int start, int length, int* dataset)
{
for (int i = start; i < start + length; i++)
{
dataset[i] = dataset[i];
}
return;
}
This little snippet managed to level the differences between times. At least now they lay on the same order of magnitude.
Down the results:
LENGTH
PARENT
1 CHILD
5 CHILDREN
10 CHILDREN
1'000
0.000002
0.000001
0.000004
0.000003
100'000
0.000099
0.000110
0.000124
0.000121
10'000'000
0.009496
0.008686
0.009316
0.009248
100'000'000
0.090267
0.092168
0.089862
0.093356
500'000'000
-
-
-
-
Unfortunately this edit rise another problem. With big set of data this function freeze. I know this isn't the right way to force the COW so... Any suggestions?
SHORT: Turns out that in order to prove the thesis of the project I was using the wrong approach. Summing all of the clock times taken by each processes and comparing to the single one is like trying to prove that baking 4 pieces of bread in a row takes less than baking all simultaneously counting the oven time instead of watching my watch. I should have measured the wall time instead of clock time.
LONG: That said, regarding why the same function called by only one child takes more time than parent: As I said in my 2nd update I've managed to trim time to a more plausible value forcing the copy-on-write of used heap on child memory page before calling the actual calculating function. So after several experiments, turns out it's aleatory. Sometimes it's faster, sometimes is almost equal and sometimes slower. So, I thinks it depends on how the task scheduling works, which I don't have control on it neither know how to.
Talking about the other problem mentioned:
Unfortunately this edit rise another problem. With big set of data this function freeze. I know this isn't the right way to force the COW so... Any suggestions?
Well the point is that forcing COW on each processes means that, according to Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne, if I can fit ~
256mln INT value in 1GB of memory the problem is the massive devour of memory my program cause. Checking with my system monitor, I could validate that using 500mln values into my array result in almost 2GB of RAM taken. Which once fork it became equal to nProcess + 1.
I thought to answer these question will help posterity.
Essentially, my program creates 3 threads. The "server" and 2 "workers." The workers are meant to sum the 3 digit positive integers in a 1000 line file (500 numbers per thread). After each worker has summed its part, the server prints each workers total. The only problem is my semaphores are not seeming to work.
Here is a version of my program:
// simple c program to simulate POSIX thread and semaphore
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <semaphore.h>
// define semaphores
sem_t s1;
FILE *file;
int sum1 = 0, sum2 = 0, num1 = 0, num2 = 0;
// file name
char fileName[10] = "data1.dat";
// server routine
void* server_routine()
{
printf("Server sent signal to worker thread 1\n");
printf("Server sent signal to worker thread 2\n");
sem_wait(&s1);
printf("Server recieved completion signal from worker thread 1\n");
sem_wait(&s1);
printf("Server recieved completion signal from worker thread 2\n\n");
// print the final results
printf("The sum of the first 500 numbers in the file is: %d\n", sum1);
printf("The sum of the last 500 numbers in the file is: %d\n\n", sum2);
pthread_exit(NULL);
}
// thread 1 reoutine
void* t1_routine()
{
printf("Thread 1 recieved signal from server\n");
file = fopen(fileName, "r");
for(int i = 0; i < 500; i++)
{
fscanf(file, "%d", &num1);
sum1 += num1;
}
printf("sum in thread 1: %d\n", sum1);
printf("Thread 1 sends completion signal to server\n");
sem_post(&s1);
pthread_exit(NULL);
}
// thread 2 routine
void* t2_routine()
{
printf("Thread 2 recieved signal from server\n");
file = fopen(fileName, "r");
fseek(file, 500 * 5, SEEK_SET);
for(int i = 0; i < 500; i++)
{
fscanf(file, "%d", &num2);
sum2 += num2;
}
printf("sum in thread 2: %d\n", sum2);
printf("Thread 2 sends completion signal to server\n");
sem_post(&s1);
pthread_exit(NULL);
}
// main function
int main(int argc, char *argv[])
{
// define threads
pthread_t server, t1, t2;
// initialize the semaphore
sem_init(&s1, 0, 0);
if(pthread_create(&server, NULL, &server_routine, NULL) != 0)
{
return 1;
}
if(pthread_create(&t1, NULL, &t1_routine, NULL) != 0)
{
return 2;
}
if(pthread_create(&t2, NULL, &t2_routine, NULL) != 0)
{
return 3;
}
if(pthread_join(server, NULL) != 0)
{
return 4;
}
if(pthread_join(t1, NULL) != 0)
{
return 5;
}
if(pthread_join(t2, NULL) != 0)
{
return 6;
}
// destroy semaphores
sem_close(&s1);
// exit thread
pthread_exit(NULL);
// end
return 0;
}
I've tested with less threads more semaphores as well, with non luck. I've tried different initial semaphore values. The only time I can get the correct output is when I manually wait with sleep(5); but that defeats the purpose of this project.
A few issues ...
Each client thread does its own/private fopen but FILE *file; is global so they overwrite each others values.
We need to make this variable function scoped so each thread has its own private pointer.
There are no fclose calls.
pthread_exit should not be done by the main thread. It is only for threads created with pthread_create.
Otherwise ...
Whichever thread does the fopen last will set the final value.
So, there is a race condition and the effect is the same as if the main thread (prior to pthread_create calls) had done a single fopen, defeating the purpose of each thread doing its own fopen.
Worse yet, a given thread may do the first fopen, then start with fscanf and have its file value changed when the second thread replaces the file value, so weird stuff happens to each thread because they are doing fseek/fscanf on the same FILE * instance.
Having the aforementioned fclose calls would have made the issue more evident.
Here is the refactored code. It is annotated:
// simple c program to simulate POSIX thread and semaphore
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <semaphore.h>
// define semaphores
sem_t s1;
// NOTE/BUG: each thread opens a different stream, so this must be function
// scoped
#if 0
FILE *file;
#endif
int sum1 = 0,
sum2 = 0,
num1 = 0,
num2 = 0;
// file name
char fileName[10] = "data1.dat";
// server routine
void *
server_routine()
{
printf("Server sent signal to worker thread 1\n");
printf("Server sent signal to worker thread 2\n");
sem_wait(&s1);
printf("Server recieved completion signal from worker thread 1\n");
sem_wait(&s1);
printf("Server recieved completion signal from worker thread 2\n\n");
// print the final results
printf("The sum of the first 500 numbers in the file is: %d\n", sum1);
printf("The sum of the last 500 numbers in the file is: %d\n\n", sum2);
pthread_exit(NULL);
}
// thread 1 reoutine
void *
t1_routine()
{
// NOTE/FIX: this must be function scoped (i.e. private to this thread)
#if 1
FILE *file;
#endif
printf("Thread 1 recieved signal from server\n");
file = fopen(fileName, "r");
for (int i = 0; i < 500; i++) {
fscanf(file, "%d", &num1);
sum1 += num1;
}
printf("sum in thread 1: %d\n", sum1);
printf("Thread 1 sends completion signal to server\n");
sem_post(&s1);
#if 1
fclose(file);
#endif
pthread_exit(NULL);
}
// thread 2 routine
void *
t2_routine()
{
// NOTE/FIX: this must be function scoped (i.e. private to this thread)
#if 1
FILE *file;
#endif
printf("Thread 2 recieved signal from server\n");
file = fopen(fileName, "r");
fseek(file, 500 * 5, SEEK_SET);
for (int i = 0; i < 500; i++) {
fscanf(file, "%d", &num2);
sum2 += num2;
}
printf("sum in thread 2: %d\n", sum2);
printf("Thread 2 sends completion signal to server\n");
sem_post(&s1);
#if 1
fclose(file);
#endif
pthread_exit(NULL);
}
// main function
int
main(int argc, char *argv[])
{
// define threads
pthread_t server, t1, t2;
// initialize the semaphore
sem_init(&s1, 0, 0);
if (pthread_create(&server, NULL, &server_routine, NULL) != 0) {
return 1;
}
if (pthread_create(&t1, NULL, &t1_routine, NULL) != 0) {
return 2;
}
if (pthread_create(&t2, NULL, &t2_routine, NULL) != 0) {
return 3;
}
if (pthread_join(server, NULL) != 0) {
return 4;
}
if (pthread_join(t1, NULL) != 0) {
return 5;
}
if (pthread_join(t2, NULL) != 0) {
return 6;
}
// destroy semaphores
sem_close(&s1);
// exit thread
// NOTE/BUG: only a subthread should do this
#if 0
pthread_exit(NULL);
#endif
// end
return 0;
}
In the code above, I've used cpp conditionals to denote old vs. new code:
#if 0
// old code
#else
// new code
#endif
#if 1
// new code
#endif
Note: this can be cleaned up by running the file through unifdef -k
UPDATE:
Thank you for the response Craig. I have implemented your suggestions to my own code but nothing seemed to change. I then decided to copy paste your updated code into a c file to test it out and I got the same result. It is as follows (in a separate comment since the output is too long): –
Max
It's hard to compare results because we're using different datasets. I created a perl script to create some data.
Most important is that the sum reported by the given worker matches what the server sees for that worker task.
Then, if we know what each per thread section of the file should sum to, that is another matter.
The per line termination is critical (e.g.): CRLF vs LF (see below)
The actual order of worker sem_post and termination doesn't really matter. It can vary system to system or, even, invocation to invocation. What matters is that the server thread waits for N threads (i.e.) N sem_wait calls before printing any sums.
I've produced an updated version below.
Server does not "signal" a worker. The worker "signals" the server by doing sem_post and the server "receives" it by doing sem_wait
I've create a task/thread struct to hold the sums, thread IDs, etc.
I've generalized the code to allow N threads.
Added check of \n placement (i.e. line width). That is, under linux/POSIX a four digit number would be followed by LF (newline) and length would be 5. But, under windows, it would be CRLF (carriage return/newline) and length would be 6.
Added check of file size to ensure it is exactly the desired/expected length.
Some additional diagnostics.
Here is the updated code:
// simple c program to simulate POSIX thread and semaphore
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <semaphore.h>
#include <sys/stat.h>
// number of bytes per line
// 5: 4 digits + LF
// 6: 4 digits + CRLF
#ifndef LINEWID
#define LINEWID (4 + 1)
#endif
// number of items / task
#ifndef COUNT
#define COUNT 500
#endif
// define semaphores
sem_t s1;
#if 0
int sum1 = 0,
sum2 = 0,
num1 = 0,
num2 = 0;
#endif
// file name
#if 0
char fileName[10] = "data1.dat";
#else
const char *fileName = "data1.dat";
#endif
// task control
typedef struct {
pthread_t tid; // thread ID
int tno; // thread index/offset
int sum; // sum
} tsk_t;
#define TSKMAX 50
int tskmax; // actual number of tasks
tsk_t tsklist[TSKMAX]; // list of tasks
// loop through all task blocks
#define TSKFORALL \
tsk_t *tsk = &tsklist[0]; tsk < &tsklist[tskmax]; ++tsk
// server routine
void *
server_routine(void *vp)
{
// NOTE/BUG: server does _not_ signal worker
#if 0
printf("Server sent signal to worker thread 1\n");
printf("Server sent signal to worker thread 2\n");
#endif
for (TSKFORALL) {
printf("Server waiting ...\n");
sem_wait(&s1);
printf("Server complete ...\n");
}
// print the final results
for (TSKFORALL)
printf("The sum of task %d is %d\n",tsk->tno,tsk->sum);
return (void *) 0;
}
// thread 1 reoutine
void *
worker_routine(void *vp)
{
FILE *file;
tsk_t *tsk = vp;
printf("Thread %d running ...\n",tsk->tno);
file = fopen(fileName, "r");
fseek(file,tsk->tno * COUNT * LINEWID,SEEK_SET);
tsk->sum = 0;
int num1;
int first = -1;
int last = -1;
for (int i = 0; i < COUNT; i++) {
if (fscanf(file, "%d", &num1) != 1) {
printf("Thread %d fscan error\n",tsk->tno);
break;
}
if (i == 0)
first = num1;
if (i == (COUNT - 1))
last = num1;
tsk->sum += num1;
}
printf("sum in thread %d: %d (first %d, last %d)\n",
tsk->tno, tsk->sum, first, last);
sem_post(&s1);
#if 1
fclose(file);
#endif
return (void *) 0;
}
// main function
int
main(int argc, char **argv)
{
--argc;
++argv;
setlinebuf(stdout);
setlinebuf(stderr);
if (argc != 1)
tskmax = 2;
else
tskmax = atoi(*argv);
if (tskmax > TSKMAX)
tskmax = TSKMAX;
// define threads
pthread_t server;
printf("main: %d tasks\n",tskmax);
printf("main: %d count\n",COUNT);
FILE *file = fopen(fileName,"r");
if (file == NULL) {
printf("main: fopen failure\n");
exit(96);
}
// check alignment
char chr;
fseek(file,LINEWID - 1,0);
fread(&chr,1,1,file);
if (chr != '\n') {
printf("main: newline mismatch -- chr=%2.2X\n",chr);
exit(97);
}
// get the file size
struct stat st;
if (fstat(fileno(file),&st) < 0) {
printf("main: fstat fault\n");
exit(97);
}
// ensure the file has the correct size
off_t size = tskmax * LINEWID * COUNT;
if (st.st_size != size)
printf("main: wrong file size -- st_size=%llu size=%llu\n",
(unsigned long long) st.st_size,
(unsigned long long) size);
fclose(file);
// initialize the semaphore
sem_init(&s1, 0, 0);
// set the offsets
int tno = 0;
for (TSKFORALL, ++tno)
tsk->tno = tno;
if (pthread_create(&server, NULL, &server_routine, NULL) != 0)
return 98;
for (TSKFORALL) {
if (pthread_create(&tsk->tid,NULL,worker_routine,tsk) != 0)
return 1 + tsk->tno;
}
if (pthread_join(server, NULL) != 0) {
return 99;
}
for (TSKFORALL) {
if (pthread_join(tsk->tid, NULL) != 0) {
return 5;
}
}
// destroy semaphores
sem_close(&s1);
// end
return 0;
}
Here is the perl script output that I used to generate the data:
number of tasks 2
element count per task 500
line separater 0A
section 0 sum 124750
section 1 sum 125250
Here is the program output:
main: 2 tasks
Server waiting ...
Thread 0 running ...
Thread 1 running ...
sum in thread 1: 125250 (first 1, last 500)
sum in thread 0: 124750 (first 0, last 499)
Server complete ...
Server waiting ...
Server complete ...
The sum of task 0 is 124750
The sum of task 1 is 125250
Here is the perl script:
#!/usr/bin/perl
# gendata -- generate data
#
# arguments:
# 1 - number of tasks (DEFAULT: 2)
# 2 - number of items / task (DEFAULT: 500)
# 3 - line separater (DEFAULT: \n)
master(#ARGV);
exit(0);
# master -- master control
sub master
{
my(#argv) = #_;
$tskmax = shift(#argv);
$tskmax //= 2;
printf(STDERR "number of tasks %d\n",$tskmax);
$count = shift(#argv);
$count //= 500;
printf(STDERR "element count per task %d\n",$count);
$sep = shift(#argv);
$sep //= "\n";
printf(STDERR "line separater");
foreach $chr (split(//,$sep)) {
$hex = ord($chr);
printf(STDERR " %2.2X",$hex);
}
printf(STDERR "\n");
for ($itsk = 0; $itsk < $tskmax; ++$itsk) {
$val = $itsk;
$sum = 0;
for ($lno = 1; $lno <= $count; ++$lno, ++$val) {
printf("%4d%s",$val,$sep);
$sum += $val;
}
printf(STDERR "section %d sum %d\n",$itsk,$sum);
}
}
EDIT:
Thanks for the suggestions given so far. I changed the program and now the parent handles some of the signals, but it looks like it doesn't handle all of them. New code and results are posted below.
EDIT2:
I changed the random number generation as proposed. Now the parent catches only two signals but it always catches the right bits (two last bits).
"Unfortunately, I am not experienced in C POSIX and I have to write a program that will take one argument (filename containing a binary number) and parse this file. Each bit denoted in the file means that one child should be created (each bit is dedicated to one child). The value of the bit (0 or 1) decides which signals should be sent to the parent (0 - SIGUSR1, 1 - SIGUSR2).
The child process should choose a random interval (10-200ms) and send appropriate signal to the parent.
The parent should receive the signals and print the last 5 bits received every time a new signal arrives.
The final step is the matching process - the parent checks the received signals (bits assigned to SIGUSR1 or SIGUSR2) and if there's a match it prints SUCCESS. If there's no match (whenever a wrong bit is sent - compared to the file) the parent starts matching from the beginning."
The updated version:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <signal.h>
volatile sig_atomic_t last_signal = 0;
char * str;
char * received;
int count = 0;
#define ERR(source) (fprintf(stderr,"%s:%d\n",__FILE__,__LINE__),\
perror(source),kill(0,SIGKILL),\
exit(EXIT_FAILURE))
void sethandler( void (*f)(int), int sigNo)
{
struct sigaction act;
memset(&act, 0, sizeof(struct sigaction));
act.sa_handler = f;
if (-1==sigaction(sigNo, &act, NULL)) ERR("sigaction");
}
char *readFile(char *fileName)
{
FILE *file = fopen(fileName, "r");
char *code;
size_t n = 0;
int c;
if (file == NULL) return NULL; //could not open file
fseek(file, 0, SEEK_END);
long f_size = ftell(file);
fseek(file, 0, SEEK_SET);
code = malloc(f_size);
received = malloc(f_size);
while ((c = fgetc(file)) != EOF) {
code[n++] = (char)c;
}
code[n] = '\0';
return code;
}
void append(char* s, char c)
{
int len = strlen(s);
s[len] = c;
s[len+1] = '\0';
}
static void sig_handle(int signum)
{
last_signal = signum;
}
void child_w(int number_of)
{
if(str[number_of] == '0')
{
if (kill(getppid(), SIGUSR1)==0) printf("[SIGUSR1] sent \n");
else
{
printf("ERROR kill. \n");
exit(EXIT_FAILURE);
}
}
if(str[number_of] == '1')
{
if (kill(getppid(), SIGUSR2) == 0) printf("[SIGUSR2] sent \n");
else
{
printf("ERROR kill. \n");
exit(EXIT_FAILURE);
}
}
}
void create_children(int n)
{
pid_t s;
int j = n;
int time = rand() % 191 + 10; // range 10 - 200
struct timespec time_wait = { .tv_sec = 0, .tv_nsec = time * 1000000L };
while(j-->0)
{
nanosleep(&time_wait, NULL);
if((s=fork())<0) ERR("Fork ERROR");
if(!s) {
printf("Child %d started ", j);
printf("with bit: %c \n", str[j]);
child_w(j);
exit(EXIT_SUCCESS);
}
}
}
void parent_w(sigset_t oldmask)
{
int count = 0;
int match = 0;
while(1)
{
last_signal = 0;
while(last_signal != SIGUSR1 && last_signal != SIGUSR2)
{
sigsuspend(&oldmask);
}
printf("\n");
if(last_signal == SIGUSR1)
{
received[count] = '0';
for(int i=0; i<sizeof(received); ++i)
{
printf("%c ", received[i]);
}
count++;
}
else if(last_signal == SIGUSR2)
{
received[count] = '1';
for(int i=0; i<sizeof(received); ++i)
{
printf("%c ", received[i]);
}
count++;
}
printf("\n");
}
}
int main(int argc, char ** argv)
{
char filename[250];
if(argc!=2)
{
printf("Provide one parameter - filename. \n");
return EXIT_FAILURE;
}
strcpy(filename, argv[1]);
str = readFile(filename);
printf("FILE: ");
for(int i=0; i<sizeof(str); ++i)
{
printf("%c ", str[i]);
}
printf("\n");
for(int i=0; i<sizeof(received); ++i)
{
received[i] = '-';
}
sethandler(sig_handle, SIGUSR1);
sethandler(sig_handle, SIGUSR2);
sigset_t mask, oldmask;
sigemptyset(&mask);
sigaddset(&mask, SIGUSR1);
sigaddset(&mask, SIGUSR2);
sigprocmask(SIG_BLOCK, &mask, &oldmask);
create_children(sizeof(str));
parent_w(oldmask);
sigprocmask(SIG_UNBLOCK, &mask, NULL);
free(str);
free(received);
return EXIT_SUCCESS;
}
Now the output always looks like this:
FILE: 1 0 0 1 1 0 1 0
Child 7 started with bit: 0
[SIGUSR1] sent
Child 6 started with bit: 1
[SIGUSR2] sent
Child 5 started with bit: 0
[SIGUSR1] sent
Child 4 started with bit: 1
[SIGUSR2] sent
Child 3 started with bit: 1
[SIGUSR2] sent
Child 2 started with bit: 0
[SIGUSR1] sent
Child 1 started with bit: 0
[SIGUSR1] sent
0 - - - - - - -
Child 0 started with bit: 1
[SIGUSR2] sent
0 1 - - - - - -
Any further suggestions will be appreciated :).
In addtition to the problems mentioned by others, your readFile() function invokes undefined behavior by overrunning the buffer you allocate for the file contents:
char *readFile(char *fileName)
{
FILE *file = fopen(fileName, "r");
char *code;
size_t n = 0;
int c;
if (file == NULL) return NULL; //could not open file
fseek(file, 0, SEEK_END);
long f_size = ftell(file);
fseek(file, 0, SEEK_SET);
code = malloc(f_size);
received = malloc(f_size);
while ((c = fgetc(file)) != EOF) {
code[n++] = (char)c;
}
code[n] = '\0'; // <- this is f_size + 1 bytes into the code array
return code;
}
When you terminate the data with code[n] = '\0'; you write past the end of the buffer code points to, thus invoking undefined behavior.
And, off-topic...
Strictly speaking you can't use fseek()/ftell() to get the size of a file. In your case, you're opening the file in text mode with FILE *file = fopen(fileName, "r");, but in text mode ftell() does not return a byte offset. Per 7.21.9.4 The ftell function, paragraph 2 of the C11 standard:
The ftell function obtains the current value of the file position indicator for the stream pointed to by stream. For a binary stream, the value is the number of characters from the beginning of the file. For a text stream, its file position indicator contains unspecified information, usable by the fseek function for returning the file position indicator for the stream to its position at the time of the ftell call; the difference between two such return values is not necessarily a meaningful measure of the number of characters written or read.
On POSIX systems, you won't have a problem as POSIX defines ftell() to always return an accurate byte offset. But on Windows you will likely read fewer bytes than the file size would otherwise indicate as the \r\n character sequence actually in the file contents is read as a single \n character.
But on some systems you will truly get "unspecified information" and your code will fail completely.
And seeking to the end of a binary stream isn't portable either. In fact, it's explicitly undefined behavior:
Setting the file position indicator to end-of-file, as with fseek(file, 0, SEEK_END), has undefined behavior for a binary stream...
Again, not a problem on POSIX or Windows systems.
One real problem with fseek()/ftell(), though, is that the long value returned from ftell() doesn't have enough range on many systems to represent file sizes larger than 2 GB. long is 32 bits on 32-bit Linux systems, and its also only 32 bits on all Windows systems, both 32- and 64-bit.
As #KamilCuk observed, it is not necessary or appropriate to re-register signal handlers when they fire. That was standard at one time because of uncertainty about the implementation of the signal() function (which remains to this day): some implementations register handlers such that after they fire once, the signal disposition is reset. With sigaction(), however, once can specify whether they want that "one-shot" behavior or whether they instead want the signal handler to remain registered when it fires, with the latter being the default with that function.
sigaction() allows control of some other details on which signal() implementations vary. In practice, signal() has very few appropriate uses, and none that sigaction() cannot also cover. If you're programming for POSIX then its best to forget that signal() exists.
With all that said, however, I don't think your usage of signal() is the key problem here.
Another issue is that signal handlers are rather restricted in what they may safely do:
they may access file-scope variables of type sig_atomic_t
they may call async-signal-safe standard functions
they may declare and access local variables
they may call any of the program's other functions that comply with these restrictions
HOWEVER, signal handlers are typically called with their own, separate stacks, and these are often very small, so in practice, they cannot safely declare very much in the way of local variables, nor start a very deep call tree. Exactly what the limits are is unspecified, so signal handlers should generally do as little as possible.
In particular, neither printf() nor any of the other stdio functions are async-signal safe. Signal handlers produce undefined behavior if they call any of them. They may call write() if you wish, but there is probably a better alternative here. For example, the parent could pause() or sigsuspend() to await a signal, and then print whatever it needs to do outside the handler. The handler need only set a variable to indicate which signal was received. This will avoid the parent busy-waiting as it presently does, though you still have an issue with potential collisions.
This is more likely to be part of your problem, but I suspect that it's not the key issue, either.
I think the real problem is probably that signals are being lost. Ordinary signals do not queue, so if a signal is received while that signal is already pending for the process then it has no additional effect. The problem is structured to avoid that by asking for each child to delay a random amount of time before firing its signal, but
That's not truly safe, just less likely to manifest a collision, and
Your implementation does not actually delay.
Consider:
int time = rand()%100 + 10;
struct timespec time_wait = {time/1000, 0};
nanosleep(&time_wait, NULL);
Variable time will be assigned a value between 10 and 109, so time / 1000 -- an integer division -- will always evaluate to 0.
Something like this would be more appropriate:
int time = rand() % 191 + 10; // range 10 - 200
struct timespec time_wait = { .tv_sec = 0, .tv_nsec = time * 1000000L };
nanosleep(&time_wait, NULL);
Additionally, instead of seeding a separate (P)RNG in each child, I would seed one, once, in the parent, and generate the delays there, before each fork. Drawing random numbers from the same RNG produces a more uniform distribution.
First of all, good code.
Second:
man pages are your friends. man signal
signal() registers handler for the signal. So after signal(SIGUSR1, some_function) the function some_function will be executed after the signal is received.
Removoe signal() calls from signal handlers (why would you re-register the same handler from inside the handler? It already is the handler for this signal.)
Remove signal() call from the loop in parent. Just register functions once, that's all.
Your sethandler function is the same as signal.
After some fixing:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <signal.h>
char * str; // the array consisting of bits from the file
char * received; //array of the parent receiving the signals
int count = 0;
//Error macro
#define ERR(source) (fprintf(stderr,"%s:%d\n",__FILE__,__LINE__),\
perror(source),exit(EXIT_FAILURE))
//Reading file char by char and returning allocated char array
char *readFile(char *fileName)
{
FILE *file = fopen(fileName, "r");
char *code;
size_t n = 0;
int c;
if (file == NULL) return NULL; //could not open file
fseek(file, 0, SEEK_END);
long f_size = ftell(file);
fseek(file, 0, SEEK_SET);
code = malloc(f_size);
received = malloc(f_size);
while ((c = fgetc(file)) != EOF) {
code[n++] = (char)c;
}
code[n] = '\0';
return code;
}
// Append the character to the received array
void append(char* s, char c)
{
int len = strlen(s);
s[len] = c;
s[len+1] = '\0';
}
// SIGUSR1 handler. I tried to implement simple counter to check if the parent is receiving the signals, then proceed to printing last 5 bits received. Unfortunately this part seems to not work at all.
static void sig_handle1(int signum) {
count++;
printf("%s %d \n", __func__, count);
}
// Handler for SIGUSR2 - same as handler for SIGUSR1
static void sig_handle2(int signum) {
count++;
printf("%s %d \n", __func__, count);
}
// Child function - set the random interval, wait and then send the appropriate signal to the parent
void child_w(int number_of)
{
srand(time(NULL)*getpid());
int time = rand()%100 + 10;
struct timespec time_wait = {time/1000, 0};
nanosleep(&time_wait, NULL);
if(str[number_of] == '0')
{
if (kill(getppid(), SIGUSR1)==0) printf("[SIGUSR1] sent \n");
else
{
printf("ERROR kill. \n");
exit(EXIT_FAILURE);
}
}
if(str[number_of] == '1')
{
if (kill(getppid(), SIGUSR2) == 0) printf("[SIGUSR2] sent \n");
else
{
printf("ERROR kill. \n");
exit(EXIT_FAILURE);
}
}
}
// Function which will create children (number of children = number of bits in the file)
void create_children(int n)
{
pid_t s;
int j = n;
while(j-->0)
{
if((s=fork())<0) ERR("Fork ERROR");
if(!s) {
printf("Child %d started ", j);
printf("with bit: %c \n", str[j]);
child_w(j);
//if(j==1) kill(getppid(), SIGUSR2);
exit(EXIT_SUCCESS);
}
}
}
// Parent function to check the received signals
void parent_w()
{
signal(SIGUSR1, sig_handle1);
signal(SIGUSR2, sig_handle2);
while(1)
{
pause();
}
}
int main(int argc, char ** argv)
{
char filename[250];
if(argc!=2)
{
printf("Provide one parameter - filename. \n");
return EXIT_FAILURE;
}
strcpy(filename, argv[1]);
str = readFile(filename);
printf("FILE: ");
for(int i=0; i<sizeof(str); ++i)
{
printf("%c ", str[i]);
}
printf("\n");
create_children(sizeof(str)-1);
parent_w();
free(str);
free(received);
return EXIT_SUCCESS;
}
Example exeuction:
FILE: 1 0 0 1 1 0 1
Child 0 started with bit: 1
Child 1 started with bit: 0
Child 2 started with bit: 0
Child 3 started with bit: 1
[SIGUSR2] sent
sig_handle2 1
[SIGUSR1] sent
sig_handle1 2
[SIGUSR1] sent
sig_handle1 3
[SIGUSR2] sent
sig_handle2 4
Child 4 started with bit: 1
Child 5 started with bit: 0
sig_handle2 5
[SIGUSR2] sent
Child 6 started with bit: 1
sig_handle1 6
[SIGUSR1] sent
sig_handle2 7
[SIGUSR2] sent
^C
I am working with a program to write the summation of 1-500 and 500-1000 using two separate threads. I need the output to be written in to a text file which is created by the program itself. When I run the program it creates the file according to the given name, but I am not getting the output as needed. It only writes one single line to the text file. That is the summation of 500-1000. But when I get the output using console it shows the answer as needed. How to overcome this problem. Thanks!
#include <stdio.h>
#include <pthread.h>
#include <fcntl.h>
#include <stdlib.h>
#define ARRAYSIZE 1000
#define THREADS 2
void *slave(void *myid);
/* shared data */
int data[ARRAYSIZE]; /* Array of numbers to sum */
int sum = 0;
pthread_mutex_t mutex;/* mutually exclusive lock variable */
int wsize; /* size of work for each thread */
int fd1;
int fd2;
FILE * fp;
char name[20];
/* end of shared data */
void *slave(void *myid)
{
int i,low,high,myresult=0;
low = (int) myid * wsize;
high = low + wsize;
for(i=low;i<high;i++)
myresult += data[i];
/*printf("I am thread:%d low=%d high=%d myresult=%d \n",
(int)myid, low,high,myresult);*/
pthread_mutex_lock(&mutex);
sum += myresult; /* add partial sum to local sum */
fp = fopen (name, "w+");
//printf("the sum from %d to %d is %d",low,i,myresult);
fprintf(fp,"the sum from %d to %d is %d\n",low,i,myresult);
printf("the sum from %d to %d is %d\n",low,i,myresult);
fclose(fp);
pthread_mutex_unlock(&mutex);
return;
}
main()
{
int i;
pthread_t tid[THREADS];
pthread_mutex_init(&mutex,NULL); /* initialize mutex */
wsize = ARRAYSIZE/THREADS; /* wsize must be an integer */
for (i=0;i<ARRAYSIZE;i++) /* initialize data[] */
data[i] = i+1;
printf("Enter file name : \n");
scanf("%s",name);
//printf("Name = %s",name);
fd1=creat(name,0666);
close(fd1);
for (i=0;i<THREADS;i++) /* create threads */
if (pthread_create(&tid[i],NULL,slave,(void *)i) != 0)
perror("Pthread_create fails");
for (i=0;i<THREADS;i++){ /* join threads */
if (pthread_join(tid[i],NULL) != 0){
perror("Pthread_join fails");
}
}
}
Its because you are opening the same file two times, one on each thread. They are overwriting each other's job.
To solve this you can:
Use the a+ mode on fopen() to append the new line to end of the existing file, or;
Open the file in main() and the threads will only fprintf() to it.
I am trying to get the example code to work such that multiple threads will calculate the sum of successive prime numbers (note that the original author's algorithm for successive prime calculation is very inefficient). So far, running unit tests shows that the output is inconsistent, i.e. it will change slightly each time I run the program. I will post the modified source code in C, along with output for debugging purposes.
Source:
/************************************************************************
* Code listing from "Advanced Linux Programming," by CodeSourcery LLC *
* Copyright(C) 2001 by New Riders Publishing *
* See COPYRIGHT for license information. *
***********************************************************************/
/*
* Modified By : Dylan Gleason
* Class : CST 352 - Operating Systems
* Date : 10/18/2012
*/
#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
#define DEBUG 0 /* Set to 1 to enable debug statements */
/* global variables to be accessed by each thread */
int current_sum = 2;
int primes_to_compute = 0;
/* create mutex for ensuring serial access to global data */
int thread_flag;
pthread_cond_t cond;
pthread_mutex_t lock;
/* print the thread info for debugging purposes */
void print_thread_info()
{
printf("Current thread ID : %u\n",(unsigned int*)pthread_self());
printf("Current sum of primes : %d\n", current_sum);
printf("Current prime to compute : %d\n\n", primes_to_compute);
}
/* initialize the mutex and return an integer value to determine if
initialization failed or not */
int initialize_mutex()
{
int success = 1;
if(pthread_mutex_init(&lock, NULL) == 0 &&
pthread_cond_init(&cond, NULL) == 0)
success = 0;
thread_flag = 0;
return success;
}
/* set the value of the wait thread flag to the value which the client
passes */
void set_thread_flag(int is_waiting)
{
pthread_mutex_lock(&lock); /* lock mutex */
/* set the wait flag value, and then signal in case the prime
function is blocked, waiting for flag to become set. However,
prime function can't actually check flag until the mutex is
unlocked */
thread_flag = is_waiting;
pthread_cond_signal(&cond);
pthread_mutex_unlock(&lock); /* unlock mutex */
}
void in_wait()
{
while(!thread_flag)
pthread_cond_wait(&cond, &lock);
}
/* Compute successive prime numbers(very inefficiently). Return the
Nth prime number, where N is the value pointed to by *ARG. */
void* compute_prime(void* arg)
{
while(1)
{
pthread_mutex_lock(&lock);
in_wait();
pthread_mutex_unlock(&lock);
int sum;
int factor;
int is_prime = 1;
set_thread_flag(0);
pthread_mutex_lock(&lock);
sum = current_sum;
if(DEBUG)
{
printf("First lock\n");
print_thread_info();
}
pthread_mutex_unlock(&lock);
set_thread_flag(1); /* tell next thread to go! */
/* wait until ready-flag is released from current thread */
pthread_mutex_lock(&lock);
in_wait();
pthread_mutex_unlock(&lock);
/* Test primality by successive division. */
for(factor = 2; factor < sum; ++factor)
{
if(sum % factor == 0)
{
is_prime = 0;
break;
}
}
/* Is this the prime number we're looking for? */
if(is_prime)
{
int number;
set_thread_flag(0);
pthread_mutex_lock(&lock);
/* only decrement primes_to_compute if is greater than zero! */
if(primes_to_compute > 0)
{
--primes_to_compute;
}
if(DEBUG)
{
printf("Second lock\n");
print_thread_info();
}
number = primes_to_compute;
pthread_mutex_unlock(&lock);
set_thread_flag(1);
pthread_mutex_lock(&lock);
in_wait();
pthread_mutex_unlock(&lock);
if(number == 0)
{
set_thread_flag(0);
pthread_mutex_lock(&lock);
void* sum =(void*) current_sum;
if(DEBUG)
{
printf("Third lock\n");
print_thread_info();
}
pthread_mutex_unlock(&lock);
set_thread_flag(1);
return sum;
}
}
set_thread_flag(0);
pthread_mutex_lock(&lock);
++current_sum;
if(DEBUG)
{
printf("Fourth lock\n");
print_thread_info();
}
pthread_mutex_unlock(&lock);
set_thread_flag(1);
}
return NULL;
}
int main(int argc, char* argv[])
{
int prime;
pthread_t tid[5];
/* Check command-line argument count */
if(argc != 2)
{
printf("Error: wrong number of command-line arguments\n");
printf("Usage: %s <integer>\n", argv[0]);
exit(1);
}
/* Check to see if mutex initialized correctly */
if(initialize_mutex() != 0)
{
printf("Mutex initialization failed.\n");
exit(1);
}
primes_to_compute = atoi(argv[1]);
printf("Successive primes to be computed: %d\n\n", primes_to_compute);
/* Execute five different threads to calculate the prime summation */
int t = 0;
set_thread_flag(1);
for(t; t < 5; ++t)
pthread_create(&tid[t], NULL, &compute_prime, NULL);
/* Wait for the prime number thread to complete, then get result. */
t = 0;
for(t; t < 5; ++t)
pthread_join(tid[t],(void*) &prime);
/* Print the largest prime it computed. */
printf("The %dth prime number is %d.\n", atoi(argv[1]), prime);
return 0;
}
Unit test (executing program five times):
Test successive primes up to 100:
Successive primes to be computed: 100
The 100th prime number is 547.
Successive primes to be computed: 100
The 100th prime number is 521.
Successive primes to be computed: 100
The 100th prime number is 523.
Successive primes to be computed: 100
The 100th prime number is 499.
Successive primes to be computed: 100
The 100th prime number is 541.
Note that the output of non-threaded version if the number for successive primes to calculate is 100, the result will always be 541. Clearly I am not able to grok the correct usage of the mutexes above - if someone has more experience in this area I would be very grateful! Also, please note that I am not concerned with the efficiency/correctness of the actual prime number algorithm, but rather the algorithm for making sure that the threads execute properly with consistent results.
Ok, looking at your program I think I see what's going on.
You have a race condition, and a pretty bad one. Which number you're on is determined by the current_sum variable. You access it at the beginning of each loop, but don't increment it until the end of the loop. You need to set and then increment it at the same time within the same mutex lock, otherwise two different threads will be able to pull the same value, and if they pull the same prime value then that prime will be counted twice.
Hope this helps.