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} ${CMAKE_CURRENT_BINARY_DIR}
${QT_INCLUDE_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}) 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 install(TARGETS sodep
DESTINATION ${LIB_INSTALL_DIR}) DESTINATION ${LIB_INSTALL_DIR})
@ -20,4 +20,5 @@ install(FILES
sodepvalue.h sodepvalue.h
sodepfault.h sodepfault.h
sodepmessage.h sodepmessage.h
sodeppendingcall.h
DESTINATION ${INCLUDE_INSTALL_DIR}) DESTINATION ${INCLUDE_INSTALL_DIR})

View File

@ -19,92 +19,26 @@
*/ */
#include "sodepclient.h" #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 "sodepclientthread_p.h"
#include "sodepmessage.h"
#include "sodeppendingcall.h"
struct SodepRequest
{
enum { Send, Receive } type;
SodepMessage message;
};
class SodepClientPrivate SodepClient::SodepClient(const QString &hostName, quint16 port)
{
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)
: d(new SodepClientPrivate(this)) : d(new SodepClientPrivate(this))
{ {
d->device = device; d->readerThread = new SodepClientThread(hostName, port, d);
d->readerThread = new SodepClientThread(device, this); d->readerThread->start();
} }
SodepClient::~SodepClient() SodepClient::~SodepClient()
{ {
delete d->readerThread;
delete d; 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 SodepClient::Error SodepClient::error() const
{ {
return d->error; return d->error;
@ -115,63 +49,23 @@ QString SodepClient::errorString() const
return d->errorString; return d->errorString;
} }
void SodepClientPrivate::startRequest(int id) SodepPendingCall SodepClient::asyncCall(const SodepMessage &message)
{ {
currentRequestId = id; Q_ASSERT(!d->pendingCalls.contains(message.id()));
d->pendingCalls[message.id()] = new SodepPendingCallPrivate(message);
SodepRequest &request = queuedRequests[id]; d->readerThread->sendMessage(message);
return SodepPendingCall(d->pendingCalls[message.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);
}
} }
void SodepClientPrivate::emitCurrentRequestFinished() SodepMessage SodepClient::call(const SodepMessage &message)
{ {
SodepRequest request = queuedRequests.take(currentRequestId); SodepPendingCall pending = asyncCall(message);
pending.waitForFinished();
if (request.type == SodepRequest::Receive) { return pending.reply();
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;
}
} }
void SodepClientPrivate::_k_messageLoaded(const SodepMessage &message) void SodepClientPrivate::messageReceived(const SodepMessage &message)
{ {
queuedRequests[currentRequestId].message = message; QExplicitlySharedDataPointer<SodepPendingCallPrivate> pending = pendingCalls.take(message.id());
emitCurrentRequestFinished(); 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 #ifndef SODEPCLIENT_H
#define SODEPCLIENT_H #define SODEPCLIENT_H
#include <QtCore/QObject> #include <QtCore/QtGlobal>
class QIODevice; class QIODevice;
class QObject;
class SodepClientPrivate; class SodepClientPrivate;
class SodepMessage; class SodepMessage;
class SodepPendingCall;
class Q_DECL_EXPORT SodepClient : public QObject class Q_DECL_EXPORT SodepClient
{ {
Q_OBJECT
Q_ENUMS(Error)
public: public:
enum Error enum Error
{ {
@ -41,24 +40,16 @@ public:
UnkownError UnkownError
}; };
explicit SodepClient(QIODevice *device); explicit SodepClient(const QString &hostName, quint16 port);
~SodepClient(); ~SodepClient();
int requestMessage();
int postMessage(const SodepMessage &message);
Error error() const; Error error() const;
QString errorString() const; QString errorString() const;
signals: SodepPendingCall asyncCall(const SodepMessage &message);
void requestStarted(int id); SodepMessage call(const SodepMessage &message);
void requestFinished(int id, const SodepMessage &message, bool hasError);
private: private:
Q_PRIVATE_SLOT(d, void _k_messageLoaded(const SodepMessage&))
Q_PRIVATE_SLOT(d, void _k_bytesWritten())
friend class SodepClientPrivate; friend class SodepClientPrivate;
SodepClientPrivate * const d; 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 "sodepclientthread_p.h"
#include <QtCore/QTimer>
#include <QtNetwork/QTcpSocket>
#include "sodepclient_p.h"
#include "sodepmessage.h" #include "sodepmessage.h"
#include "sodephelpers_p.h" #include "sodephelpers_p.h"
SodepClientThread::SodepClientThread(QIODevice *device, QObject *parent) SodepClientThread::SodepClientThread(const QString &hostName, quint16 port, SodepClientPrivate *client)
: QThread(parent), m_device(device), m_quit(false) : QThread(), m_hostName(hostName), m_port(port), m_socket(0), m_client(client)
{ {
moveToThread(this);
} }
SodepClientThread::~SodepClientThread() SodepClientThread::~SodepClientThread()
{ {
m_quit = true; quit();
m_cond.wakeOne();
wait(); wait();
} }
void SodepClientThread::requestMessage() void SodepClientThread::sendMessage(const SodepMessage &message)
{ {
if (!isRunning()) { QMutexLocker locker(&m_mutex);
start();
} else { m_messageQueue.enqueue(message);
m_cond.wakeOne(); 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() void SodepClientThread::run()
{ {
while (!m_quit) { m_socket = new QTcpSocket;
QMutexLocker locker(&m_mutex);
SodepMessage message = sodepReadMessage(*m_device); connect(m_socket, SIGNAL(readyRead()),
emit messageLoaded(message); 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" #include "sodepclientthread_p.moc"

View File

@ -23,32 +23,42 @@
#include <QtCore/QThread> #include <QtCore/QThread>
#include <QtCore/QMutex> #include <QtCore/QMutex>
#include <QtCore/QWaitCondition> #include <QtCore/QQueue>
class QIODevice; class QAbstractSocket;
class SodepMessage; class SodepMessage;
class SodepClientPrivate;
class SodepClientThread : public QThread class SodepClientThread : public QThread
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit SodepClientThread(QIODevice *device, QObject *parent = 0); explicit SodepClientThread(const QString &hostName, quint16 port, SodepClientPrivate *client);
~SodepClientThread(); ~SodepClientThread();
void requestMessage();
void run(); void run();
void sendMessage(const SodepMessage &message);
signals: signals:
void messageLoaded(const SodepMessage &message); void messageReceived(const SodepMessage &message);
private slots:
void readMessage();
void writeMessageQueue();
private: private:
QIODevice *m_device; QString m_hostName;
quint16 m_port;
QAbstractSocket *m_socket;
SodepClientPrivate *m_client;
QQueue<SodepMessage> m_messageQueue;
QMutex m_mutex; QMutex m_mutex;
QWaitCondition m_cond;
bool m_quit;
}; };
#endif #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; QProcess m_metaserviceProcess;
QTcpSocket m_socket; QTcpSocket m_socket;
SodepClient m_client;
public: public:
SodepMetaServiceTest() SodepMetaServiceTest()
: QObject(), m_client(&m_socket) : QObject()
{ {
qRegisterMetaType<SodepMessage>(); qRegisterMetaType<SodepMessage>();
} }
@ -130,25 +129,11 @@ private slots:
void shouldListServices() void shouldListServices()
{ {
SodepClient client("localhost", 9000);
SodepMessage message("/", "getServices"); SodepMessage message("/", "getServices");
QSignalSpy spy(&m_client, SIGNAL(requestFinished(int, const SodepMessage&, bool))); SodepMessage reply = client.call(message);
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 expected("/", "getServices"); SodepMessage expected("/", "getServices");
SodepValue value; SodepValue value;
@ -162,11 +147,7 @@ private slots:
value.children("service") << s1 << s2; value.children("service") << s1 << s2;
expected.setData(value); expected.setData(value);
QCOMPARE(spy.count(), 1); sodepCompare(reply, expected);
signal = spy.takeFirst();
QCOMPARE(signal.at(0).toInt(), id);
sodepCompare(signal.at(1).value<SodepMessage>(), expected);
QCOMPARE(signal.at(2).toBool(), false);
} }
void shouldPlaceServiceCalls_data() void shouldPlaceServiceCalls_data()