0

I wanted to perform hashing of a stream of input messages in multithreading, so was trying to implement

std::vector<std::future<HashData>> futures;

but the program aborts from abort.h when debugging in Visual Studio 2019.

Code Snippet:

std::vector<std::future<HashData>> futures;
std::vector<std::string> messages;

for (int i = 0; i < messages.size(); i++)
{
  std::promise<HashData> promiseHashData;
  std::future<HashData> futureHashData = promiseHashData.get_future();
  futures.emplace_back(std::move(futureHashData));
  std::async(std::launch::async, [&]() {PerformHash(std::move(promiseHashData), messages[i]);});
}

std::vector<HashData> vectorOfHashData;
// wait for  all async tasks to complete
for (auto& futureObj : futures)
{
  vectorOfHashData.push_back(futureObj.get());
}

void PerformHash(std::promise<HashData>&& promObject, std::string& message)
{
    ComputeHashUsingSHA256(message);
        HashData data;

    // set data for HashData object
    data.i = i;
    data.blocks = blocks;
    data.blocksize = blocksize;
    data.blockbufs = blockbufs;
    data.secs = secs;
    memcpy(data.digest, digest, SHA256_DIGEST_SIZE);

    data.has_hashdata = has_hashdata;
    memcpy(data.hashdata_buf, hashdata_buf, c_hashsize);

    promObject.set_value(data);

}

while debugging the code, observed as only few threads were created using async and post that, the program aborts from abort.h as shown in this image

devilsEye
  • 1
  • 1

2 Answers2

1

The problem is that you capture promiseHashData by reference. At each loop iteration it gets invalidated while the async thread performs computation on it.

You need to capture the instance of the promise by moving it into the lambda, like:

   std::async(std::launch::async, [promiseHashData2=std::move(promiseHashData)] ()mutable{PerformHash(std::move(promiseHashData2), messages[i]);});

Or use std::async's feature of returning std::future while changing performHash to return hashData. Using both async and promise is redundant.

ALX23z
  • 4,456
  • 1
  • 11
  • 18
  • Here, i wanted the data type of futures.get() to be HashData, but when i used the future object returned by async as futureObj.get(), it was giving compilation error: error C2664: 'std::future<_Ty>::future(const std::future<_Ty> &)': cannot convert argument 1 from 'std::future' to 'std::future<_Ty> &&' – devilsEye Feb 13 '23 at 08:01
  • `HashData PerformHash(std::promise&& promObject, std::string& message) { ComputeHashUsingSHA256(message); HashData data; // set data for HashData object data.i = i; data.blocks = blocks; data.blocksize = blocksize; data.blockbufs = blockbufs; data.secs = secs; memcpy(data.digest, digest, SHA256_DIGEST_SIZE); data.has_hashdata = has_hashdata; memcpy(data.hashdata_buf, hashdata_buf, c_hashsize); return data; } ` – devilsEye Feb 13 '23 at 08:05
  • @devilsEye you need the lambda to return `hashData` as well. Currently it just calls the function and returns nothing. – ALX23z Feb 13 '23 at 08:31
1

To build on the good answer from @ALX23z and answer your comments there:

The reason you get that error is that PerformHash (and your lambda) returns void. The return value from std::async is std::future<X>, where X is the return value of the function you give std::async. Here is a small toy example:

struct HashData {std::size_t h;};

HashData performHash(const std::string &msg) // <- returns HashData
{
    HashData hd = {msg.size()};
    return hd;
}

int main()
{
    std::vector<std::string> messages = {"Bla", "klaf", "this is a message"};
    std::vector<std::future<HashData>> futures;

    for (const auto &msg : messages)
    {
        auto fut = std::async(std::launch::async, [&]()
                              { return performHash(msg); }); // <- also returns HashData
        futures.emplace_back(std::move(fut));
    }

    std::vector<HashData> results;
    for (auto &fut : futures)
        results.push_back(fut.get());

    for (const auto &hash : results)
        std::cout << hash.h << '\n';
}

Also note that you can skip the lambda, and call std::async like this:

auto fut = std::async(std::launch::async, performHash, msg); // performHash is a free function

// If performHash is a method of class HashCalculator - included for completeness sake
HashCalculator calc;   // Need an instance somewhere  
auto fut = std::async(std::launch::async, &HashCalculator::performHash, calc, msg);
Frodyne
  • 3,547
  • 6
  • 16
  • tried this, the program aborts from abort.h when debugging in Visual Studio 2019 again. it's not resolving abort issue. – devilsEye Feb 13 '23 at 10:30
  • That sounds odd. If you go back in the stack trace until you hit your code, which line do you find there? – Frodyne Feb 13 '23 at 10:47
  • when we are iterating over vector of future objects, issue is from fut.get(). The get() method on future object is the place from where the program tries to abort. – devilsEye Feb 13 '23 at 11:05
  • That sounds suspiciously like the type of error you get when you try to call `get()` on a `std::future` twice. On the first call to `get()` the result is extracted from the shared state, and the shared state is released. This means that the future is no longer valid, and any subsequent calls to `get()` invokes Undefined Behavior. – Frodyne Feb 13 '23 at 12:36