1

I have the following code:

DisplayObject conveyor_belts[] = {eggs_1, eggs_2, flour_1, flour_2, sugar_1, sugar_2, butter_1, butter_2};
std::thread conveyor_belts_t[4];
for (int j = 0; j < 4; j++) {
    conveyor_belts_t[j] = std::thread([&](){
        conveyor_belts[j * 2].draw(15, 78 + i * 3);  // segmentation fault here
    });
}

I defined a copy constructor for displayObject, but the code above gives me segmentation fault at the line highlighted. I can resolve this issue in the following way:

DisplayObject conveyor_belts[] = {eggs_1, eggs_2, flour_1, flour_2, sugar_1, sugar_2, butter_1, butter_2};
std::thread conveyor_belts_t[4];
for (int j = 0; j < 4; j++) {
    conveyor_belts_t[j] = std::thread([&](){
        conveyor_belts[0].draw(15, 78 + i * 3);
    });
}

I wonder why a variable indexing will give me segmentation fault

Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
Thompson Liu
  • 135
  • 1
  • 6
  • 2
    You auto-captured the loop variable `j` by _reference_. This is incorrect. You must capture `j` by value in your lambda's capture list. As a personal style preference, I never use auto-capture. I prefer to be explicit about exactly what I'm giving the lambda. – paddy Nov 09 '20 at 22:00
  • @paddy Thanks, this works! Can you elaborate a little bit why I can't capture j by reference? – Thompson Liu Nov 09 '20 at 22:03
  • 1
    Because it's going to change and then be destroyed, and your threads are all going to be attempting to use it. Furthermore, it's incorrect. You want each thread to use the specific value that `j` had at the time the thread was created. Capturing by value is the way to do this. – paddy Nov 09 '20 at 22:05
  • If `j` has gone out of scope when you read from it, you'll have undefined behavior. If you happen to read it just when it reaches `4`, you'll get `4 * 2` and access the array out of bounds. Again, undefined behavior. – Ted Lyngmo Nov 09 '20 at 22:07

1 Answers1

3

In your execution thread

conveyor_belts[j * 2].draw(15, 78 + i * 3);

C++ gives you no guarantee whatsoever that the new execution thread will execute this code and read the value j which was captured by reference before the original execution thread iterates the for loop and increments the value of j being referenced here. All that C++ guarantees you is that the new execution thread starts executing at some point after std::thread gets constructed. That's your only guarantee.

It is fairly likely that by the time all execution threads wake up the for loop already finished iterating, and all the four execution threads are happily looking at their captured reference to j, which has now reached its final value of 4, and all four execution thread will happy use the value of j, which is now 4, because it was captured by reference.

Sam Varshavchik
  • 114,536
  • 5
  • 94
  • 148