One-shot delay without memory leak (deallocating Poco::Timer pointers?)

Hi,

I was trying to find how to implement a one-shot delay in OpenFrameworks; eventually I found that I could use Poco::Timer for it; here is a minimal working example (I call it testOneshot), using a standard OF example Makefile:

main.cpp

//#include "ofMain.h" // for window
#include "ofApp.h"
//#include "ofAppNoWindow.h" // for cmdline

int main()
{
  // cmdline - no window:
  //ofAppNoWindow window;
  //ofSetupOpenGL(&window, 0, 0, OF_WINDOW);
  // with window:
  ofSetupOpenGL(300,200, OF_WINDOW);

  ofRunApp(new ofApp());
}

ofApp.h

#pragma once

#include "ofMain.h"
#include "Poco/Timer.h"

class ofApp: public ofBaseApp
{
public:
  void setup();
  void update();
  void draw();

  void keyPressed(int key);

  void oneShotCallback(Poco::Timer& timer);
};

ofApp.cpp

#include "ofApp.h"

// testOneshot

void ofApp::setup() {
  ofSetLogLevel(OF_LOG_VERBOSE); //(OF_LOG_NOTICE);
  ofLogNotice() << "ofApp::setup";
}

void ofApp::update() {
}

void ofApp::draw() {
}

void ofApp::keyPressed(int key) {
  ofLogVerbose("ofApp") << "keyPressed() key = " << key;
  switch(key) {
    case 'f': //
      ofLogVerbose("ofApp") << "'f'ireing one-shot";
      Poco::Timer* mytimer = new Poco::Timer(1250, 0); // 1.25 sec after
      Poco::TimerCallback<ofApp>* mytcallback = new Poco::TimerCallback<ofApp>(*this, &ofApp::oneShotCallback);
      mytimer->start(*mytcallback);
      break;
  }
}

void ofApp::oneShotCallback(Poco::Timer& timer) {
  ofLogVerbose("ofApp") << "oneShotCallback HAS BEEN CALLED!!!";
}

So, in this program, if I press the ‘f’ button, then after a second and a quarter, I get a message printed out in terminal, and all seemingly works.

I have these two questions:

  • Is this the recommended way to implement one-shot delays in OF? Are there alternative methods for doing the same?
  • As visible on the code, I have to create (two) pointers with new - which as I understand it, I’d have to delete eventually, otherwise I get a memory leak. However, I cannot dereference them with delete in the keyPressed code portion, otherwise the one-shot would never get called - and I cannot dereference them in the oneShotCallback function, as I don’t have a reference to those pointers anymore. Do I have a memory leak here - and if so, what would be the recommended way to handle dereferencing the pointers properly?

If it works as expected, then it is fine :wink:

You might be able to not reallocate the Timer every time by storing it globally. Something similar to:

#pragma once

#include "ofMain.h"
#include "Poco/Timer.h"

class ofApp: public ofBaseApp
{
public:
  void setup();
  void update();
  void draw();

  void keyPressed(int key);

  void oneShotCallback(Poco::Timer& timer);

  Poco::Timer timer;
  Poco::TimerCallback<ofApp>* callback;
};

cpp

#include "ofApp.h"

void ofApp::setup() {
  ofSetLogLevel(OF_LOG_VERBOSE); //(OF_LOG_NOTICE);
  ofLogNotice() << "ofApp::setup";
  timer.setStartInterval(1250);
  callback = new Poco::TimerCallback<ofApp>(*this, &ofApp::oneShotCallback);
}

void ofApp::update() {
}

void ofApp::draw() {
}

void ofApp::keyPressed(int key) {
  ofLogVerbose("ofApp") << "keyPressed() key = " << key;
  switch(key) {
    case 'f': //
      ofLogVerbose("ofApp") << "'f'ireing one-shot";
      timer.start(*callback);
      break;
  }
}

void ofApp::oneShotCallback(Poco::Timer& timer) {
  ofLogVerbose("ofApp") << "oneShotCallback HAS BEEN CALLED!!!";
}

Many thanks for the response @underdoeg !

Heh, yes :slight_smile: - but I was still a bit worried, so I did a test with valgrind for the OP:

valgrind --leak-check=yes ./bin/testOneshot_debug 2>&1 | tee val.log

If I just run the program and close it without starting the timer (by pressing f), I get a bunch of possible leaks, mostly related to openGL functions. If I run it again, and this time start the one-shot delay, I get something like this in the log:

...
==5337== 
[notice ] ofApp::setup
[verbose] ofApp: keyPressed() key = 102
[verbose] ofApp: 'f'ireing one-shot
[verbose] ofApp: oneShotCallback HAS BEEN CALLED!!!
[verbose] ofApp: keyPressed() key = 102
[verbose] ofApp: 'f'ireing one-shot
[verbose] ofApp: oneShotCallback HAS BEEN CALLED!!!
==5337== 
==5337== HEAP SUMMARY:
...
==5337== 32 bytes in 2 blocks are definitely lost in loss record 475 of 755
==5337==    at 0x402A6DC: operator new(unsigned int) (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==5337==    by 0x805F3AB: ofApp::keyPressed(int) (ofApp.cpp:23)
==5337==    by 0x805FAFF: ofBaseApp::keyPressed(ofKeyEventArgs&) (ofBaseApp.h:80)
==5337==    by 0x8105F95: of::priv::Function<ofKeyEventArgs> ofEvent<ofKeyEventArgs, std::mutex>::make_function<ofBaseApp>(ofBaseApp*, void (ofBaseApp::*)(ofKeyEventArgs&), int)::{lambda(void const*, ofKeyEventArgs&)#1}::operator()(void const*, ofKeyEventArgs&) const (ofEvent.h:230)
==5337==    by 0x810BB3B: std::_Function_handler<bool (void const*, ofKeyEventArgs&), of::priv::Function<ofKeyEventArgs> ofEvent<ofKeyEventArgs, std::mutex>::make_function<ofBaseApp>(ofBaseApp*, void (ofBaseApp::*)(ofKeyEventArgs&), int)::{lambda(void const*, ofKeyEventArgs&)#1}>::_M_invoke(std::_Any_data const&, void const*, ofKeyEventArgs&) (functional:2057)
==5337==    by 0x8197513: std::function<bool (void const*, ofKeyEventArgs&)>::operator()(void const*, ofKeyEventArgs&) const (functional:2471)
==5337==    by 0x8196266: ofEvent<ofKeyEventArgs, std::mutex>::notify(void const*, ofKeyEventArgs&) (ofEvent.h:304)
==5337==    by 0x8195732: void ofNotifyEvent<ofEvent<ofKeyEventArgs, std::mutex>, ofKeyEventArgs>(ofEvent<ofKeyEventArgs, std::mutex>&, ofKeyEventArgs&) (ofEventUtils.h:222)
==5337==    by 0x8194338: ofCoreEvents::notifyKeyPressed(int, int, int, int) (ofEvents.cpp:251)
==5337==    by 0x81ACA0F: ofAppGLFWWindow::keyboard_cb(GLFWwindow*, int, int, unsigned int, int, int) (ofAppGLFWWindow.cpp:1100)
==5337==    by 0x81B11D6: _glfwInputKey (in /path/to/openFrameworks/apps/myApps/testOneshot/bin/testOneshot_debug)
==5337==    by 0x81B90E9: processEvent (in /path/to/openFrameworks/apps/myApps/testOneshot/bin/testOneshot_debug)
==5337== 
....
==5337== 440 (408 direct, 32 indirect) bytes in 2 blocks are definitely lost in loss record 633 of 755
==5337==    at 0x402A6DC: operator new(unsigned int) (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==5337==    by 0x805F37F: ofApp::keyPressed(int) (ofApp.cpp:22)
==5337==    by 0x805FAFF: ofBaseApp::keyPressed(ofKeyEventArgs&) (ofBaseApp.h:80)
==5337==    by 0x8105F95: of::priv::Function<ofKeyEventArgs> ofEvent<ofKeyEventArgs, std::mutex>::make_function<ofBaseApp>(ofBaseApp*, void (ofBaseApp::*)(ofKeyEventArgs&), int)::{lambda(void const*, ofKeyEventArgs&)#1}::operator()(void const*, ofKeyEventArgs&) const (ofEvent.h:230)
==5337==    by 0x810BB3B: std::_Function_handler<bool (void const*, ofKeyEventArgs&), of::priv::Function<ofKeyEventArgs> ofEvent<ofKeyEventArgs, std::mutex>::make_function<ofBaseApp>(ofBaseApp*, void (ofBaseApp::*)(ofKeyEventArgs&), int)::{lambda(void const*, ofKeyEventArgs&)#1}>::_M_invoke(std::_Any_data const&, void const*, ofKeyEventArgs&) (functional:2057)
==5337==    by 0x8197513: std::function<bool (void const*, ofKeyEventArgs&)>::operator()(void const*, ofKeyEventArgs&) const (functional:2471)
==5337==    by 0x8196266: ofEvent<ofKeyEventArgs, std::mutex>::notify(void const*, ofKeyEventArgs&) (ofEvent.h:304)
==5337==    by 0x8195732: void ofNotifyEvent<ofEvent<ofKeyEventArgs, std::mutex>, ofKeyEventArgs>(ofEvent<ofKeyEventArgs, std::mutex>&, ofKeyEventArgs&) (ofEventUtils.h:222)
==5337==    by 0x8194338: ofCoreEvents::notifyKeyPressed(int, int, int, int) (ofEvents.cpp:251)
==5337==    by 0x81ACA0F: ofAppGLFWWindow::keyboard_cb(GLFWwindow*, int, int, unsigned int, int, int) (ofAppGLFWWindow.cpp:1100)
==5337==    by 0x81B11D6: _glfwInputKey (in /path/to/openFrameworks/apps/myApps/testOneshot/bin/testOneshot_debug)
==5337==    by 0x81B90E9: processEvent (in /path/to/openFrameworks/apps/myApps/testOneshot/bin/testOneshot_debug)
==5337== 
...

Note that both entries claim “definitely lost”, which means a definite memory leak; and also the first is detected at ofApp.cpp:23 and the second at ofApp.cpp:22, which are these lines:

22       Poco::Timer* mytimer = new Poco::Timer(1250, 0); // 1.25 sec after
23       Poco::TimerCallback<ofApp>* mytcallback = new Poco::TimerCallback<ofApp>(*this, &ofApp::oneShotCallback);

That is, indeed each time a new is called without a delete, indeed a memory leak is detected. Interestingly, for that log, I pressed f twice, but each of the lines (ofApp.cpp 22 and 23) is reported only once.

That seems like a very good idea; I tried your code, however, when I run the application and press f for the second time, the program crashes:

[notice ] ofApp::setup
[verbose] ofApp: keyPressed() key = 102
[verbose] ofApp: 'f'ireing one-shot
[verbose] ofApp: oneShotCallback HAS BEEN CALLED!!!
[verbose] ofApp: keyPressed() key = 102
[verbose] ofApp: 'f'ireing one-shot
terminate called after throwing an instance of 'Poco::IllegalStateException'
  what():  Illegal state
[verbose] ofSignalHandler: Aborted
Aborted (core dumped)

The same happens with valgrind, too:

...
==7169== 
[notice ] ofApp::setup
[verbose] ofApp: keyPressed() key = 102
[verbose] ofApp: 'f'ireing one-shot
[verbose] ofApp: oneShotCallback HAS BEEN CALLED!!!
[verbose] ofApp: keyPressed() key = 102
[verbose] ofApp: 'f'ireing one-shot
terminate called after throwing an instance of 'Poco::IllegalStateException'
  what():  Illegal state
[verbose] ofSignalHandler: Aborted
==7169== 
...
==7169== 20 bytes in 1 blocks are possibly lost in loss record 912 of 3,179
==7169==    at 0x402A6DC: operator new(unsigned int) (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==7169==    by 0x4910003: std::string::_Rep::_S_create(unsigned int, unsigned int, std::allocator<char> const&) (in /usr/lib/i386-linux-
gnu/libstdc++.so.6.0.19)
==7169==    by 0x827595A: char* std::string::_S_construct<char const*>(char const*, char const*, std::allocator<char> const&, std::forwa
rd_iterator_tag) (in /path/to/openFrameworks/apps/myApps/testOneshot/bin/testOneshot_debug)
==7169==    by 0x491259F: std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocat
or<char> const&) (in /usr/lib/i386-linux-gnu/libstdc++.so.6.0.19)
==7169==    by 0x82AB15B: Poco::ThreadPool::defaultPool() (in /path/to/openFrameworks/apps/myApps/testOneshot/bin/testOneshot_debug)
==7169==    by 0x826C3F7: Poco::Timer::start(Poco::AbstractTimerCallback const&) (in /path/to/openFrameworks/apps/myApps/testOneshot/bin/testOneshot_debug)
==7169==    by 0x805F3F2: ofApp::keyPressed(int) (ofApp.cpp:27)
==7169==    by 0x805FAD9: ofBaseApp::keyPressed(ofKeyEventArgs&) (ofBaseApp.h:80)
==7169==    by 0x8105FCB: of::priv::Function<ofKeyEventArgs> ofEvent<ofKeyEventArgs, std::mutex>::make_function<ofBaseApp>(ofBaseApp*, void (ofBaseApp::*)(ofKeyEventArgs&), int)::{lambda(void const*, ofKeyEventArgs&)#1}::operator()(void const*, ofKeyEventArgs&) const (ofEvent.h:230)
==7169==    by 0x810BB71: std::_Function_handler<bool (void const*, ofKeyEventArgs&), of::priv::Function<ofKeyEventArgs> ofEvent<ofKeyEventArgs, std::mutex>::make_function<ofBaseApp>(ofBaseApp*, void (ofBaseApp::*)(ofKeyEventArgs&), int)::{lambda(void const*, ofKeyEventArgs&)#1}>::_M_invoke(std::_Any_data const&, void const*, ofKeyEventArgs&) (functional:2057)
==7169==    by 0x8197549: std::function<bool (void const*, ofKeyEventArgs&)>::operator()(void const*, ofKeyEventArgs&) const (functional:2471)
==7169==    by 0x819629C: ofEvent<ofKeyEventArgs, std::mutex>::notify(void const*, ofKeyEventArgs&) (ofEvent.h:304)
==7169== 
...
==7169== 34 bytes in 1 blocks are possibly lost in loss record 1,728 of 3,179
==7169==    at 0x402A6DC: operator new(unsigned int) (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==7169==    by 0x4910003: std::string::_Rep::_S_create(unsigned int, unsigned int, std::allocator<char> const&) (in /usr/lib/i386-linux-
gnu/libstdc++.so.6.0.19)
==7169==    by 0x827595A: char* std::string::_S_construct<char const*>(char const*, char const*, std::allocator<char> const&, std::forwa
rd_iterator_tag) (in /path/to/openFrameworks/apps/myApps/testOneshot/bin/testOneshot_debug)
==7169==    by 0x491259F: std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocat
or<char> const&) (in /usr/lib/i386-linux-gnu/libstdc++.so.6.0.19)
==7169==    by 0x826C2B9: Poco::Timer::start(Poco::AbstractTimerCallback const&, Poco::Thread::Priority, Poco::ThreadPool&) (in /path/to/openFrameworks/apps/myApps/testOneshot/bin/testOneshot_debug)
==7169==    by 0x826C417: Poco::Timer::start(Poco::AbstractTimerCallback const&) (in /path/to/openFrameworks/apps/myApps/testOneshot
/bin/testOneshot_debug)
==7169==    by 0x805F3F2: ofApp::keyPressed(int) (ofApp.cpp:27)
==7169==    by 0x805FAD9: ofBaseApp::keyPressed(ofKeyEventArgs&) (ofBaseApp.h:80)
==7169==    by 0x8105FCB: of::priv::Function<ofKeyEventArgs> ofEvent<ofKeyEventArgs, std::mutex>::make_function<ofBaseApp>(ofBaseApp*, void (ofBaseApp::*)(ofKeyEventArgs&), int)::{lambda(void const*, ofKeyEventArgs&)#1}::operator()(void const*, ofKeyEventArgs&) const (ofEvent.h:230)
==7169==    by 0x810BB71: std::_Function_handler<bool (void const*, ofKeyEventArgs&), of::priv::Function<ofKeyEventArgs> ofEvent<ofKeyEventArgs, std::mutex>::make_function<ofBaseApp>(ofBaseApp*, void (ofBaseApp::*)(ofKeyEventArgs&), int)::{lambda(void const*, ofKeyEventArgs&)#1}>::_M_invoke(std::_Any_data const&, void const*, ofKeyEventArgs&) (functional:2057)
==7169==    by 0x8197549: std::function<bool (void const*, ofKeyEventArgs&)>::operator()(void const*, ofKeyEventArgs&) const (functional:2471)
==7169==    by 0x819629C: ofEvent<ofKeyEventArgs, std::mutex>::notify(void const*, ofKeyEventArgs&) (ofEvent.h:304)
==7169== 
...
==7169== 48 bytes in 2 blocks are possibly lost in loss record 2,112 of 3,179
==7169==    at 0x402A6DC: operator new(unsigned int) (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==7169==    by 0x4910003: std::string::_Rep::_S_create(unsigned int, unsigned int, std::allocator<char> const&) (in /usr/lib/i386-linux-
gnu/libstdc++.so.6.0.19)
==7169==    by 0x8204DAA: char* std::string::_S_construct<char*>(char*, char*, std::allocator<char> const&, std::forward_iterator_tag) (
in /path/to/openFrameworks/apps/myApps/testOneshot/bin/testOneshot_debug)
==7169==    by 0x82AAC07: Poco::ThreadPool::createThread() (in /path/to/openFrameworks/apps/myApps/testOneshot/bin/testOneshot_debug
)
==7169==    by 0x82AB037: Poco::ThreadPool::ThreadPool(std::string const&, int, int, int, int) (in /path/to/openFrameworks/apps/myAp
ps/testOneshot/bin/testOneshot_debug)
==7169==    by 0x82AB195: Poco::ThreadPool::defaultPool() (in /path/to/openFrameworks/apps/myApps/testOneshot/bin/testOneshot_debug)
==7169==    by 0x826C3F7: Poco::Timer::start(Poco::AbstractTimerCallback const&) (in /path/to/openFrameworks/apps/myApps/testOneshot
/bin/testOneshot_debug)
==7169==    by 0x805F3F2: ofApp::keyPressed(int) (ofApp.cpp:27)
==7169==    by 0x805FAD9: ofBaseApp::keyPressed(ofKeyEventArgs&) (ofBaseApp.h:80)
==7169==    by 0x8105FCB: of::priv::Function<ofKeyEventArgs> ofEvent<ofKeyEventArgs, std::mutex>::make_function<ofBaseApp>(ofBaseApp*, void (ofBaseApp::*)(ofKeyEventArgs&), int)::{lambda(void const*, ofKeyEventArgs&)#1}::operator()(void const*, ofKeyEventArgs&) const (ofEvent.h:230)
==7169==    by 0x810BB71: std::_Function_handler<bool (void const*, ofKeyEventArgs&), of::priv::Function<ofKeyEventArgs> ofEvent<ofKeyEventArgs, std::mutex>::make_function<ofBaseApp>(ofBaseApp*, void (ofBaseApp::*)(ofKeyEventArgs&), int)::{lambda(void const*, ofKeyEventArgs&)#1}>::_M_invoke(std::_Any_data const&, void const*, ofKeyEventArgs&) (functional:2057)
==7169==    by 0x8197549: std::function<bool (void const*, ofKeyEventArgs&)>::operator()(void const*, ofKeyEventArgs&) const (functional:2471)
==7169== 
...
==7169== 112 bytes in 1 blocks are possibly lost in loss record 2,558 of 3,179
==7169==    at 0x402A17C: malloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==7169==    by 0x48ABBA0: __cxa_allocate_exception (in /usr/lib/i386-linux-gnu/libstdc++.so.6.0.19)
==7169==    by 0x826C29F: Poco::Timer::start(Poco::AbstractTimerCallback const&, Poco::Thread::Priority, Poco::ThreadPool&) (in /path/to/openFrameworks/apps/myApps/testOneshot/bin/testOneshot_debug)
==7169==    by 0x826C417: Poco::Timer::start(Poco::AbstractTimerCallback const&) (in /path/to/openFrameworks/apps/myApps/testOneshot
/bin/testOneshot_debug)
==7169==    by 0x805F3F2: ofApp::keyPressed(int) (ofApp.cpp:27)
==7169==    by 0x805FAD9: ofBaseApp::keyPressed(ofKeyEventArgs&) (ofBaseApp.h:80)
==7169==    by 0x8105FCB: of::priv::Function<ofKeyEventArgs> ofEvent<ofKeyEventArgs, std::mutex>::make_function<ofBaseApp>(ofBaseApp*, void (ofBaseApp::*)(ofKeyEventArgs&), int)::{lambda(void const*, ofKeyEventArgs&)#1}::operator()(void const*, ofKeyEventArgs&) const (ofEvent.h:230)
==7169==    by 0x810BB71: std::_Function_handler<bool (void const*, ofKeyEventArgs&), of::priv::Function<ofKeyEventArgs> ofEvent<ofKeyEventArgs, std::mutex>::make_function<ofBaseApp>(ofBaseApp*, void (ofBaseApp::*)(ofKeyEventArgs&), int)::{lambda(void const*, ofKeyEventArgs&)#1}>::_M_invoke(std::_Any_data const&, void const*, ofKeyEventArgs&) (functional:2057)
==7169==    by 0x8197549: std::function<bool (void const*, ofKeyEventArgs&)>::operator()(void const*, ofKeyEventArgs&) const (functional:2471)
==7169==    by 0x819629C: ofEvent<ofKeyEventArgs, std::mutex>::notify(void const*, ofKeyEventArgs&) (ofEvent.h:304)
==7169==    by 0x8195768: void ofNotifyEvent<ofEvent<ofKeyEventArgs, std::mutex>, ofKeyEventArgs>(ofEvent<ofKeyEventArgs, std::mutex>&, ofKeyEventArgs&) (ofEventUtils.h:222)
==7169==    by 0x819436E: ofCoreEvents::notifyKeyPressed(int, int, int, int) (ofEvents.cpp:251)
==7169== 
...
==7169== 368 bytes in 2 blocks are possibly lost in loss record 2,860 of 3,179
==7169==    at 0x402C109: calloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==7169==    by 0x401113E: allocate_dtv (dl-tls.c:296)
==7169==    by 0x40118AB: _dl_allocate_tls (dl-tls.c:460)
==7169==    by 0x479479C: allocate_stack (allocatestack.c:589)
==7169==    by 0x479479C: pthread_create@@GLIBC_2.1 (pthread_create.c:500)
==7169==    by 0x826A3A0: Poco::ThreadImpl::startImpl(Poco::SharedPtr<Poco::Runnable, Poco::ReferenceCounter, Poco::ReleasePolicy<Poco::
Runnable> >) (in /path/to/openFrameworks/apps/myApps/testOneshot/bin/testOneshot_debug)
==7169==    by 0x826A892: Poco::Thread::start(Poco::Runnable&) (in /path/to/openFrameworks/apps/myApps/testOneshot/bin/testOneshot_d
ebug)
==7169==    by 0x82AB011: Poco::ThreadPool::ThreadPool(std::string const&, int, int, int, int) (in /path/to/openFrameworks/apps/myAp
ps/testOneshot/bin/testOneshot_debug)
==7169==    by 0x82AB195: Poco::ThreadPool::defaultPool() (in /path/to/openFrameworks/apps/myApps/testOneshot/bin/testOneshot_debug)
==7169==    by 0x826C3F7: Poco::Timer::start(Poco::AbstractTimerCallback const&) (in /path/to/openFrameworks/apps/myApps/testOneshot
/bin/testOneshot_debug)
==7169==    by 0x805F3F2: ofApp::keyPressed(int) (ofApp.cpp:27)
==7169==    by 0x805FAD9: ofBaseApp::keyPressed(ofKeyEventArgs&) (ofBaseApp.h:80)
==7169==    by 0x8105FCB: of::priv::Function<ofKeyEventArgs> ofEvent<ofKeyEventArgs, std::mutex>::make_function<ofBaseApp>(ofBaseApp*, void (ofBaseApp::*)(ofKeyEventArgs&), int)::{lambda(void const*, ofKeyEventArgs&)#1}::operator()(void const*, ofKeyEventArgs&) const (ofEvent.h:230)
==7169== 
...

In this log, there is only a mention of the line ofApp.cpp:27, which is, in my case:

...
 26       //~ mytimer->start(*mytcallback);
 27       timer.start(*callback);
 28       break;
...

I also tried this variant - with an new moved to the keyboard handler and explicit delete added in the oneShotCallback:

...
void ofApp::setup() {
  ofSetLogLevel(OF_LOG_VERBOSE); //(OF_LOG_NOTICE);
  ofLogNotice() << "ofApp::setup";
  timer.setStartInterval(1250);
  //~ callback = new Poco::TimerCallback<ofApp>(*this, &ofApp::oneShotCallback);
}

void ofApp::update() {
}

void ofApp::draw() {
}

void ofApp::keyPressed(int key) {
  ofLogVerbose("ofApp") << "keyPressed() key = " << key;
  switch(key) {
    case 'f': //
      ofLogVerbose("ofApp") << "'f'ireing one-shot";
      callback = new Poco::TimerCallback<ofApp>(*this, &ofApp::oneShotCallback); // moved from setup()
      timer.start(*callback);
      break;
  }
}

void ofApp::oneShotCallback(Poco::Timer& timer) {
  ofLogVerbose("ofApp") << "oneShotCallback HAS BEEN CALLED!!!";
  delete callback;
}
...

… and it crashes in exactly the same way as your code.


Then, I found http://stackoverflow.com/questions/19032171/poco-timer-example - and while that uses a separate handler class which I’d like to avoid, I tried changing callback so it is not a pointer anymore:

ofApp.h

...
  Poco::TimerCallback<ofApp> callback;
...

ofApp.cpp

...
void ofApp::setup() {
  ofSetLogLevel(OF_LOG_VERBOSE); //(OF_LOG_NOTICE);
  ofLogNotice() << "ofApp::setup";
  timer.setStartInterval(1250);
}
...
void ofApp::keyPressed(int key) {
  ofLogVerbose("ofApp") << "keyPressed() key = " << key;
  switch(key) {
    case 'f': //
      ofLogVerbose("ofApp") << "'f'ireing one-shot";
      callback = Poco::TimerCallback<ofApp>(*this, &ofApp::oneShotCallback);
      timer.start(callback);
      break;
  }
}

void ofApp::oneShotCallback(Poco::Timer& timer) {
  ofLogVerbose("ofApp") << "oneShotCallback HAS BEEN CALLED!!!";
  timer.stop();
}
...

… and this won’t even compile:

/path/to/openFrameworks/apps/myApps/testOneshot/src/main.cpp: In function ‘int main()’:
/path/to/openFrameworks/apps/myApps/testOneshot/src/main.cpp:14:22: error: use of deleted function ‘ofApp::ofApp()’
   ofRunApp(new ofApp());
                      ^
In file included from /path/to/openFrameworks/apps/myApps/testOneshot/src/main.cpp:3:0:
/path/to/openFrameworks/apps/myApps/testOneshot/src/ofApp.h:7:7: note: ‘ofApp::ofApp()’ is implicitly deleted because the default definition would be ill-formed:
 class ofApp: public ofBaseApp
       ^
In file included from /path/to/openFrameworks/apps/myApps/testOneshot/src/ofApp.h:5:0,
                 from /path/to/openFrameworks/apps/myApps/testOneshot/src/main.cpp:3:
/path/to/openFrameworks/libs/poco/include/Poco/Timer.h:221:2: error: ‘Poco::TimerCallback<C>::TimerCallback() [with C = ofApp]’ is private
  TimerCallback();
  ^
In file included from /path/to/openFrameworks/apps/myApps/testOneshot/src/main.cpp:3:0:
/path/to/openFrameworks/apps/myApps/testOneshot/src/ofApp.h:7:7: error: within this context
 class ofApp: public ofBaseApp
       ^
...

This error is mentioned in http://stackoverflow.com/questions/22083662/c-strange-is-private-error, but I tried adding friend Poco::TimerCallback<ofApp>::TimerCallback(); to ofApp.h, it doesn’t help.

So I guess the question still stands - how should I use a one-shot delay / timer in openFrameworks without causing a memory leak?

According to this example: http://stackoverflow.com/questions/19032171/poco-timer-example

Maybe something like this might work:

ofApp::ofApp():callback(*this, &ofApp::oneShotCallback){
}

void ofApp::setup(){
...

Otherwise I sometimes add really primitive timers as follows (not really useful if you have more than one or two though):

class ofApp{
    void update(){
          if(nextTrigger != 0 && ofGetElapsedTimeMillis()>nextTrigger){
                //do something
                nextTrigger = 0;
          }
    }

    void trigger(){
          nextTrigger = ofGetElapsedTimeMillis()+1500;
    }

    uint64_t nextTrigger;
}
1 Like

Many thanks again, @underdoeg :

Hm, I’m not sure I understand - should this be a function declaration, or definition - or should it be a function call? ofApp::ofApp() would as a constructor, at best return an object, but :callback(...) implies we’re calling a static function on a class, but then we have braces {} which imply a function definition? In any case, I tried that line verbatim, and it doesn’t compile:

...
/path/to/openFrameworks/apps/myApps/testOneshot/src/ofApp.cpp:6:14: error: definition of implicitly-declared ‘ofApp::ofApp()’
 ofApp::ofApp():callback(*this, &ofApp::oneShotCallback){ }
              ^
make[1]: *** [obj/linux/Debug/src/ofApp.o] Error 1
...

Thanks, that looks reasonable, but I will need more than one timer - and Poco::Timers would actually be very nice (semantically) for me to use, as long as I can find out how to get rid of the memory leak issue…

That is an initializer list. Basically it allows you to pass data to the constructors to your variables.

Check this link. Maybe it will clear up things

http://en.cppreference.com/w/cpp/language/initializer_list

1 Like

Ah, thanks for that @underdoeg - that explains things; there were simply some spaces missing from the code, then… So, I tried this:

ofApp.h:

...
class ofApp: public ofBaseApp
{
public:
  ofApp();
  void setup();
...

ofApp.cpp:

#include "ofApp.h"
#include "Poco/ThreadPool.h"

// testOneshot

ofApp::ofApp() : callback(*this, &ofApp::oneShotCallback){ } 

void ofApp::setup() {
  ofSetLogLevel(OF_LOG_VERBOSE); //(OF_LOG_NOTICE);
  ofLogNotice() << "ofApp::setup";
  timer.setStartInterval(1250);
}

void ofApp::update() {
}

void ofApp::draw() {
}

void ofApp::keyPressed(int key) {
  ofLogVerbose("ofApp") << "keyPressed() key = " << key;
  switch(key) {
    case 'f': //
      ofLogVerbose("ofApp") << "'f'ireing one-shot";
      timer.start(callback);
      break;
  }
}

void ofApp::oneShotCallback(Poco::Timer& timer) {
  ofLogVerbose("ofApp") << "oneShotCallback HAS BEEN CALLED!!!";
  timer.stop();
  Poco::ThreadPool::defaultPool().joinAll();
}

… and while the initialization passes without a problem, still on second press of f, the program crashes as before:

[notice ] ofApp::setup
[verbose] ofApp: keyPressed() key = 102
[verbose] ofApp: 'f'ireing one-shot
[verbose] ofApp: oneShotCallback HAS BEEN CALLED!!!
[verbose] ofApp: keyPressed() key = 102
[verbose] ofApp: 'f'ireing one-shot
terminate called after throwing an instance of 'Poco::IllegalStateException'
  what():  Illegal state
[verbose] ofSignalHandler: Aborted
Aborted (core dumped)

So, unfortunately, this doesn’t work either…

Ok, I arrived at a partial solution, so I’m going to post about it here. First, a little review of the problems encountered so far:

  • In the OP code, PoCo’s Timer and TimerCallback are instantiated as pointers through new; this code generally works, however it introduces a memory leak, so it is unsafe to use
  • Transitioning PoCo’s Timer and TimerCallback to “proper” typed variables (instead of pointers) is not trivial, since the templated Poco::TimerCallback<ofApp> class variable cannot be instantiated in code (as it will raise “error: ‘Poco::TimerCallback<C>::TimerCallback() [with C = ofApp]’ is private”) - this can be solved, as @underdoeg noted, by adding an ofApp constructor with an initializer list
  • Even with “proper” PoCo Timer and TimerCallback variables, a Poco::IllegalStateException is raised upon repeated (though not the first) firings of the one-shot delay

After looking a bit for the causes of the third problem, I found this:

http://pocoproject.org/docs/Poco.Timer.html

void stop(); - Stops the timer. If the callback method is currently running it will be allowed to finish first. WARNING: Never call this method from within the callback method, as a deadlock would result. To stop the timer from within the callback method, call restart(0).

… and as you can see, in most of my attempts, I call timer.stop() exactly from the callback method, oneShotCallback! So, after finding Reading row with null values causes exception in SQLite · Issue #1158 · pocoproject/poco · GitHub, I wrapped the timer.start() call in this:

...
      try {
        timer.start(callback);
      } catch(Poco::IllegalStateException e) {
        ofLogVerbose("ofApp") << e.displayText();
      }
...

… and could notice the following message:

[notice ] ofApp::setup
[verbose] ofApp: keyPressed() key = 102
[verbose] ofApp: 'f'ireing one-shot
[verbose] ofApp: oneShotCallback HAS BEEN CALLED!!!
[verbose] ofApp: keyPressed() key = 102
[verbose] ofApp: 'f'ireing one-shot
[verbose] ofApp: Illegal state: Timer already running *************
...

Ahaa: Illegal state: Timer already running - so probably the timer doesn’t auto-stop itself once the callback is triggered, nor does the timer.stop() do anything about that when called in oneShotCallback(), probably for the reason stated above. In fact, this message is generated by:

poco/Foundation/src/Timer.cpp (note, I may have some slightly older source code):

...
void Timer::start(const AbstractTimerCallback& method, Thread::Priority priority, ThreadPool& threadPool)
{
        Clock nextInvocation;
        nextInvocation += static_cast<Clock::ClockVal>(_startInterval)*1000;

        FastMutex::ScopedLock lock(_mutex);

        if (_pCallback)
        {
                throw Poco::IllegalStateException("Timer already running");
        }
...
void Timer::stop()
{
        FastMutex::ScopedLock lock(_mutex);
        if (_pCallback)
        {
                _periodicInterval = 0;
                _mutex.unlock();
                _wakeUp.set();
                _done.wait(); // warning: deadlock if called from timer callback
                _mutex.lock();
                delete _pCallback;
                _pCallback = 0;
        }
}
...

So, as per include/Poco/Timer.h, _pCallback is a private variable of Timer class, and indeed it is set to 0 by Timer::stop() explicitly - and that is done by no other function of Timer, which is why the restart(0) recommended above doesn’t work either.

So, the problem becomes: how to call the timer.stop() after the one-shot callback has been executed - or rather, from where, since we cannot call it at the end of the callback function? After some digging, I found Poco Util/testsuite/src/TimerTest.cpp, which uses a Poco::Event _event for synchronization:

http://pocoproject.org/docs/Poco.Event.html :

An Event is a synchronization object that allows one thread to signal one or more other threads that a certain event has happened. Usually, one thread signals an event, while one or more other threads wait for an event to become signalled.

So, with that, I finally got to a code which can fire repeated one-shot delays, without crashing, and without detected memory leaks related to them:

ofApp.h

#pragma once

#include "ofMain.h"
#include "Poco/Timer.h"
#include "Poco/Event.h"

class ofApp: public ofBaseApp
{
public:
  ofApp();
  void setup();
  void update();
  void draw();

  void keyPressed(int key);

  void oneShotCallback(Poco::Timer& timer);

  Poco::Timer timer;
  Poco::TimerCallback<ofApp> callback;
  Poco::Event _event;
};

ofApp.cpp

#include "ofApp.h"
#include "Poco/ThreadPool.h"

// testOneshot

// initializer list for `callback`; else "error: ‘Poco::TimerCallback... is private"
ofApp::ofApp() : callback(*this, &ofApp::oneShotCallback){ }

void ofApp::setup() {
  ofSetLogLevel(OF_LOG_VERBOSE); //(OF_LOG_NOTICE);
  ofLogNotice() << "ofApp::setup";
  timer.setStartInterval(1250);
}

void ofApp::update() {
}

void ofApp::draw() {
}

void ofApp::keyPressed(int key) {
  ofLogVerbose("ofApp") << "keyPressed() key = " << key;
  switch(key) {
    case 'f': //
      ofLogVerbose("ofApp") << "'f'ireing one-shot";
      try {
        timer.start(callback);
      } catch(Poco::IllegalStateException e) {
        ofLogVerbose("ofApp") << e.displayText();
      }
      _event.wait();
      timer.stop();
      break;
  }
}

void ofApp::oneShotCallback(Poco::Timer& timer) {
  ofLogVerbose("ofApp") << "oneShotCallback HAS BEEN CALLED!!!";
  _event.set();
}

So, the trick here is that, after we call timer.start() from the keyboard handler keyPressed as before, in the same handler we do _event.wait();, which only proceeds to timer.stop() after the _event.set() in the oneShotCallback() has been called.

It is important to note that, - while I haven’t seen this mentioned explicitly anywhere, - the _event.wait(); call seems to be blocking, at least in the context of its calling function, here keyPressed(). That is, with this code, once we get to the _event.wait(); in keyPressed(), it will block (even if other threads may continue to run) - which means that all other keyboard processing will be blocked until the .wait() completes! That means that if I press f twice 250 ms apart:

… I will get the corresponding callbacks at at least 1250 ms (since the keyPressed() will detect the second press only after the first _event.wait(); has concluded).

This is only a “partial” solution, because - on the other hand, the code in OP (which leaked memory), reacted somewhat like this:

… which means that if this is desired, probably some sort of (a) queue(s) (maybe a std::vector of Poco::TimerCallback<ofApp>* pointers, along with one containing Poco::Timer* pointers?!) needs to be implemented, so each time a new timer/callback pointer is obtained, it is pushed to this(these) queue(s) ; and each time a callback completes, a pointer is popped from this/these queue(s) and deleted - except this (along with possible timer stopping) has to be done outside of the callback function, so probably some additional signalling variables would have to be added, too (and then all this ends with a special class for managing multiple one-shot delays).

OK, here is finally a working example, with a class TimerExample that can manage multiple firing of one-shots, using arbitrary methods of ofApp as callback handlers and variable delay time; it internally uses three std::vector arrays to manage the state of timers and callbacks, so it is able to delete their memory after the callbacks executed (though you must signal the end of callback “manually”). Confirmed that the behavior is like:

… and that valgrind does not report a memory leak with this.

ofApp.h

#pragma once

#include "ofMain.h"
#include "Poco/Timer.h"


class ofApp; // forward declare

class TimerExample {
public:
  std::vector<Poco::Timer*> timers_arr;
  std::vector<Poco::TimerCallback<ofApp>*> callbacks_arr;
  std::vector<int> signalCallbackDone_arr;
  void start(ofApp* ofAppRef, const Poco::TimerCallback<ofApp>::Callback& method, long startInterval) {
    Poco::Timer* mytimer = new Poco::Timer(startInterval, 0); // 1250 = 1.25 sec after
    Poco::TimerCallback<ofApp>* mytcallback = new Poco::TimerCallback<ofApp>(*ofAppRef, method);
    mytimer->start(*mytcallback);
    timers_arr.push_back(mytimer);
    callbacks_arr.push_back(mytcallback);
  }
  void update() {
    while (signalCallbackDone_arr.size()>0) {
      int curind = signalCallbackDone_arr.back();
      signalCallbackDone_arr.pop_back();
      if (timers_arr.size() > 0) {
        Poco::Timer* mytimer = timers_arr[curind];
        Poco::TimerCallback<ofApp>* mytcallback = callbacks_arr[curind];
        timers_arr.erase(timers_arr.begin() + curind);
        callbacks_arr.erase(callbacks_arr.begin() + curind);
        mytimer->stop();
        delete mytimer;
        delete mytcallback;
      }
    }
  }
  void signalCallbackDone(Poco::Timer& intimer) {
    int curind = -1;
    for(size_t i = 0; i < timers_arr.size(); i++){
      Poco::Timer* ttimer = timers_arr[i];
      if (ttimer == &intimer) { curind = i; break; }
    } // end for
    if (curind > -1) {
      signalCallbackDone_arr.push_back(curind);
    }
  }
};


class ofApp: public ofBaseApp
{
public:
  void setup();
  void update();
  void draw();

  void keyPressed(int key);

  void oneShotCallback(Poco::Timer& timer);
  TimerExample timex;
};

ofApp.cpp

// testOneshot
#include "ofApp.h"

void ofApp::setup() {
  ofSetLogLevel(OF_LOG_VERBOSE); //(OF_LOG_NOTICE);
  ofLogNotice() << "ofApp::setup";
}

void ofApp::update() {
  timex.update();
}

void ofApp::draw() {
}

void ofApp::keyPressed(int key) {
  ofLogVerbose("ofApp") << "keyPressed() key = " << key;
  switch(key) {
    case 'f': //
      ofLogVerbose("ofApp") << "'f'ireing one-shot";
      timex.start(this, &ofApp::oneShotCallback, 1250);
      break;
  }
}

void ofApp::oneShotCallback(Poco::Timer& timer) {
  ofLogVerbose("ofApp") << "oneShotCallback HAS BEEN CALLED!!!";
  timex.signalCallbackDone(timer); // signal end..
}

Let’s hope that was it with Poco one-shot timers…

I’m currently working on an addon called ofxControl, featuring LFOs, ramps, and two timer classes (for one shot and repeated function calls). It’s quite easy to handle. I’ll post a note here once i’ll have the alpha version.

1 Like