Big API refactoring in the SODEP binding. Now you can place call

(synchronous or asynchronous) like you would do with QtDBUS for
instance.

svn path=/branches/work/~ervin/sodep/; revision=953134
This commit is contained in:
Kevin Ottens 2009-04-13 12:39:26 +00:00
parent 8952d159aa
commit a8068361b0
10 changed files with 343 additions and 191 deletions

View File

@ -4,11 +4,11 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_BINARY_DIR}
${QT_INCLUDE_DIR})
set(sodep_LIB_SRCS sodepclient.cpp sodepclientthread.cpp sodepvalue.cpp sodepfault.cpp sodepmessage.cpp)
set(sodep_LIB_SRCS sodepclient.cpp sodepclientthread.cpp sodepvalue.cpp sodepfault.cpp sodepmessage.cpp sodeppendingcall.cpp)
kde4_add_library(sodep SHARED ${sodep_LIB_SRCS})
target_link_libraries(sodep ${QT_QTCORE_LIBRARY})
target_link_libraries(sodep ${QT_QTCORE_LIBRARY} ${QT_QTNETWORK_LIBRARY})
install(TARGETS sodep
DESTINATION ${LIB_INSTALL_DIR})
@ -20,4 +20,5 @@ install(FILES
sodepvalue.h
sodepfault.h
sodepmessage.h
sodeppendingcall.h
DESTINATION ${INCLUDE_INSTALL_DIR})

View File

@ -19,92 +19,26 @@
*/
#include "sodepclient.h"
#include "sodepclient_p.h"
#include <QtCore/QHash>
#include <QtCore/QIODevice>
#include <QtCore/QTimer>
#include "sodepmessage.h"
#include "sodephelpers_p.h"
#include "sodepclientthread_p.h"
#include "sodepmessage.h"
#include "sodeppendingcall.h"
struct SodepRequest
{
enum { Send, Receive } type;
SodepMessage message;
};
class SodepClientPrivate
{
public:
SodepClientPrivate(SodepClient *client)
: q(client), device(0),
error(SodepClient::NoError),
lastAllocatedRequestId(0),
currentRequestId(-1) {}
void startRequest(int id);
void emitCurrentRequestFinished();
void _k_messageLoaded(const SodepMessage &message);
void _k_bytesWritten();
SodepClient * const q;
QIODevice *device;
SodepClientThread *readerThread;
SodepClient::Error error;
QString errorString;
int lastAllocatedRequestId;
int currentRequestId;
QHash<int, SodepRequest> queuedRequests;
};
SodepClient::SodepClient(QIODevice *device)
SodepClient::SodepClient(const QString &hostName, quint16 port)
: d(new SodepClientPrivate(this))
{
d->device = device;
d->readerThread = new SodepClientThread(device, this);
d->readerThread = new SodepClientThread(hostName, port, d);
d->readerThread->start();
}
SodepClient::~SodepClient()
{
delete d->readerThread;
delete d;
}
int SodepClient::requestMessage()
{
int id = ++d->lastAllocatedRequestId;
SodepRequest request;
request.type = SodepRequest::Receive;
d->queuedRequests[id] = request;
if (d->currentRequestId==-1) {
d->startRequest(id);
}
return id;
}
int SodepClient::postMessage(const SodepMessage &message)
{
int id = ++d->lastAllocatedRequestId;
SodepRequest request;
request.type = SodepRequest::Send;
request.message = message;
d->queuedRequests[id] = request;
if (d->currentRequestId==-1) {
d->startRequest(id);
}
return id;
}
SodepClient::Error SodepClient::error() const
{
return d->error;
@ -115,63 +49,23 @@ QString SodepClient::errorString() const
return d->errorString;
}
void SodepClientPrivate::startRequest(int id)
SodepPendingCall SodepClient::asyncCall(const SodepMessage &message)
{
currentRequestId = id;
SodepRequest &request = queuedRequests[id];
error = SodepClient::NoError;
errorString = QString();
emit q->requestStarted(id);
if (request.type == SodepRequest::Receive) {
QObject::connect(readerThread, SIGNAL(messageLoaded(const SodepMessage&)),
q, SLOT(_k_messageLoaded(const SodepMessage&)));
readerThread->requestMessage();
} else {
QObject::connect(device, SIGNAL(bytesWritten(qint64)),
q, SLOT(_k_bytesWritten()));
SodepMessage &message = request.message;
sodepWrite(*device, message);
}
Q_ASSERT(!d->pendingCalls.contains(message.id()));
d->pendingCalls[message.id()] = new SodepPendingCallPrivate(message);
d->readerThread->sendMessage(message);
return SodepPendingCall(d->pendingCalls[message.id()]);
}
void SodepClientPrivate::emitCurrentRequestFinished()
SodepMessage SodepClient::call(const SodepMessage &message)
{
SodepRequest request = queuedRequests.take(currentRequestId);
if (request.type == SodepRequest::Receive) {
QObject::disconnect(readerThread, SIGNAL(messageLoaded(const SodepMessage&)),
q, SLOT(_k_messageLoaded(const SodepMessage&)));
} else {
QObject::disconnect(device, SIGNAL(bytesWritten(qint64)),
q, SLOT(_k_bytesWritten()));
}
emit q->requestFinished(currentRequestId, request.message, false);
if (currentRequestId<lastAllocatedRequestId) {
startRequest(currentRequestId+1);
} else {
currentRequestId = -1;
}
SodepPendingCall pending = asyncCall(message);
pending.waitForFinished();
return pending.reply();
}
void SodepClientPrivate::_k_messageLoaded(const SodepMessage &message)
void SodepClientPrivate::messageReceived(const SodepMessage &message)
{
queuedRequests[currentRequestId].message = message;
emitCurrentRequestFinished();
QExplicitlySharedDataPointer<SodepPendingCallPrivate> pending = pendingCalls.take(message.id());
pending->setReply(message);
}
void SodepClientPrivate::_k_bytesWritten()
{
if (device->bytesToWrite()>0) {
return;
}
emitCurrentRequestFinished();
}
#include "sodepclient.moc"

View File

@ -21,18 +21,17 @@
#ifndef SODEPCLIENT_H
#define SODEPCLIENT_H
#include <QtCore/QObject>
#include <QtCore/QtGlobal>
class QIODevice;
class QObject;
class SodepClientPrivate;
class SodepMessage;
class SodepPendingCall;
class Q_DECL_EXPORT SodepClient : public QObject
class Q_DECL_EXPORT SodepClient
{
Q_OBJECT
Q_ENUMS(Error)
public:
enum Error
{
@ -41,24 +40,16 @@ public:
UnkownError
};
explicit SodepClient(QIODevice *device);
explicit SodepClient(const QString &hostName, quint16 port);
~SodepClient();
int requestMessage();
int postMessage(const SodepMessage &message);
Error error() const;
QString errorString() const;
signals:
void requestStarted(int id);
void requestFinished(int id, const SodepMessage &message, bool hasError);
SodepPendingCall asyncCall(const SodepMessage &message);
SodepMessage call(const SodepMessage &message);
private:
Q_PRIVATE_SLOT(d, void _k_messageLoaded(const SodepMessage&))
Q_PRIVATE_SLOT(d, void _k_bytesWritten())
friend class SodepClientPrivate;
SodepClientPrivate * const d;
};

View File

@ -0,0 +1,53 @@
/**
* This file is part of the KDE project
* Copyright (C) 2009 Kevin Ottens <ervin@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef SODEPCLIENT_P_H
#define SODEPCLIENT_P_H
#include "sodepclient.h"
#include "sodeppendingcall_p.h"
#include <QtCore/QMap>
#include <QtCore/QObject>
class SodepClientThread;
class SodepClientPrivate : public QObject
{
Q_OBJECT
public:
SodepClientPrivate(SodepClient *client)
: q(client),
error(SodepClient::NoError) {}
public slots:
void messageReceived(const SodepMessage &message);
private:
friend class SodepClient;
SodepClient * const q;
SodepClientThread *readerThread;
SodepClient::Error error;
QString errorString;
QMap<int, QExplicitlySharedDataPointer<SodepPendingCallPrivate> > pendingCalls;
};
#endif

View File

@ -20,41 +20,71 @@
#include "sodepclientthread_p.h"
#include <QtCore/QTimer>
#include <QtNetwork/QTcpSocket>
#include "sodepclient_p.h"
#include "sodepmessage.h"
#include "sodephelpers_p.h"
SodepClientThread::SodepClientThread(QIODevice *device, QObject *parent)
: QThread(parent), m_device(device), m_quit(false)
SodepClientThread::SodepClientThread(const QString &hostName, quint16 port, SodepClientPrivate *client)
: QThread(), m_hostName(hostName), m_port(port), m_socket(0), m_client(client)
{
moveToThread(this);
}
SodepClientThread::~SodepClientThread()
{
m_quit = true;
m_cond.wakeOne();
quit();
wait();
}
void SodepClientThread::requestMessage()
void SodepClientThread::sendMessage(const SodepMessage &message)
{
if (!isRunning()) {
start();
} else {
m_cond.wakeOne();
QMutexLocker locker(&m_mutex);
m_messageQueue.enqueue(message);
QTimer::singleShot(0, this, SLOT(writeMessageQueue()));
}
void SodepClientThread::writeMessageQueue()
{
QMutexLocker locker(&m_mutex);
while (!m_messageQueue.isEmpty()) {
sodepWrite(*m_socket, m_messageQueue.dequeue());
}
}
void SodepClientThread::readMessage()
{
if (m_socket->bytesAvailable()==0) {
return;
}
SodepMessage message = sodepReadMessage(*m_socket);
emit messageReceived(message);
if (m_socket->bytesAvailable()>0) {
QTimer::singleShot(0, this, SLOT(readMessage()));
}
}
void SodepClientThread::run()
{
while (!m_quit) {
QMutexLocker locker(&m_mutex);
m_socket = new QTcpSocket;
SodepMessage message = sodepReadMessage(*m_device);
emit messageLoaded(message);
connect(m_socket, SIGNAL(readyRead()),
this, SLOT(readMessage()), Qt::QueuedConnection);
connect(this, SIGNAL(messageReceived(SodepMessage)),
m_client, SLOT(messageReceived(SodepMessage)));
m_cond.wait(&m_mutex);
}
m_socket->connectToHost(m_hostName, m_port);
m_socket->waitForConnected(-1);
exec();
delete m_socket;
}
#include "sodepclientthread_p.moc"

View File

@ -23,32 +23,42 @@
#include <QtCore/QThread>
#include <QtCore/QMutex>
#include <QtCore/QWaitCondition>
#include <QtCore/QQueue>
class QIODevice;
class QAbstractSocket;
class SodepMessage;
class SodepClientPrivate;
class SodepClientThread : public QThread
{
Q_OBJECT
public:
explicit SodepClientThread(QIODevice *device, QObject *parent = 0);
explicit SodepClientThread(const QString &hostName, quint16 port, SodepClientPrivate *client);
~SodepClientThread();
void requestMessage();
void run();
void sendMessage(const SodepMessage &message);
signals:
void messageLoaded(const SodepMessage &message);
void messageReceived(const SodepMessage &message);
private slots:
void readMessage();
void writeMessageQueue();
private:
QIODevice *m_device;
QString m_hostName;
quint16 m_port;
QAbstractSocket *m_socket;
SodepClientPrivate *m_client;
QQueue<SodepMessage> m_messageQueue;
QMutex m_mutex;
QWaitCondition m_cond;
bool m_quit;
};
#endif

View File

@ -0,0 +1,79 @@
/**
* This file is part of the KDE project
* Copyright (C) 2008 Kevin Ottens <ervin@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "sodeppendingcall.h"
#include "sodeppendingcall_p.h"
SodepPendingCall::SodepPendingCall(const SodepPendingCall &other)
: d(other.d)
{
}
SodepPendingCall::SodepPendingCall(QExplicitlySharedDataPointer<SodepPendingCallPrivate> dd)
: d(dd)
{
}
SodepPendingCall::~SodepPendingCall()
{
}
SodepPendingCall &SodepPendingCall::operator=(const SodepPendingCall &other)
{
d = other.d;
return *this;
}
bool SodepPendingCall::isFinished() const
{
return d->isFinished;
}
SodepMessage SodepPendingCall::reply() const
{
return d->reply;
}
void SodepPendingCall::waitForFinished()
{
SodepPendingCallWaiter waiter;
waiter.waitForFinished(d.data());
}
void SodepPendingCallPrivate::setReply(const SodepMessage &message)
{
Q_ASSERT(message.id()==id);
isFinished = true;
reply = message;
foreach (SodepPendingCallWaiter *waiter, waiters) {
waiter->eventLoop.quit();
}
waiters.clear();
}
void SodepPendingCallWaiter::waitForFinished(SodepPendingCallPrivate *pendingCall)
{
pendingCall->waiters << this;
eventLoop.exec();
}

View File

@ -0,0 +1,52 @@
/**
* This file is part of the KDE project
* Copyright (C) 2008 Kevin Ottens <ervin@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef SODEPPENDINGCALL_H
#define SODEPPENDINGCALL_H
#include <QtCore/QExplicitlySharedDataPointer>
class SodepClient;
class SodepPendingCallPrivate;
class SodepMessage;
class Q_DECL_EXPORT SodepPendingCall
{
public:
SodepPendingCall(const SodepPendingCall &other);
~SodepPendingCall();
SodepPendingCall &operator=(const SodepPendingCall &other);
bool isFinished() const;
SodepMessage reply() const;
void waitForFinished();
private:
friend class SodepClient;
SodepPendingCall(); // Not defined
SodepPendingCall(QExplicitlySharedDataPointer<SodepPendingCallPrivate> dd);
QExplicitlySharedDataPointer<SodepPendingCallPrivate> d;
};
#endif

View File

@ -0,0 +1,61 @@
/**
* This file is part of the KDE project
* Copyright (C) 2008 Kevin Ottens <ervin@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef SODEPPENDINGCALL_P_H
#define SODEPPENDINGCALL_P_H
#include <QtCore/QEventLoop>
#include <QtCore/QObject>
#include <QtCore/QSharedData>
#include "sodepmessage.h"
class SodepPendingCallPrivate;
class SodepPendingCallWaiter
{
public:
void waitForFinished(SodepPendingCallPrivate *pendingCall);
private:
friend class SodepPendingCallPrivate;
QEventLoop eventLoop;
};
class SodepPendingCallPrivate : public QSharedData
{
public:
SodepPendingCallPrivate(const SodepMessage &message)
: id(message.id()), isFinished(false) {}
void setReply(const SodepMessage &message);
private:
friend class SodepPendingCall;
friend class SodepPendingCallWaiter;
qint64 id;
bool isFinished;
SodepMessage reply;
QList<SodepPendingCallWaiter*> waiters;
};
#endif

View File

@ -68,11 +68,10 @@ class SodepMetaServiceTest : public QObject
QProcess m_metaserviceProcess;
QTcpSocket m_socket;
SodepClient m_client;
public:
SodepMetaServiceTest()
: QObject(), m_client(&m_socket)
: QObject()
{
qRegisterMetaType<SodepMessage>();
}
@ -130,25 +129,11 @@ private slots:
void shouldListServices()
{
SodepClient client("localhost", 9000);
SodepMessage message("/", "getServices");
QSignalSpy spy(&m_client, SIGNAL(requestFinished(int, const SodepMessage&, bool)));
int id = m_client.postMessage(message);
QEventLoop eventLoop;
connect(&m_client, SIGNAL(requestFinished(int, const SodepMessage&, bool)),
&eventLoop, SLOT(quit()));
eventLoop.exec();
QCOMPARE(spy.count(), 1);
QVariantList signal = spy.takeFirst();
QCOMPARE(signal.at(0).toInt(), id);
sodepCompare(signal.at(1).value<SodepMessage>(), message);
QCOMPARE(signal.at(2).toBool(), false);
id = m_client.requestMessage();
eventLoop.exec();
SodepMessage reply = client.call(message);
SodepMessage expected("/", "getServices");
SodepValue value;
@ -162,11 +147,7 @@ private slots:
value.children("service") << s1 << s2;
expected.setData(value);
QCOMPARE(spy.count(), 1);
signal = spy.takeFirst();
QCOMPARE(signal.at(0).toInt(), id);
sodepCompare(signal.at(1).value<SodepMessage>(), expected);
QCOMPARE(signal.at(2).toBool(), false);
sodepCompare(reply, expected);
}
void shouldPlaceServiceCalls_data()