C++ vector of futures access violation

I am trying to do some multithreading with futures in C++ using std::async. I need to create a bunch of objects whose constructors do some serializations in parallel. I am calling a function that calls the constructor and returns a shared_ptr to the object. I call this function with std::async and create a vector of futures returned by this function and then call .get() on each future in a loop where I assumed it will be a blocking call but that’s where I get an exception in the debugger. I am not sure what’s going on.

Here is a simplified version of a reproducible example that throws exception with MSVC 14.3+ when compiled with C++17

#include <iostream>
#include <vector>
#include <future>
#include <memory>
#include <numeric>

struct TaskUnit {
  std::vector<int> preds;
  int vertex;
};

struct Task {
  std::vector<TaskUnit> units;
  int task_id;

  Task(std::vector<TaskUnit> const& units, int const task_id)
    : units(units), task_id(task_id) {}

  static std::shared_ptr<Task const>
    Create(std::vector<TaskUnit> const& units, int const id)
  {
    return std::make_shared<Task const>(units, id);
  }
};

auto ConstructTask(int const task_count)
{
  std::vector<TaskUnit> units{ {{1,2}, 1}};
  return std::async(std::launch::async, Task::Create, std::cref(units), task_count);
}

auto ConstructTasks(int const n_tasks)
{
  std::vector<std::future<std::shared_ptr<Task const>>> futures;
  std::vector<int> range(n_tasks);
  std::iota(range.begin(), range.end(), 0);
  for (auto const& task_count : range)
  {
    futures.push_back(ConstructTask(task_count));
  }
  std::vector<std::shared_ptr<Task const>> tasks;
  for (auto& future : futures)
  {
    tasks.push_back(future.get());
  }
  return tasks;
}

int main()
{
    auto tasks = ConstructTasks(10);
    for (auto const& task : tasks)
    {
      std::cout << task->task_id;
    }
}

>Solution :

You pass a reference to a std::vector<TaskUnit> that goes out of scope and dies when the function returns. Pass it by value instead:

class Task {
  static std::shared_ptr<Task const>
    Create(std::vector<TaskUnit> const& units, int const id)
//                                    ^ remove
  {
    return std::make_shared<Task const>(units, id);
  }
};

auto ConstructTask(int const task_count) {
    std::vector<TaskUnit> units{{{1, 2}, 1}};
    return std::async(std::launch::async, Task::Create, units, task_count);
//                                                      ^^^^^ remove cref
}

Leave a Reply