PDA

View Full Version : Sharing data between threads



bbui210
15th October 2008, 00:16
Hello everyone,

I'm using QT4.4.1 and have the following situation:

We have 'C' code with which we want to add a Graphical User Interface to, so I created the GUI using QT4 and built it as a static library. I then create a new thread in our multi-threaded 'C' application and execute the GUI. I was able to do with no problems at all, but now I want to be able to send data back and forth between the C-main function and the QT-main function (QT GUI Library).

I wasn't sure how to do this because the QApplication::exec() is necessary to start the GUI and has it's own event loop that processes different events.

Can anyone provide any help on this subject? Creating a separate thread in QT (QThreads) and running the C application is not an option because I will not have access to manipulate the C code, only the QT GUI library.


Regards,
BB

caduel
15th October 2008, 08:48
You may notify Qt via QCoreApplication::postEvent() (which is tread-safe.).

HTH

bbui210
15th October 2008, 19:37
Okay, but I'm still not sure how to get data from the C code into the QT application when I am outside of the QT event loop.

For example I have the following code in the C++ library:

C++ Library


int hpgeUI(int argc, char *argv[])
{
QApplication rdac(argc, argv, QApplication::GuiServer);

MainWindow window;
window.showFullScreen(); //Run in full screen mode
window.setPalette(QPalette(QColor(233, 233, 235))); //Set palette color to off white
window.setAutoFillBackground(true); //Fill background w/ palette color
window.show();//Show window

return rdac.exec();
}


I then execute this function from a new thread in my C code:

C Code:


int main(int argc, char *argv[])
{
char arg[] = "-display transformed:Rot90 -nomous";

while(hpgeUI(2, *arg)) //While GUI is running
//do some updating of data here using mutex protected data
}


From what I understand, the QApplication does not exit or return anything until it receives an exit() command, so the while() loop above will not do anything until the GUI is closed. Is this correct?

So my question is, how do I get data from the Main C Code into my QApplication if my code sits and waits for an exit() from QApplication?

If my assumptions are incorrect, then I can simply run a separate loop after executing the exec() function and use the postEvent() function correct?


I appreciate all the help.

Regards,
BB

fullmetalcoder
15th October 2008, 20:18
well... my two cents : wrong paradigm!

let me explain it a little better : it does not make sense to call the GUI part from the "non-GUI" part. Instead, create your GUI as a standalone application that lives its own life and interacts with the "non-GUI" part either invoking it as a command line application or directly calling relevant code (provided it is packed into a library).

If your core code is supposed to produce data as long as the GUI is running just display that GUI from the main function and mess with the data in a separate thread with which the GUI can exchnage data through signals/slots and/or thread-safe classes known to both (using a producer/consumer approach for instance).

bbui210
15th October 2008, 20:59
let me explain it a little better : it does not make sense to call the GUI part from the "non-GUI" part.

Yes, I understand that this way of thinking doesn't make sense, that the GUI should be the core of the application and the rest of the precessing should be threaded out. However, I am unable to do this because our "core" code is huge and is in C (not C++).

I do understand I am not visualizing this properly, so any help/push in the right direction is much appreciated.



Instead, create your GUI as a standalone application that lives its own life and interacts with the "non-GUI" part either invoking it as a command line application or directly calling relevant code (provided it is packed into a library).
I would like to keep the whole application as one instead of two separate applications sending data back and forth between the two.



If your core code is supposed to produce data as long as the GUI is running
Yes this is what it's supposed to do


just display that GUI from the main function and mess with the data in a separate thread with which the GUI can exchnage data through signals/slots and/or thread-safe classes known to both (using a producer/consumer approach for instance).
The main function is in C and because of this I am having a hard time putting all of this together.

I cannot change the way the Main/Core application is laid out, so I have to work with a given thread created from this Core and have to display the data given to me.

I may still be looking at it all wrong, but I hope this clears things up a little bit and again, I appreciate all the help.

Regards,
BB

fullmetalcoder
15th October 2008, 22:22
you can always call C code from C++ (unless you use loose casts that some C++ compilers may not accept) but the opposite is not true.

wouldn't it be possible to do the following :


int main(int argc, char **argv)
{
QApplication app(argc, argv);

// setup GUI

// launch the core loop in another thread, no matter how (QThread or whatever is already used in your core, as long as it does not block things now)

return app.exec(); // start GUI event loop (so display and interactivity)
}

Then communication between GUI and "core" can be done through any of the following (it is possible to mix these):


signals/slots
events
"global" thread-safe classes wrapping around the shared data (GUI and "core" have access to the instance of such classes. such a class serializes data acess using mutexes or read/write locks and everyone is happy).

Given that your core is written in C and that you cannot modify it the simplest approach would probably be to create a QThread subclass in which you do data collection (or production...) and which takes care of communication with the GUI (it can uses all three above means of communication while the "core" cannot).

Of course this is just a general canvas but without more precisions that's a much as you may get.

bbui210
15th October 2008, 22:34
Thank you for your help



Given that your core is written in C and that you cannot modify it the simplest approach would probably be to create a QThread subclass in which you do data collection (or production...) and which takes care of communication with the GUI (it can uses all three above means of communication while the "core" cannot).

I was thinking of something like this.

So one option would be to create this QThread that can read/write to mutex protected global data in my GUI Library and then create a function/method that will allow the outside (CORE Application) read/write to these protected variables also.

Am I on the right track?

fullmetalcoder
15th October 2008, 23:05
I was thinking about something more like this :


your C code has well-defined entry points already (you can feed hime some data and get other from it)
you create a thread which uses this entry point to do whathever operation you need with the core
the data that must be shared between core and UI is located in a third class (or a set of class). both the GUI and the "runner thread" have access to instances of this (set of) class so they can read/write data independently
this (set of) class serializes read/write operations using either QMutex or QReadWriteLock so that threading does not screw things up

I hope I am making it clear enough. Please tell me if it is not the case.

bbui210
15th October 2008, 23:13
I think I have a better idea of how to accomplish this. It's time to start punching out some code. I will report back with the status of how well it goes.

Thank you,
BB

bbui210
16th October 2008, 20:22
So I have got some code to compile, but I'm having some problems. Here is what I'm doing to give you an idea of what's going on with my code:



I have C code with which I create a thread to run the GUI in
In the GUI code I create a QThread which provides me entry points to write data to and from.


I can compile it all and run it, but it seems as though the GUI Thread is blocking my Main thread. The graphics show up, but everything else in my main C code is not executing.

My GUI code:


//globals
QApplication *rdac;
idThread *idthread;
MainWindow *window;

int hpgeUI(char *argv[])
//int main(int argc, char *argv[])
{
rdac = new QApplication(argc, argv, QApplication::GuiServer);
idthread = new idThread;
window = new MainWindow;

window->showFullScreen(); //Run in full screen mode
window->setPalette(QPalette(QColor(233, 233, 235))); //Set palette color to off white
window->setAutoFillBackground(true); //Fill background w/ palette color
window->show();//Show window
rdac->connect(idthread, SIGNAL(newIdValue(int)), window, SLOT(newIdValue(int)));
//return 0;
return rdac->exec();
}

void updateData(int id)
{
idthread->updateID(id);
}



The QThread to pass data to and from the C code:

void idThread::updateID(int id)
{
/* Lock the mutex so we can grab the data */
QMutexLocker locker(&mutex);
/* copy the id to our thread's copy */
this->localID = id;
/* if the thread is not running, start it, else restart the thread. */
if(!isRunning()){
start(LowPriority);
} else {
restart = true;
/* wake up the thread if necessary */
condition.wakeOne();
}
}

void idThread::run()
{
cout << "Running idThread\n";
int id = 0;
forever //Qt psuedo-keyword, like foreach
{
/* Lock the mutex and copy ID to a local copy so that if new ID's come in, we can get them immediately */
mutex.lock();
id = this->localID;
mutex.unlock();
/* if we get an abort for some reason, break from this loop and kill the thread */
if(abort)
return;
/* Emit the new ID unless restart is true */
if(!restart)
emit newIdValue(id);
/* Once we're done with all the iterations, we call QWaitCondition::wait() to put the thread */
/* to sleep by calling, unless restart is true. There's no use in keeping a worker thread looping */
/* indefinitely while there's nothing to do. */
mutex.lock();
if(!restart)
condition.wait(&mutex);
restart = false;
mutex.unlock();
}

}


My C Code that puts everything together:


void *initUI(char *argv[]) {
printf("This is the initUI thread\n");
hpgeUI(argv);
return 0;
}


int main(int argc, char *argv[])
{

pthread_t ui;
pthread_create(&ui, NULL, initUI(argv), 0);
printf("Done initializing...\n");
int x = 0, test = 0x1;
while(1)
{
x++;
if(x%50 == 0) {
printf("Testing with value: 0x%04x\n", test);
if(test < 0x8)
test++;
else
test = 0x1;
updateData(test);
}
}
return 0;
}



Am I going about it all wrong? Again, I appreciate all the help.

Regards,
BB

fullmetalcoder
16th October 2008, 23:20
It might look like as though I'm repeating myself but :


it is a bad idea to init your GUI from the core. Doing the opposite is recommended
I am not familiar with pthread and how well Qt interacts with other threading APIs in the same app but what I am sure about is that Qt docs explicitely tell you that the GUI runs in its own thread and that you may not call GUI commands from another thread so I suppose your approach, being inherently disrespectful of Qt docs, is likely to fail (again I am not familiar with the possible interactions at this level so it might actually work).

I just packed a minimalistic example (http://edyuk.org/misc/core.tar.gz)showing the design I've explained to you. It is BSD-licensed so you can reuse it freely even in closed source projects. If you have any question about the code please ask.

Note : the app expects you to type some text in the line edit. It will send the data to the "core thread" which in turns call some C code through an entry point, get a result back (here some sort of hash) and make it available to the GUI. Modifications may be needed for use with your application but the design is here.

bbui210
17th October 2008, 00:43
Thank you again for your quick responses.




it is a bad idea to init your GUI from the core. Doing the opposite is recommended



Yea, and trust me, if I had a choice I would have done so in a heartbeat. I just don't have a choice and will have to make this work. I'm really not that hardheaded :D




I am not familiar with pthread and how well Qt interacts with other threading APIs in the same app but what I am sure about is that Qt docs explicitly tell you that the GUI runs in its own thread and that you may not call GUI commands from another thread so I suppose your approach, being inherently disrespectful of Qt docs, is likely to fail (again I am not familiar with the possible interactions at this level so it might actually work).



I understand that the GUI commands all must reside in the same thread. I have done this by creating a thread and starting the GUI in it, as seen in the function initUI (from the code above). So getting the UI up and running is not a problem now. My problem, which might be the mix of pthread and QT, is that once the GUI executes (in its own thread), the originating thread doesn't move on.

I do all of my (dummy) data transfers after the start of the GUI, so because the thread seems to be halted or paused, I am unable to transfer/pipe the data.

Maybe it's not getting any CPU time? I don't know.

Any advice?

Regards,
BB


EDIT: THIS MAY BE A TOPIC FOR ANOTHER THREAD (Starting QT in a seperate pthread). Unless anyone has any insights to this issue?

fullmetalcoder
17th October 2008, 18:02
Well, either the example code you shown is not really what you're working on but only a rough approximation of something a lot more complicated, or you did not even look at the example code I proposed. As far as I understood your explanations the proposed design should do pretty fine (may need some adjustements but I'm not going to write your program ;)). All you have to do is to init your GUI where I init my simple test widget and place calls to your C code inside the run() function of the CoreThread.

Again (yeah I like repeating myself), creating GUI from core is more than just a design error, it is a complete design failure. If you can call GUI init code from core then you can also change your core to fit in a GUI app from the start. If you can't (as you claim) then you shouldn't be able to init any GUI but you will always be able to access your core from the GUI (or at least from the place that creates your GUI). I really don't understand what bugs you about such a simple yet important change of paradigm.

bbui210
17th October 2008, 18:42
Well, either the example code you shown is not really what you're working on but only a rough approximation of something a lot more complicated
Yes my example is a very simplified version so that I could explain how things are working. The actual Core application creates about 10-15 other threads that do processing and modification of mutex protected global variables.


or you did not even look at the example code I proposed.
I had a look, I sent you a PM with some questions about it.


As far as I understood your explanations the proposed design should do pretty fine (may need some adjustements but I'm not going to write your program ;)).
Of course you're not, where would the fun be if you did? :D



I really don't understand what bugs you about such a simple yet important change of paradigm.
It is partially because the Core is such a huge base of code that I do not want to make any large changes to it (or tell the creator to do so). I had hoped that it would be possible for the core to create a separate GUI thread among the dozen of others, but I am seeing that what I want to do is not possible. Sorry if it took a few iterations of beating my head against the wall until you got your point across.

Regards,
BB

fullmetalcoder
19th October 2008, 15:21
you really don't have to change the core (except maybe move it out of the main() function) and you wouldn't have to change the GUI much either (where it to be designed already). You just have to insert a layer between them that handle communication in a way that is independent from both (i.e use signals/slots and/or events to pass data from GUi to communication layer (CoreThread in my example) and use C core entry points, from communication layer, to pass that data (and retrieve results) to the C core).

The underlying architecture of the core is not an issue as long as it offers entry points to the data it manipulates (in and out). The rest is just a matter of bridging UI and core as explained above.

bbui210
19th October 2008, 18:56
Yea, after my last post I've finally cleared up my confusion. I combined what you were saying and what I was trying to say and was able to work.

Again, I appreciate all your help.

Regards,
BB