-4

So I have this function which is behaving like the setInterval function in JS. I found it here. I am currently trying to change it so it can be stopped. I do not fully understand the behavior of this code.

void setInterval(function<void(void)> func, unsigned int interval) {
  thread([func, interval]() {
    while (1) {
      auto x = chrono::steady_clock::now() + chrono::milliseconds(interval);
      func();
      this_thread::sleep_until(x);
    }
  }).detach();
}

I tried it like this:

void setInterval(function<void(void)> func, unsigned int interval, bool &b) {
  thread([func, interval, *b]() {
    while (*b) {
      auto x = chrono::steady_clock::now() + chrono::milliseconds(interval);
      func();
      this_thread::sleep_until(x);
    }
  }).detach();
}

(this won't compile), and in main calling it like this:

bool B;
setInterval(myFunction,1000,B);

I was expecting that if I change the B variable to false, then the thread in setInterval function stops, but I haven't managed to reach my goal like this. Any idead/suggestions? Thank you in advance.

  • Please *always* provide a [mcve] that other people can test for themselves. – Jesper Juhl Feb 14 '18 at 18:03
  • 1
    Does this code actually compile? – NathanOliver Feb 14 '18 at 18:05
  • My code does not – Sótanyi Bálint Feb 14 '18 at 18:08
  • 1
    Well, what are the compiler errors you are getting then? – NathanOliver Feb 14 '18 at 18:10
  • @SótanyiBálint What is the meaning of `*b`, when the type of `b` is `bool&`? It is **not** a pointer, so why are you trying to dereference it? – Algirdas Preidžius Feb 14 '18 at 18:10
  • I thought it is a pointer actually – Sótanyi Bálint Feb 14 '18 at 18:12
  • 1
    @SótanyiBálint Why would you think so? Consider learning from a [good C++ book](https://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list). References are covered in those. – Algirdas Preidžius Feb 14 '18 at 18:13
  • I am learning now from Accelerated C++, but sometimes i get confused. Maybe because I used JS until now and I am still thinking in that way – Sótanyi Bálint Feb 14 '18 at 18:15
  • @Sótanyi Bálint C++ is *not* like JS (even if the syntax may sometimes seem similar). Forget *all* you know about JS when writing C++ code - they are *very* different languages. – Jesper Juhl Feb 14 '18 at 18:22
  • Do you have any ideas regarding my question? Or a different way to solve this problem? – Sótanyi Bálint Feb 14 '18 at 18:25
  • @SótanyiBálint "_Do you have any ideas regarding my question?_" Please re-read my initial comment (reading the compilation errors, would be a great place to start, as well), and act accordingly: don't try to dereference something, that is not a pointer. – Algirdas Preidžius Feb 14 '18 at 18:26
  • Questions seeking debugging help ("why isn't this code working?") must include the desired behavior, a specific problem or error and the shortest code necessary to reproduce it in the question itself. Questions without a clear problem statement are not useful to other readers. See: How to create a [mcve]. Use the [edit] link to improve your *question* - do not add more information via comments. Thanks! – GhostCat Feb 14 '18 at 18:28
  • 2
    Use `&b` to capture by reference, though `bool` requires synchronization and can't safely be accessed synchronously. `std::atomic_bool` would work. You should also consider rather `detach` really is what you want to do. Using `detach` is often a sign of poor design is may cause you problems in the future. You will usually want to keep a handle on your thread and `join` it to know when it's finished stopping. – François Andrieux Feb 14 '18 at 18:29
  • You can do it really expressively if you're already using a framework or engine with an event loop and timer mechanism (which, in the case of JavaScript, is done for you). If you aren't, then you can't. Shrug. – Lightness Races in Orbit Feb 14 '18 at 21:01

1 Answers1

0

Sorry, but I didn't find a design simpler than that.

You could, make a class that owns both a thread, and a weak_ptr to itself, to be a "holder" that the callable can see it safely, because the callable will still exists even if the object is destructed. You don't want a dangling pointer.

template<typename T>
struct IntervalRepeater {
    using CallableCopyable = T;
private:
    weak_ptr<IntervalRepeater<CallableCopyable>> holder;
    std::thread theThread;

    IntervalRepeater(unsigned int interval,
            CallableCopyable callable): callable(callable), interval(interval) {}

    void thread() {
        weak_ptr<IntervalRepeater<CallableCopyable>> holder = this->holder;
        theThread = std::thread([holder](){
            // Try to strongify the pointer, to make it survive this loop iteration,
            //    and ensure that this pointer is valid, if not valid, end the loop.
            while (shared_ptr<IntervalRepeater<CallableCopyable>> ptr = holder.lock()) {
                auto x = chrono::steady_clock::now() + chrono::milliseconds(ptr->interval);
                ptr->callable();
                this_thread::sleep_until(x);
            }
        });
    }

public:
    const CallableCopyable callable;
    const unsigned int interval;

    static shared_ptr<IntervalRepeater<T>> createIntervalRepeater(unsigned int interval,
            CallableCopyable callable) {
        std::shared_ptr<IntervalRepeater<CallableCopyable>> ret =
                shared_ptr<IntervalRepeater<CallableCopyable>>(
                        new IntervalRepeater<CallableCopyable>(interval, callable));
        ret->holder = ret;
        ret->thread();

        return ret;
    }

    ~IntervalRepeater() {
        // Detach the thread before it is released.
        theThread.detach();
    }

};

void beginItWaitThenDestruct() {
    auto repeater = IntervalRepeater<function<void()>>::createIntervalRepeater(
            1000, [](){ cout << "A second\n"; });
    std::this_thread::sleep_for(std::chrono::milliseconds(3700));
}

int main() {
    beginItWaitThenDestruct();
    // Wait for another 2.5 seconds, to test whether there is still an effect of the object
    //   or no.
    std::this_thread::sleep_for(std::chrono::milliseconds(2500));
    return 0;
}

C++ is not JavaScript, but C++ can apply most programming paradigms in different languages.

user9335240
  • 1,739
  • 1
  • 7
  • 14
  • The trouble is that this code must also take on the responsibility covered by part of the JavaScript runtime engine, making the "user-space" end result necessarily more verbose than the JS code equivalent. In short, it's not a fair test. – Lightness Races in Orbit Feb 14 '18 at 21:05