Isaac SDK and std::thread

Hello, just getting started with Isaac sdk, so I’m having some very basic issues:

I cannot get Isaac to work with std::threads, as the application immediate terminates. Here’s my minimal codelet:

== thread_test.h =================================
#include <iostream>
#include <thread>
#include "engine/alice/alice_codelet.hpp"

void foo();
class ThreadTest : public isaac::alice::Codelet {
 public:
  ThreadTest();
 private:
  std::thread t_;
};
ISAAC_ALICE_REGISTER_CODELET(ThreadTest);

== thread_test.cpp =================================
#include "thread_test.h"
void foo(){
  while (true) {
    std::cout << "alive\n";
    std::this_thread::sleep_for(std::chrono::milliseconds(1000));
  }
}
ThreadTest::ThreadTest() : t_(&foo) {}

Which outputs:

terminate called without an active exception
alive
====================================================================================================
|                            Isaac application terminated unexpectedly                             |
====================================================================================================
#01 external/com_nvidia_isaac_engine/engine/alice/tools/main(+0x6ccba) [0x556fe1c70cba]
#02 external/com_nvidia_isaac_engine/engine/alice/tools/main(+0xe4130) [0x556fe1ce8130]
#03 external/com_nvidia_isaac_engine/engine/alice/tools/main(+0xe44a0) [0x556fe1ce84a0]
#04 /lib/x86_64-linux-gnu/libpthread.so.0(+0x12980) [0x7f666a6fe980]
#05 /lib/x86_64-linux-gnu/libc.so.6(gsignal+0xc7) [0x7f6669d98fb7]
#06 /lib/x86_64-linux-gnu/libc.so.6(abort+0x141) [0x7f6669d9a921]
#07 /usr/lib/x86_64-linux-gnu/libstdc++.so.6(+0x8c957) [0x7f666a3ef957]
#08 /usr/lib/x86_64-linux-gnu/libstdc++.so.6(+0x92ae6) [0x7f666a3f5ae6]
#09 /usr/lib/x86_64-linux-gnu/libstdc++.so.6(+0x92b21) [0x7f666a3f5b21]
#10 /home/username/.cache/bazel/_bazel_username/583dd29125c8fd58b635e5dbdc3442cf/execroot/com_nvidia_isaac_sdk/bazel-out/k8-opt/bin/apps/thread_test/thread_test.runfiles/com_nvidia_isaac_sdk//apps/thread_test/libthread_test_components_module.so(+0x8ac39) [0x7f6669472c39]
#11 isaac::alice::GatherComponentInfo[abi:cxx11]() /home/username/.cache/bazel/_bazel_username/583dd29125c8fd58b635e5dbdc3442cf/execroot/com_nvidia_isaac_sdk/bazel-out/k8-opt/bin/apps/thread_test/thread_test.runfiles/com_nvidia_isaac_sdk//apps/thread_test/libthread_test_components_module.so(_ZN5isaac5alice19GatherComponentInfoB5cxx11Ev+0x1029) [0x7f666947a7a9]
#12 /home/username/.cache/bazel/_bazel_username/583dd29125c8fd58b635e5dbdc3442cf/execroot/com_nvidia_isaac_sdk/bazel-out/k8-opt/bin/apps/thread_test/thread_test.runfiles/com_nvidia_isaac_sdk//apps/thread_test/libthread_test_components_module.so(IsaacGatherComponentInfo+0x36) [0x7f666946d816]
#13 external/com_nvidia_isaac_engine/engine/alice/tools/main(+0x74fd1) [0x556fe1c78fd1]
#14 external/com_nvidia_isaac_engine/engine/alice/tools/main(+0x772b7) [0x556fe1c7b2b7]
#15 external/com_nvidia_isaac_engine/engine/alice/tools/main(+0x77c4c) [0x556fe1c7bc4c]
#16 external/com_nvidia_isaac_engine/engine/alice/tools/main(+0x315a7) [0x556fe1c355a7]
#17 external/com_nvidia_isaac_engine/engine/alice/tools/main(+0x33a69) [0x556fe1c37a69]
#18 external/com_nvidia_isaac_engine/engine/alice/tools/main(+0x33dbf) [0x556fe1c37dbf]
#19 external/com_nvidia_isaac_engine/engine/alice/tools/main(+0x12873) [0x556fe1c16873]
#20 /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xe7) [0x7f6669d7bbf7]
#21 external/com_nvidia_isaac_engine/engine/alice/tools/main(+0x17eca) [0x556fe1c1beca]
====================================================================================================

Any idea why this is not working?

It turns out that it’s working once i define a proper destructor:

void ThreadTest::debug() const {
  while (running_) {
    std::cout << "alive\n";
    std::this_thread::sleep_for(std::chrono::milliseconds(1000));
  }
}

ThreadTest::~ThreadTest() {
  running_ = false;
  t_.join();
}

Still not sure why the codelet gets terminated immediately without the destructor though.

Your ThreadTest codelet is not implementing most of the interface of Codelet such as start(), stop(), and tick(). Without starting ticking in start(), your codelet subclass is likely being created and then being destroyed, but you then block within the destructor which is dangerous.

Could you describe a bit more about what you’re trying to do with threading?

I am just trying to get familiar with the interface a Codelet offers, in particular I was experimenting with the getNodeComponentOrNull to directly expose a c++ interface to other Codelets instead of using the message interface. For that reason my ThreadTest does not implement a tick function.

If I understand you correctly however, if I do not call either tickPeriodicallyor tickOnMessage, my codelet will be destroyed immediately?

In that case: I have noticed there are many codelets in the SDK that have neither incoming, nor outgoing messages, e.g. isaac.alice.PoseInitializer. Are these nodes generally not supposed to live for an extended time - i.E. in this case just set the pose in start() and then be destroyed? Do those codelets implement tick()?

Maybe having a long-living node without having it tick() is not intended? I’m trying to figure out these kind of things.

I understand however that this thread is titled poorly - it seems I misunderstood the cause of my issue.

Codelets work for both “something that responds to events” or "available for dynamic instantiation through getNodeComponentOrNull()". If you don’t have it respond to events, then it is basically a plain old object that you happen to be able to gain a pointer to through the SDK engine.