Is it possible to kill WaitForSingleObject (handle, INFINITE)?

I'm having trouble closing an application that uses WaitForSingleObject () with an INFINITE time interval.

This is the full picture. I am doing the following to allow my application to handle a device wake event:

Register an event with:

CeRunAppAtEvent("\\\\.\\Notifications\\NamedEvents\\WakeupEvent", NOTIFICATION_EVENT_WAKEUP); 

Start a new thread to wait:

 Thread waitForWakeThread = new Thread(new ThreadStart(WaitForWakeup)); waitForWakeThread.Start(); 

Then follow these steps in the target method:

 private void WaitForWakeup() { IntPtr handle = CreateEvent(IntPtr.Zero, 0, 0, "WakeupEvent"); while (true) { WaitForSingleObject(handle, INFINITE); MessageBox.Show("Wakey wakey"); } } 

Everything works fine until I try to close the application when, as expected, WaitForSingleObject continues to wait and does not allow the application to close properly. We only allow one instance of our application to run at a time, and we test it at startup. It continues to work until the device becomes soft. reset.

Is there a way to kill WaitForSingleObject's wait descriptor to make it return?

Many thanks.

+6
source share
4 answers

Instead, use WaitForMultipleObject and pass 2 descriptors. Existing, and one for the event, called "exit." While the application is disconnected, SetEvent in the exit event and WaitForMultipleObject will return, and you can get it to end the stream.

You need to include the WaitForMultipleObject return value in order to execute the appropriate behavior, depending on which one was started.

Perhaps you can also set the stream as a background stream. This will prevent the application from stopping when the main thread terminates.

Cm:

http://msdn.microsoft.com/en-us/library/system.threading.thread.isbackground.aspx

+16
source

'All this works fine until I try to close the application when, as expected, WaitForSingleObject continues to wait and does not allow the application to close properly.'

Any application can close, no matter what its threads do. If you call ExitProcess (0) from any thread in your application, the application will close, regardless of whether there are threads waiting for INFINITE on some APIs / sychro, sleeping, running on another processor, whatever. The OS will change the state of all thereds that don't start so that they never run again, and use its interprocessor system driver to hard interrupt any other processors that actually run your thread code. Once all threads are stopped, the OS frees descriptors, segments, etc., and your application no longer exists.

Problems arise when developers try to "cleanly" close threads that are stuck - like yours when the application closes. So..

Do you have TThread.WaitFor or similar in the OnClose / OnCloseQuery, FormDestroy, or destructor handler? If you have, and you have no significant reason for the thread to be interrupted, just comment on it!

This allows you to close the main form and thus your code will finally reach the ExitProcess () that it was trying to get to, since you clicked on the red cross button

You can, compartment, just call ExitProcess () yourself, but this can lead to a drain of resources, for example, in other processes - database connections.

'216/217 closing errors if I do not stop threads. This is often due to developers following the examples of β€œfailed” Delphi threads and interacting with threads, directly exchanging data between the fields of the secondary thread and the fields of the main thread (for example, TThread.synchronize). It just sucks and struggles to cause problems even when the application starts, not paying attention to stopping when the form has been destroyed, and the stream is trying to write to it or the stream has been destroyed, and the main stream form using call methods on it. It is much safer to communicate asynchronously with threads through the queue / PostMessaging objects that survive both of them, for example. objects created in the stream / form and freed in the form / stream, or using the (thread safe) pool of objects created in the initialization section. Forms can then be closed / freed safely, while related threads can continue to uselessly populate objects for processing until the main form is closed, ExitProcess () is reached, and the OS cancels the threads.

"My form handle is invalid because it is closed, but my thread is trying to send a message to it." If PostMessage excludes, exit the stream. The best way is similar to the above approach - send messages only to the window, which goes through all forms. Create it in the initialization section with the trivial WndProc, which processes only one const message number, which all threads use for publication. You can use wParam to transfer the TwinControl instance that the thread is trying to contact (usually a form variable), while lParam passes the object being passed. When it receives a message from a stream, WndProc calls "Peform" on the passed TwinControl, and TwinControl receives the comms object in the message handler. A simple global boolean "AppClosing", say, can stop WndProc calling Peform () on TwinControls, which are freed up during shutdown. This approach also avoids the problems that occur when the OS recreates the form window using a different descriptor - the Delphi form descriptor is not used, and Windows will not recreate / modify the simple form descriptor created during initialization.

I have been following these approaches for decades and have not experienced any problems with stopping, even with applications with dozens of threads building objects around in queues.

Rgds, Martin

+2
source

This is what I would do ...

  • Use the EventWaitHandle class instead of calling CreateEvent directly. There should not be a need to use a Windows API other than CeRunAppAtEvent (and API calls make the code ugly ...). Do this job first.
  • Before creating the stream, create the ManualResetEvent variable, which is not initially marked. Name it "TerminateEvent".
  • Replace the WaitForSingleObject API call with WaitHandle.WaitAny (WaitHandle []) and pass in an array containing "TerminateEvent" and an EventWaitHandle class that exchanges the CeRunAppAtEvent notification.
  • Your loop can use the WaitAny return value to determine what to do. The return value is the index of the wait descriptor array that unlocked the thread, so you can determine whether to continue the loop or not.
  • To cleanly end a stream, you can call Set on your TerminateEvent and then Join the stream to wait for it to complete.
+1
source

Of course, the preferred way to solve this is to use WaitForMultipleObjects or any other suitable function that can wait for several criteria (e.g. WaitForMultipleObjects , MsgWaitForMultipleObjects , etc.).

However, if you do not have control over the use of a function, there are several sophisticated methods to solve this problem. You can hack functions imported from the system DLL by changing the memory table of the import table of any module. Since WaitForMultipleObjects exported from kernel32.dll, this is normal. using this technique, you can redirect the function call to your own hands, and there you can use WaitForMultipleObjects .

0
source

Source: https://habr.com/ru/post/888334/


All Articles