Home > Mobile >  How to pass value to a Grandchild thread under a Child thread from Parent thread or Child thread in
How to pass value to a Grandchild thread under a Child thread from Parent thread or Child thread in

Time:11-03

I have a file.txt with the following values: 1234 567 8910

I want to create a program in c , that creates x number of child threads with x being the number of lines in "file.txt". The child thread receives the line so it splits the line into digits and stores them in an array.

Then I want to create y number of grandchild threads under each child thread with y being the number of digits in that child thread array or received line value and pass each grandchild thread a single digit.

For example, for the above file.txt, my parent thread in main() will create 3 child threads. The first child thread will receive "1234", second "567", third "8910". Then the first child will create 4 grandchild threads and pass the first grandchild thread "1", second "2", third "3", fourth "4". Similarly, the second child will create 3 grandchild threads and pass the "5", "6", "7" digits. And lastly, the third child will create 4 grandchild threads and pass the "8", "9", "1", "0" digits.

I want to pass all these values using multi-threading in parallel. I am able to get everything working fine till the child thread, and I am able to create the right amount of grandchild threads, but I am unable to pass them the values. Any help and guidance are much appreciated.

Here is my code: (please guide me to fix it)

#include <iostream>
#include <pthread.h>
#include <math.h>
#include <fstream>
#include <sys/wait.h>
#include <cstdlib>
#include <unistd.h>
using namespace std;

struct info {
    int* digit = new int;
    int* totalDigits = new int;
};

struct grandchildThreadData {
    int GCTDdigit;
    int GCTDgrandchildIndex;
    int GCTDchildIndex;
    info* GCTDinfo = new info;
    info* GCTDparentInfo = new info;
};

struct childThreadData {
    long int CTDlineValue;
    int CTDchildIndex;
    info* CTDinfo = new info;
};

// thread declaration
void* childThread(void*);
void* grandchildThread(void*);

// function to convert and store line to digits
int* digitSeparator(long int);

// MAIN PROGRAM
int main() {
    // FILE OPENING
    int totalLines{0};
    ifstream file1("file.txt"), file2("file.txt");
    string temp;
    while (getline(file1, temp)) {
        totalLines  ;
    }
    file1.close();
    long int* valueOnLine = new long int[totalLines];
    int i =0;
    while (!file2.eof()) {
        file2 >> valueOnLine[i];
        i  ;
    }
    file2.close();
    // FILE CLOSING

    // THREAD START

    static struct info* mainInfo = new info[totalLines];
    pthread_t* child = new pthread_t[totalLines];
    // will be used to pass values to child
    static struct childThreadData* Carg = new childThreadData[totalLines];

    // Creating Childthreads
    for (int i = 0; i < totalLines; i  ) {
        Carg[i].CTDinfo[i] = mainInfo[i];
        Carg[i].CTDchildIndex = i;
        Carg[i].CTDlineValue = valueOnLine[i];

        if (pthread_create(&child[i], nullptr, childThread, &Carg[i])) {
            fprintf(stderr, "Error creating thread\n");
            return 1;
        }
    }

    // Joining Childthreads
    for (int i = 0; i < totalLines; i  ) {
        if (pthread_join(child[i], nullptr)) {
            fprintf(stderr, "Error joining thread\n");
            return 2;
        }
    }

    delete[] valueOnLine;
    delete[] child;
    delete[] Carg;

    return 0;
}

void* childThread(void* i) {
    struct childThreadData* CTptr = (struct childThreadData*)i;

    int totalDigits = log10((float)CTptr->CTDlineValue)   1;
    int* numberArray = digitSeparator(CTptr->CTDlineValue);
 
    // THIS LINE WILL PUT TOTAL DIGITS INTO mainInfo
    CTptr->CTDinfo[CTptr->CTDchildIndex].totalDigits[0] = totalDigits;

    static struct info* childInfo = new info[totalDigits];  // This can be used to print modified info in grandchild
    pthread_t* grandchild = new pthread_t[totalDigits];
    static struct grandchildThreadData* GCarg = new grandchildThreadData[totalDigits];

    // THIS LINE WILL PUT EACH DIGIT ON CORRECT LOCATION of mainInfo
    for (int i=0; i< totalDigits; i  ) {
        CTptr->CTDinfo[CTptr->CTDchildIndex].digit[i] = numberArray[i];
    }

    // GRANDCHILD THREAD
    for (int i = 0; i < totalDigits; i  ) {
        GCarg[i].GCTDinfo[i] = childInfo[i];    // grandchild to child communication but does not work
        // or
        GCarg[i].GCTDparentInfo[i] = CTptr->CTDinfo[i]; // grandchild to parent communication but does not work
        GCarg[i].GCTDgrandchildIndex = CTptr->CTDchildIndex;    // Here CTptr->CTDchildIndex should pass 0, 1, 2 to grandchild but I get different values
        GCarg[i].GCTDchildIndex = i; // This line works fine for some reason
        GCarg[i].GCTDdigit = CTptr->CTDinfo[CTptr->CTDchildIndex].digit[i]; // This line should pass the correct digit, but again, I am getting different results in grandchild
     
        if (pthread_create(&grandchild[i], nullptr, grandchildThread, &GCarg[i])) {
            fprintf(stderr, "Error creating thread\n");
        }
    }

    //Joining GrandChildthreads
    for (int i = 0; i < totalDigits; i  ) {
        if (pthread_join(grandchild[i], nullptr)) {
            fprintf(stderr, "Error joining thread\n");
        }
    }

    return nullptr;
}

void* grandchildThread(void* i) {
    struct grandchildThreadData* GCTptr = (struct grandchildThreadData*)i;

    // THIS LINE SHOULD PRINT THE DIGIT
    cout << GCTptr->GCTDdigit;

    return nullptr;
}

int* digitSeparator(long int number) {
    int totalDigits = log10((float)number)   1;
    int* separatorPtr = new int[totalDigits];
    int j = 0;

    for (int i = totalDigits - 1; i >= 0; i--) {
        long int divisor = pow((float)10, i);
        long int digit = number / divisor;
        number -= digit * divisor;
        separatorPtr[j] = digit;
        j  ;
    }

    return separatorPtr;
}

CodePudding user response:

The issue was caused by using static structs:

So, if we replace:

static struct childThreadData* Carg = new childThreadData[totalLines];
static struct grandchildThreadData* GCarg = new grandchildThreadData[totalLines];

with:

childThreadData* Carg = new childThreadData[totalLines];
grandchildThreadData* GCarg = new grandchildThreadData[totalLines];

It will work fine.

CodePudding user response:

This is not an exhaustive list of the problems in the program but it deals some fundamental problems.

  • Your program does not check if file.txt is successfully opened. If it's not, the program will continue to run until it gets a signed integer overflow - which means that the program has undefined behavior.

  • If the file is successfully opened, you'll run into heap-buffer-overflow

    // Creating Childthreads
    for(int i = 0; i < totalLines; i  ) {
         Carg[i].CTDinfo[i] = mainInfo[i];        // <- here
    

    That's because CTDinfo[i] is out of bounds when i > 0. CTDinfo is a pointer to one single info - not totalLines of infos.

  • Same thing below. CTDinfo can't be treated as an array since it's a pointer to a single info - and digit is a pointer to a single int - not an array of int.

    // THIS LINE WILL PUT EACH DIGIT ON CORRECT LOCATION of mainInfo
    for(int i = 0; i < totalDigits; i  ) {
         CTptr->CTDinfo[CTptr->CTDchildIndex].digit[i] = numberArray[i];
    }
    

Suggestions:

  • Write a much smaller program to get the hang of threading.
  • Write a much smaller program to learn how to build up very complicated structures (if you are really required to do so). Use C classes, like std::vector. I doubt this program would need a single new/new[] if you replaced all the manual memory management with standard C containers. Things like info looks like there's been a misunderstanding. There is no reason why pointers should be used here:
    struct info {
         int* digit = new int;
         int* totalDigits = new int;
    };
    
  • Use std::thread and its support functions and classes instead of the platform specific C API in pthread.
  • Related