Files
Sourcetrail/src/lib/utility/messaging/MessageQueue.cpp
T
Eberhard Graether f13aec70dd ui: Added tabs UI to top of main window (issue #215)
* Added tab bar to top of main window with UI/shortcuts similar to web browsers
* ComponentManager offers components for 2 use-cases: application and tab
* Application components: log view (status/errors), bookmarks, screen search, status bar, tabs, tooltips, dialogs and all tab views disabled
* Tab components: graph, code, history and search
* When a project is loaded there is always at least one tab, otherwise there is none
* Each tab replaces the application views of the same name in the main layout when activated
* Each tab has it's own TaskScheduler, to process tasks independent from other tabs
* The application has a global TaskScheduler for application wide tasks
* The singloton class TaskManager is responsible for managing the TaskSchedulers
* MessageListeners and Messages hold an optional schedulerId, both must be set to only send to a specific TaskScheduler
* Bookmark buttons where split off into a separate view per tab, bookmark managemement is done in the application
* History menu processing is done by the QtMainWindow now
* Screen search is an application component, the responders are always changed to the active tab
* Plugin messages are opened in a new tab
* Symbols can be opened in a new tab via middle click/context menu in graph, code, history dropdown
* Full text index creation is mutexed now in PersistenStorage to make it fully thread-safe
* All tabs are closed when switching projects
* Tab contents are only animated when visible

fortune cookie message = Catch your lucky star!
2018-11-05 01:28:12 +01:00

320 lines
6.6 KiB
C++

#include "MessageQueue.h"
#include <chrono>
#include <thread>
#include "logging.h"
#include "MessageBase.h"
#include "MessageFilter.h"
#include "MessageListenerBase.h"
#include "TaskGroupParallel.h"
#include "TaskGroupSequence.h"
#include "TaskLambda.h"
#include "TabId.h"
std::shared_ptr<MessageQueue> MessageQueue::getInstance()
{
if (!s_instance)
{
s_instance = std::shared_ptr<MessageQueue>(new MessageQueue());
}
return s_instance;
}
MessageQueue::~MessageQueue()
{
std::lock_guard<std::mutex> lock(m_listenersMutex);
for (size_t i = 0; i < m_listeners.size(); i++)
{
m_listeners[i]->removedListener();
}
m_listeners.clear();
}
void MessageQueue::registerListener(MessageListenerBase* listener)
{
std::lock_guard<std::mutex> lock(m_listenersMutex);
m_listeners.push_back(listener);
}
void MessageQueue::unregisterListener(MessageListenerBase* listener)
{
std::lock_guard<std::mutex> lock(m_listenersMutex);
for (size_t i = 0; i < m_listeners.size(); i++)
{
if (m_listeners[i] == listener)
{
m_listeners.erase(m_listeners.begin() + i);
// m_currentListenerIndex and m_listenersLength need to be updated in case this happens while a message is
// handled.
if (i <= m_currentListenerIndex)
{
m_currentListenerIndex--;
}
if (i < m_listenersLength)
{
m_listenersLength--;
}
return;
}
}
LOG_ERROR("Listener was not found");
}
MessageListenerBase* MessageQueue::getListenerById(Id listenerId) const
{
std::lock_guard<std::mutex> lock(m_listenersMutex);
for (size_t i = 0; i < m_listeners.size(); i++)
{
if (m_listeners[i]->getId() == listenerId)
{
return m_listeners[i];
}
}
return nullptr;
}
void MessageQueue::addMessageFilter(std::shared_ptr<MessageFilter> filter)
{
m_filters.push_back(filter);
}
void MessageQueue::pushMessage(std::shared_ptr<MessageBase> message)
{
std::lock_guard<std::mutex> lock(m_messageBufferMutex);
m_messageBuffer.push_back(message);
}
void MessageQueue::processMessage(std::shared_ptr<MessageBase> message, bool asNextTask)
{
if (message->isLogged())
{
LOG_INFO_BARE(L"send " + message->str());
}
if (m_sendMessagesAsTasks && message->sendAsTask())
{
sendMessageAsTask(message, asNextTask);
}
else
{
sendMessage(message);
}
}
void MessageQueue::startMessageLoopThreaded()
{
std::thread(&MessageQueue::startMessageLoop, this).detach();
std::lock_guard<std::mutex> lock(m_threadMutex);
m_threadIsRunning = true;
}
void MessageQueue::startMessageLoop()
{
{
std::lock_guard<std::mutex> lock(m_loopMutex);
if (m_loopIsRunning)
{
LOG_ERROR("Loop is already running");
return;
}
m_loopIsRunning = true;
}
while (true)
{
processMessages();
{
std::lock_guard<std::mutex> lock(m_loopMutex);
if (!m_loopIsRunning)
{
break;
}
}
std::this_thread::sleep_for(std::chrono::milliseconds(25));
}
{
std::lock_guard<std::mutex> lock(m_threadMutex);
if (m_threadIsRunning)
{
m_threadIsRunning = false;
}
}
}
void MessageQueue::stopMessageLoop()
{
{
std::lock_guard<std::mutex> lock(m_loopMutex);
if (!m_loopIsRunning)
{
LOG_WARNING("Loop is not running");
}
m_loopIsRunning = false;
}
while (true)
{
{
std::lock_guard<std::mutex> lock(m_threadMutex);
if (!m_threadIsRunning)
{
break;
}
}
std::this_thread::sleep_for(std::chrono::milliseconds(25));
}
}
bool MessageQueue::loopIsRunning() const
{
std::lock_guard<std::mutex> lock(m_loopMutex);
return m_loopIsRunning;
}
bool MessageQueue::hasMessagesQueued() const
{
std::lock_guard<std::mutex> lock(m_messageBufferMutex);
return m_messageBuffer.size() > 0;
}
void MessageQueue::setSendMessagesAsTasks(bool sendMessagesAsTasks)
{
m_sendMessagesAsTasks = sendMessagesAsTasks;
}
std::shared_ptr<MessageQueue> MessageQueue::s_instance;
MessageQueue::MessageQueue()
: m_currentListenerIndex(0)
, m_listenersLength(0)
, m_loopIsRunning(false)
, m_threadIsRunning(false)
, m_sendMessagesAsTasks(false)
{
}
void MessageQueue::processMessages()
{
while (true)
{
std::shared_ptr<MessageBase> message;
{
std::lock_guard<std::mutex> lock(m_messageBufferMutex);
for (std::shared_ptr<MessageFilter> filter : m_filters)
{
if (!m_messageBuffer.size())
{
break;
}
filter->filter(&m_messageBuffer);
}
if (!m_messageBuffer.size())
{
break;
}
message = m_messageBuffer.front();
m_messageBuffer.pop_front();
}
processMessage(message, false);
}
}
void MessageQueue::sendMessage(std::shared_ptr<MessageBase> message)
{
std::lock_guard<std::mutex> lock(m_listenersMutex);
// m_listenersLength is saved, so that new listeners registered whithin message handling don't get the
// current message and the length can be reduced when a listener gets unregistered.
m_listenersLength = m_listeners.size();
// The currentListenerIndex holds the index of the current listener being handled, so it can be changed when a
// listener gets removed while message handling.
for (m_currentListenerIndex = 0; m_currentListenerIndex < m_listenersLength; m_currentListenerIndex++)
{
MessageListenerBase* listener = m_listeners[m_currentListenerIndex];
if (listener->getType() == message->getType() &&
(message->getSchedulerId() == 0 || listener->getSchedulerId() == 0 ||
listener->getSchedulerId() == message->getSchedulerId()))
{
// The listenersMutex gets unlocked so changes to listeners are possible while message handling.
m_listenersMutex.unlock();
listener->handleMessageBase(message.get());
m_listenersMutex.lock();
}
}
}
void MessageQueue::sendMessageAsTask(std::shared_ptr<MessageBase> message, bool asNextTask) const
{
std::shared_ptr<TaskGroup> taskGroup;
if (message->isParallel())
{
taskGroup = std::make_shared<TaskGroupParallel>();
}
else
{
taskGroup = std::make_shared<TaskGroupSequence>();
}
{
std::lock_guard<std::mutex> lock(m_listenersMutex);
for (size_t i = 0; i < m_listeners.size(); i++)
{
MessageListenerBase* listener = m_listeners[i];
if (listener->getType() == message->getType() &&
(message->getSchedulerId() == 0 || listener->getSchedulerId() == 0 ||
listener->getSchedulerId() == message->getSchedulerId()))
{
Id listenerId = listener->getId();
taskGroup->addTask(std::make_shared<TaskLambda>(
[listenerId, message]()
{
MessageListenerBase* listener = MessageQueue::getInstance()->getListenerById(listenerId);
if (listener)
{
listener->handleMessageBase(message.get());
}
}
));
}
}
}
Id schedulerId = message->getSchedulerId();
if (!schedulerId)
{
schedulerId = TabId::app();
}
if (asNextTask)
{
Task::dispatchNext(schedulerId, taskGroup);
}
else
{
Task::dispatch(schedulerId, taskGroup);
}
}