Skip to content

Commit

Permalink
Merge pull request #108 from sintefmath/cdyk_qt_run_functor_as_gui
Browse files Browse the repository at this point in the history
introduces an invoker object in the HTTP server that handles running functions as the main thread. This is used for grabbing images from opengl and for creating renderlists.
  • Loading branch information
cdyk committed Jun 6, 2014
2 parents 1d76555 + c5f6f71 commit d5a390f
Show file tree
Hide file tree
Showing 11 changed files with 331 additions and 211 deletions.
3 changes: 2 additions & 1 deletion include/tinia/qtcontroller/QTController.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ namespace jobcontroller {
namespace qtcontroller {
namespace impl {
class ServerController;
class Invoker;
}
class GUIBuilder;

Expand Down Expand Up @@ -84,7 +85,7 @@ class QTController : public jobcontroller::Controller
boost::scoped_ptr<QApplication> m_app;
boost::scoped_ptr<QMainWindow> m_main_window;


impl::Invoker* m_invoker; // Lifetime managed by qt parent-child machinery.
impl::ServerController* m_serverController; // Lifetime managed by qt parent-child machinery.
QToolBar* m_toolBar; // Lifetime managed by qt parent-child machinery.

Expand Down
16 changes: 7 additions & 9 deletions include/tinia/qtcontroller/impl/ServerThread.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "tinia/jobcontroller.hpp"
#include "tinia/model/impl/xml/XMLHandler.hpp"
#include "tinia/qtcontroller/moc/OpenGLServerGrabber.hpp"
#include "tinia/qtcontroller/moc/Invoker.hpp"

namespace tinia {
namespace qtcontroller {
Expand All @@ -12,18 +13,17 @@ namespace impl {
class ServerThread : public QRunnable
{
public:
explicit ServerThread(OpenGLServerGrabber& grabber,
explicit ServerThread(OpenGLServerGrabber* grabber,
Invoker* mainthread_invoker,
tinia::jobcontroller::Job* job,
int socket,
QObject *parent = 0);
int socket );

void run();

private:


bool isLongPoll(const QString& request);
void getSnapshotTxt(QTextStream& os, const QString& request);

/** Handles non-static content, if applicable.
* @returns true if the file is non-static, false otherwise.
Expand All @@ -33,8 +33,6 @@ class ServerThread : public QRunnable

void updateState(QTextStream& os, const QString& request);

void getRenderList(QTextStream& os, const QString& request);

/** Writes the error code to the stream formated as HTTP requires,
* with the optional message formated in HTML
*/
Expand All @@ -44,9 +42,9 @@ class ServerThread : public QRunnable
int m_socket;

tinia::model::impl::xml::XMLHandler m_xmlHandler;
tinia::jobcontroller::Job* m_job;

OpenGLServerGrabber& m_grabber;
tinia::jobcontroller::Job* m_job;
OpenGLServerGrabber* m_grabber;
Invoker* m_mainthread_invoker;
};

} // namespace impl
Expand Down
10 changes: 5 additions & 5 deletions include/tinia/qtcontroller/moc/HTTPServer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "tinia/jobcontroller.hpp"
#include "tinia/qtcontroller/moc/OpenGLServerGrabber.hpp"
#include "tinia/qtcontroller/moc/Invoker.hpp"
#include "tinia/model/impl/xml/XMLHandler.hpp"
#include <QTcpServer>
#include <QTcpSocket>
Expand All @@ -18,16 +19,15 @@ class HTTPServer : public QTcpServer
/**
* Takes control over imageSource
*/
explicit HTTPServer(tinia::jobcontroller::Job*,
tinia::qtcontroller::impl::OpenGLServerGrabber* imageSource,
explicit HTTPServer( tinia::jobcontroller::Job*,
QObject *parent = 0);

void incomingConnection(int socket);

private:
tinia::jobcontroller::Job* m_job;

boost::scoped_ptr<tinia::qtcontroller::impl::OpenGLServerGrabber> m_serverGrabber;
tinia::jobcontroller::Job* m_job;
OpenGLServerGrabber* m_serverGrabber; // Lifetime managed by Qt child-parent
Invoker* m_mainthread_invoker; // Lifetime managed by Qt child-parent.

};

Expand Down
56 changes: 56 additions & 0 deletions include/tinia/qtcontroller/moc/Invoker.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#pragma once

#include <QObject>
#include <QMutex>
#include <QRunnable>
#include <QWaitCondition>

namespace tinia {
namespace qtcontroller {
namespace impl {


class Invoker : public QObject
{
Q_OBJECT
public:


/** Create a new invoker object.
* \note Must be created in main thread.
*/
explicit Invoker( QObject* parent );

void
invokeInMainThread( QRunnable* function, bool block = true );


signals:

void
requestMainThreadInvocation( QRunnable* function, bool* i_am_done );

private slots:

void
handleMainThreadInvocation( QRunnable* function, bool *i_am_done );

private:
QThread* m_main_thread;
QMutex m_wait_mutex;

/** Shared wait conditions for all invocations.
*
* This wait condition is shared by all invocations, as the number of
* concurrent invocations is currently assumed to be low. We use a bool
* flag to specify which condition that is true.
*
* Refers to \ref m_wait_mutex.
*/
QWaitCondition m_wait_condition;

};

} // namespace impl
} // namespace qtcontroller
} // namespace tinia
69 changes: 34 additions & 35 deletions include/tinia/qtcontroller/moc/OpenGLServerGrabber.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,54 +11,53 @@ namespace tinia {
namespace qtcontroller {
namespace impl {

class OpenGLServerGrabber : public QObject, public ImageSource
class OpenGLServerGrabber : public QObject
{
Q_OBJECT
public:
explicit OpenGLServerGrabber(tinia::jobcontroller::Job* job,
QObject *parent = 0);
explicit OpenGLServerGrabber( QObject *parent );

~OpenGLServerGrabber();

void getImageAsText(QTextStream& os, unsigned int width, unsigned int height, QString key);
/** Mutex that governs exclusive access to this object. */
QMutex*
exclusiveAccessMutex()
{ return &m_mainMutex; }

/** Returns a pointer to the grabbed image.
*
* \note \ref exclusiveAccessMutex must be held before invocation.
*/
const unsigned char*
imageBuffer() const
{ return m_buffer; }

/** Grabs an image of a view
*
* \note Must be invoked in the thread that holds the OpenGL context,
* usually the main/GUI-thread.
* \note \ref exclusiveAccessMutex must be held before invocation.
*/
void
grab( tinia::jobcontroller::OpenGLJob* job,
unsigned int width,
unsigned int height,
const std::string &key );

void getRenderListUpdateResponse( QTextStream& response,
const QString& request );

signals:
void glImageReady();
void getGLImage(unsigned int width, unsigned int height, QString key);
void signalGetRenderListUpdate( const QString& request );

private slots:
void getImage(unsigned int width, unsigned int height, QString key);
void getRenderListUpdate( const QString& request );
void wakeListeners();

private:
void setupOpenGL();
void resize(unsigned int width, unsigned int height);
bool m_glImageIsReady;

// We only want to grab one image at the time
QMutex m_mainMutex;

// This is for waiting. We let the event-loop take the image, and wait
// for it to finish.
QMutex m_waitMutex;
QWaitCondition m_waitCondition;

tinia::jobcontroller::Job* m_job;
unsigned char* m_buffer;
QMutex m_mainMutex;
unsigned char* m_buffer;
size_t m_buffer_size;
QString m_renderlist_update_xml;
bool m_openglIsReady;
unsigned int m_fbo;
unsigned int m_renderbufferRGBA;
unsigned int m_renderbufferDepth;

unsigned int m_width;
unsigned int m_height;
bool m_openglIsReady;
unsigned int m_fbo;
unsigned int m_renderbufferRGBA;
unsigned int m_renderbufferDepth;
unsigned int m_width;
unsigned int m_height;
};

} // namespace impl
Expand Down
14 changes: 10 additions & 4 deletions src/qtcontroller/HTTPServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,22 @@ namespace tinia {
namespace qtcontroller {
namespace impl {

HTTPServer::HTTPServer(tinia::jobcontroller::Job* job, tinia::qtcontroller::impl::OpenGLServerGrabber* imageSource, QObject *parent) :
QTcpServer(parent), m_job(job),
m_serverGrabber(imageSource)
HTTPServer::HTTPServer( tinia::jobcontroller::Job* job,
QObject *parent)
: QTcpServer(parent),
m_job(job),
m_serverGrabber( new OpenGLServerGrabber( this ) ),
m_mainthread_invoker( new Invoker( this ) )
{
listen(QHostAddress::Any, 8080);
}

void HTTPServer::incomingConnection(int socket)
{
ServerThread* thread = new ServerThread(*m_serverGrabber, m_job, socket);
ServerThread* thread = new ServerThread( m_serverGrabber,
m_mainthread_invoker,
m_job,
socket );

//connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));

Expand Down
55 changes: 55 additions & 0 deletions src/qtcontroller/Invoker.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#include <QtGlobal>
#include <QThread>
#include <QMutexLocker>
#include "tinia/qtcontroller/moc/Invoker.hpp"
namespace tinia {
namespace qtcontroller {
namespace impl {

Invoker::Invoker( QObject* parent )
: QObject( parent ),
m_main_thread( QThread::currentThread() )
{
connect( this, SIGNAL( requestMainThreadInvocation(QRunnable*,bool*)),
this, SLOT( handleMainThreadInvocation(QRunnable*,bool*)) );
}

void
Invoker::invokeInMainThread( QRunnable* function, bool block )
{
Q_ASSERT( function != NULL );
if( !block ) {
emit requestMainThreadInvocation( function, NULL );
}
else {
bool i_am_done = false; // specific condition for this request.
emit requestMainThreadInvocation( function, &i_am_done );

// wait for function to complete
QMutexLocker locker( &m_wait_mutex );
while ( !i_am_done ) {
m_wait_condition.wait( &m_wait_mutex );
}
}
}


void
Invoker::handleMainThreadInvocation( QRunnable* function, bool* i_am_done )
{
Q_ASSERT( QThread::currentThread() == m_main_thread );
function->run();

// notify completion if invocation is blocking.
if( i_am_done != NULL ) {
QMutexLocker locker( &m_wait_mutex );
*i_am_done = true;
m_wait_condition.wakeAll();
}
}



} // namespace impl
} // namespace qtcontroller
} // namespace tinia
Loading

0 comments on commit d5a390f

Please sign in to comment.