Overview
Examples
Screenshots
Comparisons
Applications
Download
Documentation
Tutorials
Bazaar
Status & Roadmap
FAQ
Authors & License
Forums
Funding Ultimate++
Search on this site
Search in forums












SourceForge.net Logo
Home » U++ Library support » U++ MT-multithreading and servers » Thread calls GUI
Thread calls GUI [message #20056] Sat, 14 February 2009 19:26 Go to next message
Sami is currently offline  Sami
Messages: 6
Registered: July 2008
Promising Member
I realize a thread cannot call GUI in upp. It is not however clear how threading should be implemented then. I would ask help for proper solution to the example given below.

struct Interface {
  virtual int Ask ( const char * ) = 0;
};

struct Work {
  Interface *gui;
};

struct Library {
  Library ( Work w ) {
    int a = w.gui->Ask ( "Ok?" );
  }
};

void Threading ( Work w ) {
  Library ( w );
}

struct Task
:MyTask<TopWindow>
,Interface {
  typedef Task CLASSNAME;
  Task() {
    CtrlLayout(*this, "Example" );
    Work w;
    w.gui = this;
    Thread().Run ( callback1 ( Threading, w ) );
  }
  volatile Atomic q;
  int Ask_Weird_Hacked ( const char *s, unsigned dummy ) {
    return q = 1 + PromptYesNo ( String().Cat() << s );
  }
  int Ask ( const char *s ) {
    //problem here, cannot call PromptYesNo()
    q = 0;
    PostCallback ( callback2 ( this, &Task::Ask_Weird_Hacked, s, 0 ) );
    while ( !q ) Sleep ( 10 );
    return q - 1;
  }
};


So we begin with Task() and our problem is how to implement Ask() call properly. I first understood the Gate-method is what I'm looking for, but I didn't get it to work, can somebody explain what is it? The manual was in my opinion incomplete here.

[Updated on: Sun, 15 February 2009 00:14]

Report message to a moderator

Re: Thread calls GUI [message #20058 is a reply to message #20056] Sun, 15 February 2009 08:24 Go to previous messageGo to next message
mirek is currently offline  mirek
Messages: 11843
Registered: November 2005
Ultimate Member
Unfortunately, Gate cannot work here. Gate is supposed to return the value, which is not possible until callback is performed..

I am afraid that the solution for your problem might be quite complex.

IMO, you will have to use something like Semaphore on thread part. Use PostCallback to signal to GUI thread you need that prompt, enter semaphore after PostCallback.

GUI thread then performs the prompt, signals the result via some shared variable, then releases semaphore of thread to get it going.

void DoAsk(Semaphore *sem, int *result)
{
   *result = PromptYesNo("");
   sem->Release();
}

struct MyThread {
    int Ask() {
        Sempahore sem;
        int result;
        PostCallback(callback2(DoAsk, &sem, &result));
        sem.Wait();
        return result;
    }
}


(To my best knowledge, we do not need mutex for result, as sempahore does the synchronization for us as well).

(Not testes, but should work).

Mirek
Re: Thread calls GUI [message #20061 is a reply to message #20058] Sun, 15 February 2009 22:55 Go to previous messageGo to next message
Sami is currently offline  Sami
Messages: 6
Registered: July 2008
Promising Member
Thanks for your answer. It appears that this semaphore and additional function should be done for all interface calls... Have you considered fixing this apparent design issue in upp? Why we cannot have upp serialize (if it needs to) the gui calls transparently, so that there would be no limits for threads calling the gui?

Additional question. Suppose we have a kill button in the GUI. void Task::KillButton(). How to kill the thread in this function? We presume the thread is heavy and cannot ping asking the interface for ShouldWeCancelNow() frequently enough to be able to shutdown itself.
Re: Thread calls GUI [message #20062 is a reply to message #20061] Sun, 15 February 2009 23:22 Go to previous messageGo to next message
Mindtraveller is currently offline  Mindtraveller
Messages: 916
Registered: August 2007
Location: Russia, Moscow rgn.
Experienced Contributor

Some time ago I`ve started developing "alternative" multithreading system for U++. General idea is that you do not need any sync objects. Because threads do not see ANY shared variables. Instead threads have internal callback queues and exchange with callbacks. Realization is rather optimal (but could be better if I had more spare time Smile ).

Recently I was badly needed this approach to be working for a number of threads and also for a "main" GUI thread. Finally these classes are ready and tested for some time, but still under heavy development.

Simplified code looks like this...

1. Declaring main GUI thread/window and one more thread:
class GUIThread : public CallbackQueue, public WithMainWindowLayout<TopWindow>
{
public:
	GUIThread();
   ~GUIThread();
	virtual void Init();
	virtual void Shutdown();
	
	void HandleIncomingMessages();

public /*sync*/:
	void RefreshAll(const Drawing &bdr, const ControlGUI &cg);
	void SetupBathsSettings(Vector<Vector<Value> > rows);
};

class IOThread : public CallbackThread, protected RS232
{
public:
	IOThread();
   ~IOThread();
	virtual void Init();
	virtual void Shutdown();
	
public /*sync*/:
	void GetAOpState(byte addr);
};


Main app function is simple:
GUIThread     guiThread;
IOThread      ioThread;
ControlThread controlThread;

GUI_APP_MAIN
{
	try
	{
		CallbackQueue::InitAll();
		CallbackQueue::StartAll();

		guiThread.Sizeable().Run();

		CallbackQueue::ShutdownAll();
	}
	catch (const Exc &ex)
	{
		PromptOK(ex);
	}
}


Finally, if I want any of my threads (including main/GUI) to do something, I just request for this:
// i/o therads checks AOp devices and tells their availability to Control thread
void IOThread::GetAOpState(byte addr)
{
	for (int i=0; i<attempts; ++i)
	{
		protoSend[3] = addr;
		protoSend[4] = CMD_AOP_STATUS;
		protoSend.Send(*this, timeout);
		
		if (protoRecv.Receive(*this, timeout))
		{
			if ((int)protoRecv[3] != (int)addr)
				continue;
			controlThread.Request(&ControlThread::AOpStatus, addr, protoRecv[4]);
		}
	}
	
	controlThread.Request(&ControlThread::AOpUnavailable, addr);
}

// Control thread analyzes system state and updates GUI accordingly
void ControlThread::SetupDisplayDrawing(bool enableSettings)
{
	static ControlGUI cg;
	// setting controls to be enabled/disabled
	// ...

	
	// drawing system elements and parameters
	DrawingDraw d(DISPLAY_W,DISPLAY_H);
	// ...

	guiThread.Request(&GUIThread::RefreshAll, static_cast<Drawing>(d), cg);
}


...and no sync objects with their debug.
If this is handy for you, I`ll upload these "alternative" multithreading sources here.

[Updated on: Sun, 15 February 2009 23:50]

Report message to a moderator

Re: Thread calls GUI [message #20063 is a reply to message #20061] Sun, 15 February 2009 23:27 Go to previous messageGo to next message
mirek is currently offline  mirek
Messages: 11843
Registered: November 2005
Ultimate Member
Sami wrote on Sun, 15 February 2009 16:55

Thanks for your answer. It appears that this semaphore and additional function should be done for all interface calls... Have you considered fixing this apparent design issue in upp? Why we cannot have upp serialize (if it needs to) the gui calls transparently, so that there would be no limits for threads calling the gui?



Yes, it is planned.

Quote:


Additional question. Suppose we have a kill button in the GUI. void Task::KillButton(). How to kill the thread in this function? We presume the thread is heavy and cannot ping asking the interface for ShouldWeCancelNow() frequently enough to be able to shutdown itself.



I am afraid that killing the thread is not as easy as it might seem - who would free all associated resources?! (e.g. allocated memory, opened files and sockets...).

That is why I think that some form of "ShouldWeCancelNow" is in fact necessarry. It can take the form of periodic call to some function which in case of cancel throws exception - that IMO is the most effective way.

Mirek

Re: Thread calls GUI [message #20084 is a reply to message #20063] Mon, 16 February 2009 21:35 Go to previous messageGo to next message
Sami is currently offline  Sami
Messages: 6
Registered: July 2008
Promising Member
Thanks for the replies.

Ok, I cannot kill the thread. What about thread exceptions. How should they be implemented. I have an option to replace c++ exceptions with a call to static function if needed. Are the exceptions allowed and where should I catch them (in the example I gave at the top post)? It's not trivial to get rid of the exceptions in the thread (to exit cleanly).
Re: Thread calls GUI [message #20086 is a reply to message #20084] Tue, 17 February 2009 07:36 Go to previous messageGo to next message
mirek is currently offline  mirek
Messages: 11843
Registered: November 2005
Ultimate Member
Sami wrote on Mon, 16 February 2009 15:35

Thanks for the replies.

Ok, I cannot kill the thread. What about thread exceptions. How should they be implemented. I have an option to replace c++ exceptions with a call to static function if needed. Are the exceptions allowed and where should I catch them (in the example I gave at the top post)? It's not trivial to get rid of the exceptions in the thread (to exit cleanly).



But exceptions are GOOD in this context. They would perform the necessarry cleanup of resources. Simply catch the "thread canceled" exception in the main thread routine...

In fact, the only hard part is to how to throw them. There I see no other option than to call some function periodically, check for the exception flag and throw if set.

Well, anyway, I guess any serious GUI program should show the progress of processing anyway - maybe that is the right place to check for cancelation too....

Mirek
Re: Thread calls GUI [message #20099 is a reply to message #20086] Tue, 17 February 2009 15:57 Go to previous messageGo to next message
tojocky is currently offline  tojocky
Messages: 607
Registered: April 2008
Location: UK
Contributor

How about to add semaphore method dword Semaphore::Wait(int timeout)?

in win32 is simple:
int Semaphore::Wait( int timeout )
{
	dword result_value;
	result_value = WaitForSingleObject(handle, timeout);
	if(result_value == WAIT_FAILED) return(SEMAPHORE_WAIT_ERROR);
	if(result_value == WAIT_TIMEOUT) return(SEMAPHORE_TIMEOUT);
}


but in POSIX is more hardly.
The good article found here.

I think it have sense on i have a postcallback and i know maximum execution time. Or I'm wrong?
Re: Thread calls GUI [message #20102 is a reply to message #20099] Tue, 17 February 2009 19:18 Go to previous message
mirek is currently offline  mirek
Messages: 11843
Registered: November 2005
Ultimate Member
tojocky wrote on Tue, 17 February 2009 09:57

How about to add semaphore method dword Semaphore::Wait(int timeout)?

in win32 is simple:
int Semaphore::Wait( int timeout )
{
	dword result_value;
	result_value = WaitForSingleObject(handle, timeout);
	if(result_value == WAIT_FAILED) return(SEMAPHORE_WAIT_ERROR);
	if(result_value == WAIT_TIMEOUT) return(SEMAPHORE_TIMEOUT);
}


but in POSIX is more hardly.
The good article found here.

I think it have sense on i have a postcallback and i know maximum execution time. Or I'm wrong?


I do not know. I believe all these timeouts just make it more error-prone. You generally should not depend on timeout when dealing with semaphore (IMO!).

Mirek
Previous Topic: StaticMutex/ONCELOCK question
Next Topic: GUI MT project does not compile
Goto Forum:
  


Current Time: Thu Apr 25 04:10:47 CEST 2019

Total time taken to generate the page: 0.01181 seconds