0

Context: On one of my C++11 application, object serialization and publish of message is time consuming. Therefore I want to do it in a separate thread using Intel TBB library (more specifically using a tbb::task_group)

Issue: the object to serialize is a struct where some of the properties are std::vector<std::unique_ptr<SomeObject>>, making it impossible to pass by copy to the lambda executed in a task

Approximately it look like

struct MockedDC {
  MockedDC(int x, std::vector<std::unique_ptr<SomeObject>> v) : x(x),
                                                                v(std::move(v)) {};

  int                                      x;
  std::vector<std::unique_ptr<SomeObject>> v;
};

The "solution" I found, is to reconstruct on the heap with the move-constructor my instance and wrap it in a shared_ptr<MockedDC> which is copyable. In the end the function which invoke the tbb::task_group::run look like

// function called like this `executeInThread(taskGroup, std::move(mockedDC))`

void  executeInThread(tbb::task_group& taskGroup, MockedDC mockedDC) {
  const std::shared_ptr<MockedDC> sharedMockedDC(new MockedDC(std::move(mockedDC)));
  auto f = [sharedMockedDC] {
    const auto serialized(serializer(*sharedMockedDC)); // pass by reference
    publish(serialized);
  };

  taskGroup.run(f);
};

it compile and run fine, but I can't put it under pressure as it will be in real life condition so my question is is it safe/sane to do this ?

I found on another stackoverflow question an alternative, but the implementation looks difficult to maintain given my C++ knowledge :) that's why I want to stick with the shared_ptr approach as suggested somewhere else

What I tried so far: I wrote a dummy code to test the thing, but I think its not enough to validate this approach. I also wanted to compile with some sanitization flags, but tbb fail to link with a bunch of errors like undefined reference to __ubsan_handle_pointer_overflow

Here is the dummy example if that help to answer (it compile and run without issues (except some int overflow but that not an issue I guess))

#include <cstdio>
#include <iostream>
#include <memory>
#include <vector>
#include <numeric>
#include "tbb/task_scheduler_init.h"
#include "tbb/task_group.h" 

struct MockedDC {
  MockedDC(int seed, size_t baseLen) : seed(seed), baseLen(baseLen) {
    this->a_MDC.reserve(baseLen);
    for (size_t i = 0; i < baseLen; ++i)
      this->a_MDC.emplace_back(new int((seed + i) / (seed + 1)));
    };

    int                               seed;
    size_t                            baseLen;
    std::vector<std::unique_ptr<int>> a_MDC;
  };

void  executeInThread(tbb::task_group& taskGroup, MockedDC mockedDC) {
  const std::shared_ptr<MockedDC> sharedMockedDC(new MockedDC(std::move(mockedDC)));
  auto f = [sharedMockedDC] {
    std::cout <<
      std::accumulate(sharedMockedDC->a_MDC.begin(), sharedMockedDC->a_MDC.end(), 0, [](int acc, const std::unique_ptr<int>& rhs) {
          return acc + *rhs;
      })
      << std::endl << std::flush;
  };
  taskGroup.run(f);
};


void  triggerTest(tbb::task_group& taskGroup) {
  for (size_t i = 0; i < 1000000; ++i) {
    MockedDC  mdc(i, 10000000);
    executeInThread(taskGroup, std::move(mdc));
  }
  return ;
};

int main() {
  tbb::task_scheduler_init  tbbInit(tbb::task_scheduler_init::automatic);
  //tbb::task_scheduler_init  tbbInit(8);
  tbb::task_group           taskGroup;

  triggerTest(taskGroup);
  taskGroup.wait();
  return (0);
};

PS: using C++14 new capture by move doesn't work because of TBB library :/

shorty_ponton
  • 454
  • 4
  • 14
  • Your "execute in thread" function takes a copy of MockedDC ... on top of that, you'd essentially be calling std::move twice. So it's not all that sane; probably better to just take a MockedDC&& and then bin the last std::move call. also - unique_ptr to integer; not sane. If you know that this will HAVE to go into a shared_ptr - why not just start off with one; then you don't have to copy it and half your complexity goes away – UKMonkey Sep 19 '19 at 16:49
  • Ah yes for the unique ptr to integer it's only for the example. The real code use a unique ptr to a class. I'll change the MockedDC&& thanks – shorty_ponton Sep 19 '19 at 16:52

0 Answers0