338 lines
9.1 KiB
C++
338 lines
9.1 KiB
C++
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
// MuldeR's Utilities for Qt
|
||
|
// Copyright (C) 2004-2014 LoRd_MuldeR <MuldeR2@GMX.de>
|
||
|
//
|
||
|
// 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 <MUtils/IPCChannel.h>
|
||
|
#include <MUtils/Exception.h>
|
||
|
|
||
|
//Qt includes
|
||
|
#include <QRegExp>
|
||
|
#include <QSharedMemory>
|
||
|
#include <QSystemSemaphore>
|
||
|
#include <QWriteLocker>
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
// 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<QSharedMemory> sharedmem;
|
||
|
QScopedPointer<QSystemSemaphore> semaphore_rd;
|
||
|
QScopedPointer<QSystemSemaphore> 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<ipc_t*>(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<ipc_t*>(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;
|
||
|
}
|