You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
133 lines
4.4 KiB
133 lines
4.4 KiB
3 months ago
|
// License: Apache 2.0. See LICENSE file in root directory.
|
||
|
// Copyright(c) 2021 Intel Corporation. All Rights Reserved.
|
||
|
|
||
|
#include <rsutils/concurrency/concurrency.h>
|
||
|
#include <rsutils/easylogging/easyloggingpp.h>
|
||
|
#include <rsutils/time/waiting-on.h>
|
||
|
|
||
|
dispatcher::dispatcher( unsigned int cap, std::function< void( action ) > on_drop_callback )
|
||
|
: _queue( cap, on_drop_callback )
|
||
|
, _was_stopped( true )
|
||
|
, _is_alive( true )
|
||
|
{
|
||
|
// We keep a running thread that takes stuff off our queue and dispatches them
|
||
|
_thread = std::thread([&]()
|
||
|
{
|
||
|
int timeout_ms = 5000;
|
||
|
while( _is_alive )
|
||
|
{
|
||
|
if( _wait_for_start( timeout_ms ) )
|
||
|
{
|
||
|
std::function< void(cancellable_timer) > item;
|
||
|
if (_queue.dequeue(&item, timeout_ms))
|
||
|
{
|
||
|
cancellable_timer time(this);
|
||
|
|
||
|
try
|
||
|
{
|
||
|
// While we're dispatching the item, we cannot stop!
|
||
|
std::lock_guard< std::mutex > lock(_dispatch_mutex);
|
||
|
item(time);
|
||
|
}
|
||
|
catch (const std::exception& e)
|
||
|
{
|
||
|
LOG_ERROR("Dispatcher [" << this << "] exception caught: " << e.what());
|
||
|
}
|
||
|
catch (...)
|
||
|
{
|
||
|
LOG_ERROR("Dispatcher [" << this << "] unknown exception caught!");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
|
||
|
dispatcher::~dispatcher()
|
||
|
{
|
||
|
// Don't get into any more dispatches
|
||
|
_is_alive = false;
|
||
|
|
||
|
// Stop whatever's in-progress, if any
|
||
|
stop();
|
||
|
|
||
|
// Wait until our worker thread quits
|
||
|
if( _thread.joinable() )
|
||
|
_thread.join();
|
||
|
}
|
||
|
|
||
|
|
||
|
void dispatcher::start()
|
||
|
{
|
||
|
{
|
||
|
std::lock_guard< std::mutex > lock(_was_stopped_mutex);
|
||
|
_was_stopped = false;
|
||
|
}
|
||
|
_queue.start();
|
||
|
// Wake up all threads that wait for the dispatcher to start
|
||
|
_was_stopped_cv.notify_all();
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
void dispatcher::stop()
|
||
|
{
|
||
|
// With the following commented-out if, we have issues!
|
||
|
// It seems stop is called multiple times and the queues are somehow waiting on something after
|
||
|
// the first time. If we return, those queues aren't woken! If we continue, the only effect will
|
||
|
// be to notify_all and we get good behavior...
|
||
|
//
|
||
|
//if( _was_stopped )
|
||
|
// return;
|
||
|
|
||
|
// First things first: don't accept any more incoming stuff, and get rid of anything
|
||
|
// pending
|
||
|
_queue.stop();
|
||
|
|
||
|
// Wait until any dispatched is done...
|
||
|
{
|
||
|
std::lock_guard< std::mutex > lock(_dispatch_mutex);
|
||
|
assert(_queue.empty());
|
||
|
}
|
||
|
// Signal we've stopped so any sleeping dispatched will wake up immediately
|
||
|
{
|
||
|
std::lock_guard< std::mutex > lock( _was_stopped_mutex );
|
||
|
_was_stopped = true;
|
||
|
}
|
||
|
_was_stopped_cv.notify_all();
|
||
|
}
|
||
|
|
||
|
|
||
|
// Return when all current items in the queue are finished (within a timeout).
|
||
|
// If additional items are added while we're waiting, those will not be waited on!
|
||
|
// Returns false if a timeout occurred before we were done
|
||
|
//
|
||
|
bool dispatcher::flush( std::chrono::steady_clock::duration timeout )
|
||
|
{
|
||
|
if( _was_stopped )
|
||
|
return true; // Nothing to do - so success (no timeout)
|
||
|
|
||
|
rsutils::time::waiting_on< bool > invoked( _was_stopped_cv, _was_stopped_mutex, false );
|
||
|
// Blocking call, we don't want the item in the queue to drop if the queue is full.
|
||
|
// TODO - Add a timeout to blocking invoke, Currently it can wait forever here.
|
||
|
auto invoked_in_thread = invoked.in_thread();
|
||
|
invoke( [invoked_in_thread]( cancellable_timer ) { invoked_in_thread.signal( true ); }, true );
|
||
|
invoked.wait_until( timeout, [&]() { return invoked || _was_stopped; } );
|
||
|
return invoked;
|
||
|
}
|
||
|
|
||
|
// Return true if dispatcher is started (within a timeout).
|
||
|
// false if not or the dispatcher is no longer alive
|
||
|
//
|
||
|
bool dispatcher::_wait_for_start( int timeout_ms )
|
||
|
{
|
||
|
// If the dispatcher is not started wait for a start event, if not such event within given timeout do nothing.
|
||
|
// If during the wait the thread destructor is called (_is_alive = false) do nothing as well.
|
||
|
std::unique_lock< std::mutex > lock(_was_stopped_mutex);
|
||
|
return _was_stopped_cv.wait_for(lock, std::chrono::milliseconds(timeout_ms), [this]() {
|
||
|
return !_was_stopped.load() || !_is_alive;
|
||
|
} ) && _is_alive;
|
||
|
}
|
||
|
|