I created a wrapper around boost :: asio :: io_service to handle asynchronous tasks in the OpenGL application GUI thread.
Tasks can be created from other threads, so boost::asio seems ideal for this purpose and means that I donβt need to write my own task queue using the associated mutexes and locking. I want the work to be performed on each frame below an acceptable threshold (for example, 5 ms), so I call poll_one until the desired budget is exceeded, instead of calling run . As far as I can explain this, I need to call reset whenever new tasks appear that seem to work well.
Since this is short, everything is here, without #include :
typedef std::function<void(void)> VoidFunc; typedef std::shared_ptr<class UiTaskQueue> UiTaskQueueRef; class UiTaskQueue { public: static UiTaskQueueRef create() { return UiTaskQueueRef( new UiTaskQueue() ); } ~UiTaskQueue() {}
I save an instance of UiTaskQueueRef in my main application class and call mUiTaskQueue->update() from the application animation loop.
I would like to extend the functionality of this class so that the task can be canceled. My previous implementation (using almost the same interface) returned a numeric identifier for each task and allowed the execution of tasks using this identifier. But now queue management and related locking are handled by boost::asio I'm not sure how best to do this.
I tried to wrap any tasks that I could cancel in shared_ptr and create a wrapper object that stores weak_ptr for the task and implements the operator () so that it can be passed to io_service . It looks like this:
struct CancelableTask { CancelableTask( std::weak_ptr<VoidFunc> f ): mFunc(f) {} void operator()(void) const { std::shared_ptr<VoidFunc> f = mFunc.lock(); if (f) { (*f)(); } } std::weak_ptr<VoidFunc> mFunc; };
Then I have an overload of my pushTask method, which looks like this:
void pushTask( std::weak_ptr<VoidFunc> f ) { mService.post( CancelableTask(f) ); mService.reset(); }
Then I send the canceled tasks to the queue using:
std::function<void(void)> *task = new std::function<void(void)>( boost::bind(&MyApp::doUiTask, this) ); mTask = std::shared_ptr< std::function<void(void)> >( task ); mUiTaskQueue->pushTask( std::weak_ptr< std::function<void(void)> >( mTask ) );
Or using VoidFunc typedef if you prefer:
VoidFunc *task = new VoidFunc( std::bind(&MyApp::doUiTask, this) ); mTask = std::shared_ptr<VoidFunc>( task ); mUiTaskQueue->pushTask( std::weak_ptr<VoidFunc>( mTask ) );
As long as I save shared_ptr to mTask , then io_service will complete the task. If I call reset on mTask , then weak_ptr cannot block and the task is skipped as desired.
My question is really sure that all these new tools are: new std::function<void(void)>( std::bind( ... ) ) OK, what should I do and a safe thing to manage with shared_ptr ?