I'm doing an assignment that involves calculating pi with threads. I've done this using mutex and it works fine, but I would like to get this version working as well. Here is my code.
#include
#include
#include
#include
#include
using namespace std;
typedef struct{
int iterations; //How many iterations this thread is going to do
int offset; //The offset multiplier for the calculations (Makes sure each thread calculates a different part of the formula)
}threadParameterList;
vector partialSumList;
void* pi_calc(void* param){
threadParameterList* _param = static_cast(param);
double k = 1.0;
for(int i = _param->iterations * _param->offset + 1; i < _param->iterations * (_param->offset + 1); ++i){
partialSumList[_param->offset] += (double)k*(4.0/((2.0*i)*(2.0*i+1.0)*(2.0*i+2.0)));
k *= -1.0;
}
pthread_exit(0);
}
int main(int argc, char* argv[]){
//Error checking
if(argc != 3){
cout << "error: two parameters required [iterations][threadcount]" << endl;
return -1;
}
if(atoi(argv[1]) <= 0 || atoi(argv[2]) <= 0){
cout << "error: invalid parameter supplied - parameters must be > 0." << endl;
return -1;
}
partialSumList.resize(atoi(argv[2]));
vector threadList (atoi(argv[2]));
vector parameterList (atoi(argv[2]));
int iterations = atoi(argv[1]),
threadCount = atoi(argv[2]);
//Calculate workload for each thread
if(iterations % threadCount == 0){ //Threads divide evenly
for(int i = 0; i < threadCount; ++i){
parameterList[i].iterations = iterations/threadCount;
parameterList[i].offset = i;
pthread_create(&threadList[i], NULL, pi_calc, ¶meterList[i]);
}
void* status;
for(int i = 0; i < threadCount; ++i){
pthread_join(threadList[i], &status);
}
}
else{ //Threads do not divide evenly
for(int i = 0; i < threadCount - 1; ++i){
parameterList[i].iterations = iterations/threadCount;
parameterList[i].offset = i;
pthread_create(&threadList[i], NULL, pi_calc, ¶meterList[i]);
}
//Add the remainder to the last thread
parameterList[threadCount].iterations = (iterations % threadCount) + (iterations / threadCount);
parameterList[threadCount].offset = threadCount - 1;
pthread_create(&threadList[threadCount], NULL, pi_calc, ¶meterList[threadCount]);
void* status;
for(int i = 0; i < threadCount-1; ++i){
pthread_join(threadList[i], &status);
cout << status << endl;
}
}
//calculate pi
double pi = 3.0;
for(int i = 0; i < partialSumList.size(); ++i){
pi += partialSumList[i];
}
cout << "Value of pi: " << setw(15) << setprecision(15) << pi << endl;
return 0;
}
The code works fine in most cases. There are certain combinations of parameters that cause me to get a double free or corruption error on return 0
. For example, if I use the parameters 100
and 10
the program creates 10 threads and does 10 iterations of the formula on each thread, works fine. If I use the parameters 10
and 4
the program creates 4 threads that do 2 iterations on 3 threads and 4 on the 4th thread, works fine. However, if I use 5
and 3
, the program will correctly calculate the value and even print it out, but I get the error immediately after. This also happens for 17
and 3
, and 10
and 3
. I tried 15
and 7
, but then I get a munmap_chunk(): invalid pointer
error when the threads are trying to be joined - although i think that's something for another question.
If I had to guess, it has something to do with pthread_exit
deallocating memory and then the same memory trying to be deallocated again on return
, since I'm passing the parameter struct as a pointer. I tried a few different things like creating a local copy and defining parameterList
as a vector of pointers, but it didn't solve anything. I've also tried erase
ing and clear
ing the vector before return
but that didn't help either.
Answer
I see this issue:
You are writing beyond the vector's bounds:
vector parameterList (atoi(argv[2]));
//...
int threadCount = atoi(argv[2]);
//...
parameterList[threadCount].iterations = (iterations % threadCount) + (iterations / threadCount);
parameterList[threadCount].offset = threadCount - 1;
Accessing parameterList[threadCount]
is out of bounds.
I don't see in the code where threadCount
is adjusted, so it remains the same value throughout that snippet.
Tip: If the goal is to access the last item in a container, use vector::back()
. It works all the time for non-empty vectors.
parameterList.back().iterations = (iterations % threadCount) + (iterations / threadCount);
parameterList.back().offset = threadCount - 1;
No comments:
Post a Comment