I'm trying to implement a non-blocking serial communication in my C++ app. A thread is responsible to do serial communication, and I've written a ThreadSafeClass to exchange data between serial thread and main thread. Here is the core of my code:
main.cpp
#include "serial.hpp"
#include "tsqueue.hpp"
int main(int argc, char *argv[])
{
serial::init();
while (true)
{
fgets(s);
serial::outQueue.enqueue(std::string(s));
}
serial::shutdown();
return 0;
}
tsqueue.hpp
#include <mutex>
#include <queue>
namespace tsqueue
{
template <typename T>
class ThreadSafeQueue
{
private:
mutable std::mutex _mtx;
std::queue<T> _que;
public:
ThreadSafeQueue();
~ThreadSafeQueue();
void enqueue(const T &item);
T tryDequeue(const T &defaultValue, bool &done);
void clear();
bool isEmpty() const;
};
template <typename T>
ThreadSafeQueue<T>::ThreadSafeQueue() {}
template <typename T>
ThreadSafeQueue<T>::~ThreadSafeQueue() { clear(); }
template <typename T>
void tsqueue::ThreadSafeQueue<T>::enqueue(const T &item)
{
std::lock_guard<std::mutex> lock(_mtx);
_que.push(item);
}
template <typename T>
T tsqueue::ThreadSafeQueue<T>::tryDequeue(const T &defaultValue, bool &done)
{
std::lock_guard<std::mutex> lock(_mtx);
if (_que.empty())
{
done = false;
return defaultValue;
}
else
{
T item = _que.front();
_que.pop();
done = true;
return item;
}
}
} // namespace tsqueue
And serial declaration/definition,
serial.hpp
#include <string>
#include "tsqueue.hpp"
namespace serial
{
static tsqueue::ThreadSafeQueue<std::string> inQueue;
static tsqueue::ThreadSafeQueue<std::string> outQueue;
void init();
void shutdown();
}
serial.cpp
#include <string>
#include "serial.hpp"
#include "tsqueue.hpp"
static std::thread _thread;
void run()
{
while (true)
{
std::string str = serial::outQueue.tryDequeue(emptyStr, dequeued);
if (dequeued) { /* Do send 'str' */ }
if (terminationRequested) { break; }
// Some sleep
}
}
void serial::init()
{
serial::inQueue.clear();
serial::outQueue.clear();
_thread = std::thread(run);
}
void serial::shutdown()
{
if (_thread.joinable()) { _thread.join(); }
}
The problem is, when tryDequeue(...) is called by serial thread's run() in serial.cpp, it always sees empty outQueue. However while loop still sees outQueue in main.cpp with provided data, even at later times. I've find out that using debug tools of vscode. I'm new to C++, but experienced in other languages. What am I doing wrong in above code? Do run() and main() see different objects?
Compiler: g++ 7.3.0, Environment: Linux (Ubuntu 18.04)
Edit: If I remove static from definitions of inQueue and outQueue, I get multiple definition error by linker for both. Although I have appropriate include guards.