diff --git a/MUtilities_VS2013.vcxproj b/MUtilities_VS2013.vcxproj index a1f9d34..a0fab79 100644 --- a/MUtilities_VS2013.vcxproj +++ b/MUtilities_VS2013.vcxproj @@ -24,6 +24,7 @@ + @@ -39,6 +40,7 @@ + diff --git a/MUtilities_VS2013.vcxproj.filters b/MUtilities_VS2013.vcxproj.filters index 272fdb4..657655d 100644 --- a/MUtilities_VS2013.vcxproj.filters +++ b/MUtilities_VS2013.vcxproj.filters @@ -78,6 +78,9 @@ Source Files + + Source Files + @@ -131,6 +134,9 @@ Public Headers + + Public Headers + diff --git a/include/MUtils/IPCChannel.h b/include/MUtils/IPCChannel.h new file mode 100644 index 0000000..a175bca --- /dev/null +++ b/include/MUtils/IPCChannel.h @@ -0,0 +1,58 @@ +/////////////////////////////////////////////////////////////////////////////// +// MuldeR's Utilities for Qt +// Copyright (C) 2004-2014 LoRd_MuldeR +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +// http://www.gnu.org/licenses/lgpl-2.1.txt +////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include + +namespace MUtils +{ + typedef enum + { + IPC_RET_SUCCESS_MASTER = 0, + IPC_RET_SUCCESS_SLAVE = 1, + IPC_RET_ALREADY_INITIALIZED = 2, + IPC_RET_FAILURE = 3 + } + ipc_result_t; + + class MUTILS_API IPCChannel_Private; + + class MUTILS_API IPCChannel + { + public: + IPCChannel(const QString &applicationId, const QString &channelId); + ~IPCChannel(void); + + int initialize(void); + + bool send(const unsigned int &command, const char *const message); + bool read(unsigned int &command, char *const message, const size_t &buffSize); + + private: + IPCChannel(const IPCChannel&) : p(NULL) {} + IPCChannel &operator=(const IPCChannel&) { return *this; } + + IPCChannel_Private *const p; + const QString m_applicationId; + const QString m_channelId; + }; +} diff --git a/include/MUtils/JobObject.h b/include/MUtils/JobObject.h index 62fb4be..b6b47ad 100644 --- a/include/MUtils/JobObject.h +++ b/include/MUtils/JobObject.h @@ -22,7 +22,6 @@ #pragma once #include -#include class QProcess; @@ -40,6 +39,6 @@ namespace MUtils bool terminateJob(unsigned int exitCode); private: - QScopedPointer p; + JobObject_Private *const p; }; } diff --git a/src/IPCChannel.cpp b/src/IPCChannel.cpp new file mode 100644 index 0000000..9aab938 --- /dev/null +++ b/src/IPCChannel.cpp @@ -0,0 +1,337 @@ +/////////////////////////////////////////////////////////////////////////////// +// MuldeR's Utilities for Qt +// Copyright (C) 2004-2014 LoRd_MuldeR +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +// http://www.gnu.org/licenses/lgpl-2.1.txt +////////////////////////////////////////////////////////////////////////////////// + +//MUtils +#include +#include + +//Qt includes +#include +#include +#include +#include + +/////////////////////////////////////////////////////////////////////////////// +// TYPES +/////////////////////////////////////////////////////////////////////////////// + +namespace MUtils +{ + static const size_t IPC_SLOTS = 128; + static const size_t MAX_MESSAGE_LEN = 4096; + + typedef struct + { + unsigned int command; + unsigned int reserved_1; + unsigned int reserved_2; + char parameter[MAX_MESSAGE_LEN]; + } + ipc_data_t; + + typedef struct + { + unsigned int pos_wr; + unsigned int pos_rd; + ipc_data_t data[IPC_SLOTS]; + } + ipc_t; +} + +/////////////////////////////////////////////////////////////////////////////// +// UTILITIES +/////////////////////////////////////////////////////////////////////////////// + +static inline QString ESCAPE(QString str) +{ + return str.replace(QRegExp("[^A-Za-z0-9_]"), "_").toLower(); +} + +static QString MAKE_ID(const QString &applicationId, const QString &channelId, const QString &itemId) +{ + return QString("ipc://mutilities.muldersoft.com:37402/%1/%2/%3").arg(ESCAPE(applicationId), ESCAPE(channelId), ESCAPE(itemId)); +} + +/////////////////////////////////////////////////////////////////////////////// +// PRIVATE DATA +/////////////////////////////////////////////////////////////////////////////// + +namespace MUtils +{ + class IPCChannel_Private + { + friend class IPCChannel; + + protected: + volatile bool initialized; + QScopedPointer sharedmem; + QScopedPointer semaphore_rd; + QScopedPointer semaphore_wr; + QReadWriteLock lock; + }; +} + +/////////////////////////////////////////////////////////////////////////////// +// CONSTRUCTOR & DESTRUCTOR +/////////////////////////////////////////////////////////////////////////////// + +MUtils::IPCChannel::IPCChannel(const QString &applicationId, const QString &channelId) +: + p(new IPCChannel_Private()), + m_applicationId(applicationId), + m_channelId(channelId) +{ + p->initialized = false; +} + +MUtils::IPCChannel::~IPCChannel(void) +{ + if(p->initialized) + { + if(p->sharedmem->isAttached()) + { + p->sharedmem->detach(); + } + } + + delete p; +} + +/////////////////////////////////////////////////////////////////////////////// +// INITIALIZATION +/////////////////////////////////////////////////////////////////////////////// + +int MUtils::IPCChannel::initialize(void) +{ + QWriteLocker writeLock(&p->lock); + + if(p->initialized) + { + return IPC_RET_ALREADY_INITIALIZED; + } + + p->sharedmem.reset(new QSharedMemory(MAKE_ID(m_applicationId, m_channelId, "sharedmem"), NULL)); + p->semaphore_rd.reset(new QSystemSemaphore(MAKE_ID(m_applicationId, m_channelId, "semaphore_rd"), 0)); + p->semaphore_wr.reset(new QSystemSemaphore(MAKE_ID(m_applicationId, m_channelId, "semaphore_wr"), 0)); + + if(p->semaphore_rd->error() != QSystemSemaphore::NoError) + { + const QString errorMessage = p->semaphore_rd->errorString(); + qWarning("Failed to create system smaphore: %s", MUTILS_UTF8(errorMessage)); + return IPC_RET_FAILURE; + } + + if(p->semaphore_wr->error() != QSystemSemaphore::NoError) + { + const QString errorMessage = p->semaphore_wr->errorString(); + qWarning("Failed to create system smaphore: %s", MUTILS_UTF8(errorMessage)); + return IPC_RET_FAILURE; + } + + if(!p->sharedmem->create(sizeof(ipc_t))) + { + if(p->sharedmem->error() == QSharedMemory::AlreadyExists) + { + if(!p->sharedmem->attach()) + { + const QString errorMessage = p->sharedmem->errorString(); + qWarning("Failed to attach to shared memory: %s", MUTILS_UTF8(errorMessage)); + return IPC_RET_FAILURE; + } + if(p->sharedmem->error() != QSharedMemory::NoError) + { + const QString errorMessage = p->sharedmem->errorString(); + qWarning("Failed to attach to shared memory: %s", MUTILS_UTF8(errorMessage)); + return IPC_RET_FAILURE; + } + p->initialized = true; + return IPC_RET_SUCCESS_SLAVE; + } + else + { + const QString errorMessage = p->sharedmem->errorString(); + qWarning("Failed to create shared memory: %s", MUTILS_UTF8(errorMessage)); + return IPC_RET_FAILURE; + } + } + + if(p->sharedmem->error() != QSharedMemory::NoError) + { + const QString errorMessage = p->sharedmem->errorString(); + qWarning("Failed to create shared memory: %s", MUTILS_UTF8(errorMessage)); + return IPC_RET_FAILURE; + } + + if(void *const data = p->sharedmem->data()) + { + memset(data, 0, sizeof(ipc_t)); + } + + if(!p->semaphore_wr->release(IPC_SLOTS)) + { + const QString errorMessage = p->semaphore_wr->errorString(); + qWarning("Failed to release system semaphore: %s", MUTILS_UTF8(errorMessage)); + return IPC_RET_FAILURE; + } + + p->initialized = true; + return IPC_RET_SUCCESS_MASTER; +} + +/////////////////////////////////////////////////////////////////////////////// +// SEND MESSAGE +/////////////////////////////////////////////////////////////////////////////// + +bool MUtils::IPCChannel::send(const unsigned int &command, const char *const message) +{ + bool success = false; + QReadLocker readLock(&p->lock); + + if(!p->initialized) + { + MUTILS_THROW("Shared memory for IPC not initialized yet."); + } + + ipc_data_t ipc_data; + memset(&ipc_data, 0, sizeof(ipc_data_t)); + ipc_data.command = command; + + if(message) + { + strncpy_s(ipc_data.parameter, MAX_MESSAGE_LEN, message, _TRUNCATE); + } + + if(!p->semaphore_wr->acquire()) + { + const QString errorMessage = p->semaphore_wr->errorString(); + qWarning("Failed to acquire system semaphore: %s", MUTILS_UTF8(errorMessage)); + return false; + } + + if(!p->sharedmem->lock()) + { + const QString errorMessage = p->sharedmem->errorString(); + qWarning("Failed to lock shared memory: %s", MUTILS_UTF8(errorMessage)); + return false; + } + + if(ipc_t *const ptr = reinterpret_cast(p->sharedmem->data())) + { + success = true; + memcpy(&ptr->data[ptr->pos_wr], &ipc_data, sizeof(ipc_data_t)); + ptr->pos_wr = (ptr->pos_wr + 1) % IPC_SLOTS; + } + else + { + qWarning("Shared memory pointer is NULL -> unable to write data!"); + } + + if(!p->sharedmem->unlock()) + { + const QString errorMessage = p->sharedmem->errorString(); + qWarning("Failed to unlock shared memory: %s", MUTILS_UTF8(errorMessage)); + return false; + } + + if(!p->semaphore_rd->release()) + { + const QString errorMessage = p->semaphore_rd->errorString(); + qWarning("Failed to acquire release semaphore: %s", MUTILS_UTF8(errorMessage)); + return false; + } + + return success; +} + +/////////////////////////////////////////////////////////////////////////////// +// READ MESSAGE +/////////////////////////////////////////////////////////////////////////////// + +bool MUtils::IPCChannel::read(unsigned int &command, char *const message, const size_t &buffSize) +{ + bool success = false; + QReadLocker readLock(&p->lock); + + command = 0; + if(message && (buffSize > 0)) + { + message[0] = '\0'; + } + + if(!p->initialized) + { + MUTILS_THROW("Shared memory for IPC not initialized yet."); + } + + ipc_data_t ipc_data; + memset(&ipc_data, 0, sizeof(ipc_data_t)); + + if(!p->semaphore_rd->acquire()) + { + const QString errorMessage = p->semaphore_rd->errorString(); + qWarning("Failed to acquire system semaphore: %s", MUTILS_UTF8(errorMessage)); + return false; + } + + if(!p->sharedmem->lock()) + { + const QString errorMessage = p->sharedmem->errorString(); + qWarning("Failed to lock shared memory: %s", MUTILS_UTF8(errorMessage)); + return false; + } + + if(ipc_t *const ptr = reinterpret_cast(p->sharedmem->data())) + { + success = true; + memcpy(&ipc_data, &ptr->data[ptr->pos_rd], sizeof(ipc_data_t)); + ptr->pos_rd = (ptr->pos_rd + 1) % IPC_SLOTS; + + if(!(ipc_data.reserved_1 || ipc_data.reserved_2)) + { + command = ipc_data.command; + strncpy_s(message, buffSize, ipc_data.parameter, _TRUNCATE); + } + else + { + qWarning("Malformed IPC message, will be ignored"); + } + } + else + { + qWarning("Shared memory pointer is NULL -> unable to write data!"); + } + + if(!p->sharedmem->unlock()) + { + const QString errorMessage = p->sharedmem->errorString(); + qWarning("Failed to unlock shared memory: %s", MUTILS_UTF8(errorMessage)); + return false; + } + + if(!p->semaphore_wr->release()) + { + const QString errorMessage = p->semaphore_wr->errorString(); + qWarning("Failed to acquire release semaphore: %s", MUTILS_UTF8(errorMessage)); + return false; + } + + return success; +} diff --git a/src/JobObject_Win32.cpp b/src/JobObject_Win32.cpp index 51c1295..b39a54a 100644 --- a/src/JobObject_Win32.cpp +++ b/src/JobObject_Win32.cpp @@ -84,6 +84,8 @@ MUtils::JobObject::~JobObject(void) CloseHandle(p->m_hJobObject); p->m_hJobObject = NULL; } + + delete p; } bool MUtils::JobObject::addProcessToJob(const QProcess *proc)