#ifndef _PYPRODUCER_H #define _PYPRODUCER_H namespace NppPythonScript { template class PyProducerConsumer { public: PyProducerConsumer(); virtual ~PyProducerConsumer(); void startConsumer(); void stopConsumer(); bool consumerBusy(); protected: bool produce(const std::shared_ptr& data); virtual void consume(const std::shared_ptr& data) = 0; virtual void queueComplete() { }; DWORD getConsumerThreadID() { return m_dwThreadId; }; private: HANDLE m_queueMutex; HANDLE m_dataAvailable; HANDLE m_shutdown; std::queue > m_queue; DWORD m_dwThreadId; HANDLE m_hConsumerThread; bool m_consuming; void consumer(); static void threadStart(PyProducerConsumer *instance); }; template PyProducerConsumer::PyProducerConsumer() : m_queueMutex(CreateMutex(NULL, FALSE, NULL)), m_dataAvailable(CreateEvent(NULL, TRUE, FALSE, NULL)), m_shutdown(CreateEvent(NULL, TRUE, FALSE, NULL)), m_dwThreadId(NULL), m_hConsumerThread(NULL), m_consuming(false) { } template PyProducerConsumer::~PyProducerConsumer() { stopConsumer(); CloseHandle(m_queueMutex); m_queueMutex = NULL; CloseHandle(m_dataAvailable); m_dataAvailable = NULL; CloseHandle(m_shutdown); m_shutdown = NULL; } template void PyProducerConsumer::stopConsumer() { SetEvent(m_shutdown); if (m_hConsumerThread) { WaitForSingleObject(m_hConsumerThread, INFINITE); CloseHandle(m_hConsumerThread); m_hConsumerThread = NULL; } } template bool PyProducerConsumer::produce(const std::shared_ptr& data) { bool retVal = false; DWORD mutexResult = WaitForSingleObject(m_queueMutex, INFINITE); if (mutexResult == WAIT_OBJECT_0) { try { m_queue.push(data); retVal = true; } catch(...) { } ReleaseMutex(m_queueMutex); SetEvent(m_dataAvailable); } return retVal; } template void PyProducerConsumer::startConsumer() { if (!m_hConsumerThread) { m_hConsumerThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)threadStart, this, 0, &m_dwThreadId); } } template bool PyProducerConsumer::consumerBusy() { return m_consuming; } template void PyProducerConsumer::consumer() { HANDLE waitHandles[] = {m_dataAvailable, m_shutdown}; bool queueEmpty; for(;;) { DWORD waitResult = WaitForMultipleObjects(2, waitHandles, FALSE, INFINITE); if (waitResult == WAIT_OBJECT_0 + 1 || waitResult == WAIT_ABANDONED_0 + 1) // Shutdown signalled { // Shutdown immediately // An alternative would be to "finish up" the queue // but we don't want to do that in PyScript - just close N++ break; } m_consuming = true; DWORD queueAvailable = WaitForSingleObject(m_queueMutex, INFINITE); if (queueAvailable == WAIT_OBJECT_0) { queueEmpty = false; std::shared_ptr data = m_queue.front(); m_queue.pop(); if (m_queue.empty()) { queueEmpty = true; ResetEvent(m_dataAvailable); } ReleaseMutex(m_queueMutex); consume(data); if (queueEmpty) { waitResult = WaitForSingleObject(m_dataAvailable, 0); if (waitResult != WAIT_OBJECT_0) { m_consuming = false; queueComplete(); } } } } } template void PyProducerConsumer::threadStart(PyProducerConsumer *instance) { instance->consumer(); } } #endif