Search code examples
c++multithreadingc++11shared-ptr

Future is not returning reference to value in C++


I've just started learning multi-threading in C++ and following this excellent tutorial.

However when I print the memory address of the string being returned in Future I'm getting a different address.

Here's my code.

#include<iostream>
#include <thread>
#include <future>
#include <string>

void thFunc(std::promise<std::string> && prms) {
  std::string str("Hello from future ^^");
  std::cout << "Location=" << (void*)str.data() << "\n";
  prms.set_value(str);
}

int main() {
  std::promise<std::string> prms;
  std::future<std::string> ftr = prms.get_future();
  std::thread th(&thFunc, std::move(prms));
  std::printf("Hello from main!!\n");

  std::string futStr = ftr.get();
  std::cout << futStr << "\t Location= " << (void*)futStr.data() << "\n";
  th.join();
  return 0;
}

How I'm compiling it:

clang++ -g -Wall -std=c++11 -stdlib=libc++ -o future_ex future_ex.cpp

And here's the output that I'm getting:

Hello from main!!
Location=0x30cdbff01
Hello from future ^^     Location= 0x30cd3dfa1

I checked the documentation of get() and it does uses std::move which means that there's no copy in between. So not sure why address is different.

Any help/explanation on this behaviour would be great :)
Please let me know if more info is needed.

EDIT:: Based on the comments here's a shot using shared_ptr. Modified the code to use shared_ptr (excluding imports)

void thFunc(std::promise<std::shared_ptr<std::string>> & prms) {
  std::string str("Hello from future ^^");
  std::shared_ptr<std::string> shared = std::make_shared<std::string>(str);
  std::cout << "Address in thread=" << shared.get() << " String= " << *shared.get() << "\n";
  prms.set_value(shared);
}

int main() {
  std::promise<std::shared_ptr<std::string>> prms;
  std::future<std::shared_ptr<std::string>> ftr = prms.get_future();
  std::thread th(&thFunc, std::ref(prms));
  std::printf("Hello from main!!\n");

  std::shared_ptr<std::string> futSharedPtr = ftr.get();
  std::cout << "Address in main= " << futSharedPtr.get() << " Value= " << *futSharedPtr.get() << "\n";
  std::cout << "COUNT:: " << futSharedPtr.use_count() << "\n";
  th.join();
  return 0;
}

Here's the output:

Hello from main!!
Address in thread=0x60000298c018 String= Hello from future ^^
Address in main= 0x60000298c018 Value= Hello from future ^^
COUNT:: 1

Solution

  • prms.set_value(str);
    

    this line is a copy of str into the promise.

    prms.set_value(std::move(str));
    

    now you move the string in.

    However, if the string is short, you can still experience the small string optimisation (SSO, or SBO for small buffer optimization). std::string does not mandate that its data is stored on the heap; in fact, it encourages that its data be stored within the std::string itself if short, saving a heap allocation.

    "Hello from future ^^" is short enough to fit in most SSO buffers for std::strings.

    Use a longer string, and properly move, and those addresses will match.


    A good way to debug "where did that copy come from" is to use a move-only type.

    Take your shared implementation and use unique_ptr. Now it fails to work at the point where the copy occurs!

    void thFunc(std::promise<std::unique_ptr<std::string>> & prms) {
      std::string str("Hello from future ^^");
      auto ptr = std::make_unique<std::string>(str); // copy here, add std::move
      std::cout << "Address in thread=" << shared.get() << " String= " << *ptr.get() << "\n";
      prms.set_value(ptr); // ERROR: copy here, add std::move
    }
    
    int main() {
      std::promise<std::unique_ptr<std::string>> prms;
      auto ftr = prms.get_future();
      std::thread th(&thFunc, std::ref(prms));
      std::printf("Hello from main!!\n");
    
      auto ptr = ftr.get();
      std::cout << "Address in main= " << ptr.get() << " Value= " << *ptr.get() << "\n";
      th.join();
      return 0;
    }