| 
 | 
 | 
 
Home » U++ Library support » U++ MT-multithreading and servers » Problem breaking loop (with close button) in main thread 
	
		
		
			| Problem breaking loop (with close button) in main thread [message #59034] | 
			Tue, 18 October 2022 17:19   | 
		 
		
			
				
				
				
					
						  
						awksed
						 Messages: 70 Registered: April 2012 
						
					 | 
					Member  | 
					 | 
		 
		 
	 | 
 
	
		Windows 7. U++ 16270. Multi-thread app. 
 
I have a GuiAcquireMutex() function called from a main thread Paint() that calls  
 
WaitForSingleObject(hMutex, dwShortTimeout) // dwShortTimeout = 1 second (full timeout is 60 seconds) 
 
if the mutex is not acquired I call: 
 
Ctrl::GuiSleep(1000) to allow other threads to run and if 60 seconds have not elapsed, jump  
back to WaitForSingleObject(). 
 
I wish to allow the close button to call Close() (that sets boolean g_bQuit) and allow the user 
close the app (breaking the mutex acquire loop). 
 
Clicking the close button results in the windows message "... is not responding". 
 
Adding Ctrl::ProcessEvents() causes a "WM_PAINT invoked ... while in Paint routine" error. 
 
Is there some way in U++ to allow the close button to call Close() while the main thread is looping 
(like Windows PumpWaitingMessages())? 
 
Code: 
 
DWORD GuiAcquireMutex(HANDLE hMutex, DWORD dwTimeout)
{
 if(hMutex == INVALID_HANDLE_VALUE)
   return ERROR_INVALID_HANDLE;
 
 long long llNow;
 long long llStart        = GetMilliTime();
 long long llEnd          = llStart + (long long) dwTimeout;
 DWORD     dwShortTimeout = 1000;        // 1 second
 DWORD     dwLastError;
 DWORD     dwWaitResult;
Again:
 dwLastError  = 0; // Success
 dwWaitResult = WaitForSingleObject(hMutex, dwShortTimeout);
 switch(dwWaitResult)
  {
   case WAIT_FAILED:
        llNow = GetMilliTime();
        
        if(llNow < llEnd)
         {
          if(g_bQuit)
           {
            dwLastError = ERROR_COUNTER_TIMEOUT;
            SetLastError(dwLastError);
            return dwLastError;
           }
          
          Ctrl::GuiSleep(1000); // Never sleeps for 1000 ms (always returns immediately)
          goto Again;
         }
        dwLastError = GetLastError();
        break;
   case WAIT_TIMEOUT:
        llNow = GetMilliTime();
        
        if(llNow < llEnd)
         {
          if(g_bQuit)
           {
            dwLastError = ERROR_COUNTER_TIMEOUT;
            SetLastError(dwLastError);
            return dwLastError;
           }
           
          Ctrl::GuiSleep(1000); // Never sleeps for 1000 ms (always returns immediately)
          goto Again;
         }
        dwLastError = ERROR_COUNTER_TIMEOUT;
        SetLastError(dwLastError);
  }
 return dwLastError;
}
 
 
Thanks. 
 
		
		
		
 |  
	| 
		
	 | 
 
 
 |  
	| 
		
 |  
	
		
		
			| Re: Problem breaking loop (with close button) in main thread [message #59036 is a reply to message #59034] | 
			Tue, 18 October 2022 20:09    | 
		 
		
			
				
				
				
					
						  
						Lance
						 Messages: 656 Registered: March 2007 
						
					 | 
					Contributor   | 
					 | 
		 
		 
	 | 
 
	
		On a second thought, your program needs the hMutex be acquired to continue its work. Then ProcessEvent() should be the way to go. Problem is, why is that ProcessEvent() failed you? 
 
Can you try something like the following 
DWORD GuiAcquireMutex(HANDLE hMutex, DWORD dwTimeout)
{
	if(hMutex == INVALID_HANDLE_VALUE)
		return ERROR_INVALID_HANDLE;
 
	DWORD     dwLastError = -1;
	while ( !g_bQuit && dwLastError != 0 )
	{
		dwLastError  = 0; // Success
		switch( WaitForSingleObject(hMutex, dwShortTimeout ) )
		{
		case WAIT_FAILED:
			Do_Set_LastError_Accordingly();
			break;
			
		case WAIT_TIMEOUT:
			dwLastError = ERROR_COUNTER_TIMEOUT;
			break;
		}
		// consider measure how long it takes ProcessEvents to finish,
		// if very quick, consider add a Sleep(500) or something like that
		// to avoid keeping CPU busy for nothing.
		topwin.ProcessEvents();
		if(TooSoon() )
		{
			Sleep (500);
		}
	}
	SetLastError (dwLastError) ;
	return dwLastError;
}
 
 
topwin is your TopWindow(or its derivative) object. 
 
I am feeling your original code didnot prepare for the case when the WaitForSingleObject call actually succeeds. I might be wrong though. Above pseudo code will need to be tuned to reflect your true intention.
		
		
		[Updated on: Tue, 18 October 2022 20:34] Report message to a moderator  
 |  
	| 
		
	 | 
 
 
 |  
	| 
		
 |  
	
		
		
			| Re: Problem breaking loop (with close button) in main thread [message #59040 is a reply to message #59039] | 
			Wed, 19 October 2022 02:21    | 
		 
		
			
				
				
				
					
						  
						Lance
						 Messages: 656 Registered: March 2007 
						
					 | 
					Contributor   | 
					 | 
		 
		 
	 | 
 
	
		Hi Awksed: 
 
My bad. Your code goto again; only when WaitForSingleEvent fails. I was thinking in the context that it has been converted into a while loop.   
 
Quote: 
Is there some way in U++ to allow the close button to call Close() while the main thread is looping 
(like Windows PumpWaitingMessages())? 
  
I would use ProcessEvents() in the loop. No idea how it will cause failure in your case. Maybe you can add a flag in Paint() to not call GuiAcquireMutex(..) again if there is a previous call not completed yet? 
 
I would think the ProcessEvents should be inserted somewhere in your GuiAcquireMutex function (before jump back to again maybe), but it's somewhere else needs to be changed accordingly if problem arose because of it.
		
		
		[Updated on: Wed, 19 October 2022 02:22] Report message to a moderator  
 |  
	| 
		
	 | 
 
 
 |  
	| 
		
 |  
	| 
		
 |  
	
		
		
			| Re: Problem breaking loop (with close button) in main thread [message #59044 is a reply to message #59043] | 
			Thu, 20 October 2022 01:04    | 
		 
		
			
				
				
				
					
						  
						Lance
						 Messages: 656 Registered: March 2007 
						
					 | 
					Contributor   | 
					 | 
		 
		 
	 | 
 
	
		Hi Awksed 
 
I am sorry to hear that you have to satisfy with a compromise. 
 
I manage to create the following test. It runs as expected on Linux, but would not work on Windows.  
 
Regards, 
Lance 
 
---------------- 
Correction: No, it's not a problem with ProcessEvents(). After I change the thread lambda to not log on GUI but to debug output file, it runs like a charm. It's interesting. I probably should not modify GUI from within other than the GUI thread. Seems Linux+GTK are more tolerant to this kind of error.   
	void Start(){
		static int cnt;
		if( cnt!= 0 )
			return;
		++ cnt;
		stop_requested = false;
		log.Clear();
		badguy.Run([this]{
			LOG("Badguy running...\n"); // CHANGE THIS
			Mutex::Lock _(mut);
			LOG("Badguy now owns the Mutex.\n");  // CHANGE THIS
			while(!stop_requested && !IsShutdownThreads() ){
				Sleep(50);
			}
			LOG("Stop requested, Badguy exiting and releasing the Mutex...\n"); // AND THIS
		});
		// wait for badguy to acquire the mutex
		while(mut.TryEnter()){
			mut.Leave();
			ProcessEvents();
			Sleep(100);
		}
		log.Append("Trying to acquire the mutex...\n");
		GuiAcquireMutex();
		--cnt;
	}
 
		
		
		[Updated on: Thu, 20 October 2022 13:09] Report message to a moderator  
 |  
	| 
		
	 | 
 
 
 |  
	
		
		
			| Re: Problem breaking loop (with close button) in main thread [message #59048 is a reply to message #59044] | 
			Thu, 20 October 2022 20:10    | 
		 
		
			
				
				
				
					
						  
						Oblivion
						 Messages: 1241 Registered: August 2007 
						
					 | 
					Senior Contributor  | 
					 | 
		 
		 
	 | 
 
	
		Hi Lance, 
 
Quote: 
probably should not modify GUI from within other than the GUI thread.  
  
 
You are allowed to modify GUI from within other threads. For this purpose, you need to use 
 
a) EnterGuiMutex() & LeaveGuiMutex(); 
b) GuiLock, which is basicly the above functions wrapped in a class for convenience: 
 
Rudimentary example: 
#include <CtrlLib/CtrlLib.h>
using namespace Upp;
#define LAYOUTFILE <DeadMutex/DeadMutex.lay>
#include <CtrlCore/lay.h>
static std::atomic<bool> fin;
class DeadMutex : public WithDeadMutexLayout<TopWindow> {
	
	typedef DeadMutex CLASSNAME;
	
public:
	DeadMutex()
	{
		CtrlLayout(*this, "Window title");
		start.WhenPush = THISFN(Start);
		stop.WhenPush = [this]{ fin = true; };
		WhenClose = [this]{ ShutdownThreads(); Break(); };
	}
	
	void Start(){
		Thread().Run([=] {
			{
				GuiLock __; // EnterGuiMutex()
				log.Append("Badguy instance running & owned the gui mutex...\n");
				// LeaveGuiMutex();
			}
			while(!fin && !IsShutdownThreads() ){
				Sleep(50);
			}
			GuiLock __;
			log.Append("Stop requested, Badguy instance exiting...\n");
			fin = false;
		});
	}
};
GUI_APP_MAIN
{
	DeadMutex().Run();
}
 
 
There is also a GuiUnlock class which basically reverses the order of LeaveGuiMutex and EnterGuiMutex, so you can force the main thread to temporarily unlock its GUI lock. 
 
What you are not allowed to do is, creating windows and prompts within other threads. 
 
 
Best regards, 
Oblivion
		
		
  Github page: https://github.com/ismail-yilmaz 
Bobcat the terminal emulator: https://github.com/ismail-yilmaz/Bobcat
		[Updated on: Thu, 20 October 2022 20:12] Report message to a moderator  
 |  
	| 
		
	 | 
 
 
 |  
	| 
		
 |   
Goto Forum:
 
 Current Time: Tue Nov 04 17:10:16 CET 2025 
 Total time taken to generate the page: 0.05225 seconds 
 |   
 |  
  |