As we have rounded up the message implementation on the template proxy, we can move ahead and do the same in the Vicon driver, as we have a demo coming up soon.
Short recap on getting JSON parameters
TL;DR
- Use .value(key, default) for safety and convenience
- Use contains() to check existence
- For optional/nested keys, combine .value or .contains
- Use .at() or operator[] only if you are sure the key exists => throws if not existing
So our config detect and hardware detect for Vicon are working.
When we try to run, there are still some problems:
- Do a hardware detection 2 times and there are two device threads
A bit of a struggle : communication thread pool
We had a crash closing the engine in the next scenario:
- Start engine, no UI
- Click 'h' to start a hardware detection
- Click 'q' to quit the engine => Boom we had a crash in the Destructor of the CProxyTestDevice that is statically created with NODE_REGISTER.
The reason is this: if we open a TCP communication through a CCommunicationObject (CProxyTestDevice has one through its inheritance from CProxyDevice), then a CCommunicationThread is created that runs a seperate thread to get telegrams and put these on a buffer. The communication object does not own this thread, it only has a std::weak_ptr<CCommunicationThread> to it. The actual owner of the thread is a static map that holds all communication threads for each port:
static std::map<UINT /*port*/, std::shared_ptr<CCommunicationThread>> &getMapThreads()
{
static std::map<UINT /*port*/, std::shared_ptr<CCommunicationThread>> m_mapThreads;
return m_mapThreads;
};
The purpose is to have shared threads per port.
The static map offers the next functions:
- AssignCommunicationThread : assigns a thread to a communicationobject, creates one if needed
- CloseCommunicationThread : when the communcation is closed
- CloseAllConnections : should be called in the main shutdown
The big problem here is that we have a static CNode derivation through the NODE_REGISTER, when we use a static std::map to keep globally track of things, then there is no guarantee that this map is destroyed after all the statically created nodes!
So in this case the problems started because of the next sequence:
- Hardwaredetect is called on the static CProxyTestDevice, that goes for all hardware detection
- This opens a communication port and a thread and it remains open
- when quitting the engine the communication on this static CProxyTestDevice is still open, but the map is already gone => boom
Finally these solutions where implemented:
- Communication objects hold a weak pointer to the communication thread, the map has the shared pointer. If the thread was deleted, the weak pointer will remain empty, so we know it is already closed
- Instead of declaring static map, we declared a static map& getStatic (The Phoenix Singleton )
- And we also protected the access to the map with a mutex, which was not done yet.
- Also changed in CCommunicationThread from raw CSocket* pointers to smart unique pointer: std::list<std::unique_ptr<CSocket>>
References:
- CCommunicationObject
- AssignCommunicationThread
- CloseCommunicationThread
- CloseAllConnections
- Commit id of this fix bb0b4bae28abb316273f9a3026dd43ba8777bce8