WaitForSingleObject(m_SomeThread->m_hThread, INFINITE);
-
Here is what my instincts are telling me. 1.) You create your thread and it begins your small intensive calculation. 2.) Your parent thread is preempted and loses its CPU quantum slice before WaitForSingleObject is called. 3.) Your calculation is completed and the thread exits. 4.) Your parent thread resumes WaitForSingleObject and begins waiting on an invalid thread handle. I would suggest the following: 1.) Create your thread suspended. 2.) Duplicate the thread handle. 3.) Wait on the duplicated handle. 4.) Resume the thread. Let me know if it solves your problem. Best Wishes, -David Delaune
Randor wrote:
1.) Create your thread suspended. 2.) Duplicate the thread handle. 3.) Wait on the duplicated handle. 4.) Resume the thread.
Better to create the thread suspended, set the
CWinThread::m_bAutoDelete
toFALSE
and then resume the execution of the thread. TheCWinThread
object will normally destroy itself when the thread exits and consequently close the thread handle. This is prevented by settingm_bAutoDelete
toFALSE
. But it also means that the caller is responsible for deleting theCWinThread
object when the thread has finished. As always: more info here[^]."It's supposed to be hard, otherwise anybody could do it!" - selfquote
"High speed never compensates for wrong direction!" - unknown -
Randor wrote:
1.) Create your thread suspended. 2.) Duplicate the thread handle. 3.) Wait on the duplicated handle. 4.) Resume the thread.
Better to create the thread suspended, set the
CWinThread::m_bAutoDelete
toFALSE
and then resume the execution of the thread. TheCWinThread
object will normally destroy itself when the thread exits and consequently close the thread handle. This is prevented by settingm_bAutoDelete
toFALSE
. But it also means that the caller is responsible for deleting theCWinThread
object when the thread has finished. As always: more info here[^]."It's supposed to be hard, otherwise anybody could do it!" - selfquote
"High speed never compensates for wrong direction!" - unknown -
Hi Roger, Thanks for the comment. Yes what you have described is another valid method to avoid waiting on an invalid thread handle. Describing it as 'better' sounds like an opinion. Best Wishes, -David Delaune
Hi David,
Randor wrote:
Describing it as 'better' sounds like an opinion.
Mmmm, it may be so, but I'd like to clarify my opinion. ;-) If the OP's problem was that he called
::WaitForSingleObject()
with an invalid handle, it would return immediately withWAIT_FAILED
, so this cannot be the problem. If the handle is still valid::WaitForSingleObject()
will either return zero or wait as expected. Your suggestion to duplicate the handle is to circumvent the fact thatCWinThread
will close the handle in its destructor, unless them_bAutoDelete
member is set toFALSE
. In this case my opinion of 'better' is to fix it the MFC way since it was introduced by MFC. I think it's cleaner and hence 'better', but whether that's 'better' or not is a matter of opinion. For what it's worth I think the OP has created some kind of race condition that may end up in a deadlock where the spawned thread sends a message to the main thread. In the message handler call chain he then waits for the thread to finished....."It's supposed to be hard, otherwise anybody could do it!" - selfquote
"High speed never compensates for wrong direction!" - unknown -
Hi David,
Randor wrote:
Describing it as 'better' sounds like an opinion.
Mmmm, it may be so, but I'd like to clarify my opinion. ;-) If the OP's problem was that he called
::WaitForSingleObject()
with an invalid handle, it would return immediately withWAIT_FAILED
, so this cannot be the problem. If the handle is still valid::WaitForSingleObject()
will either return zero or wait as expected. Your suggestion to duplicate the handle is to circumvent the fact thatCWinThread
will close the handle in its destructor, unless them_bAutoDelete
member is set toFALSE
. In this case my opinion of 'better' is to fix it the MFC way since it was introduced by MFC. I think it's cleaner and hence 'better', but whether that's 'better' or not is a matter of opinion. For what it's worth I think the OP has created some kind of race condition that may end up in a deadlock where the spawned thread sends a message to the main thread. In the message handler call chain he then waits for the thread to finished....."It's supposed to be hard, otherwise anybody could do it!" - selfquote
"High speed never compensates for wrong direction!" - unknownRoger Stoltz wrote:
If the OP's problem was that he called ::WaitForSingleObject() with an invalid handle, it would return immediately with WAIT_FAILED
This is not completely correct. The NT kernel recycles both window and thread identifiers (HANDLES). You can easily test this by creating a test dialog with 2 buttons. On the first button create a thread and on the second button end the thread. Rapidly press the buttons and observe the newly created thread sometimes has the same handle value in your traced output. It is the same for window handles... when they are closed the same value can be assigned to a window created in the near future. An excerpt from: The Birth of a Thread: CreateThread[^] Please do not make any assumptions about this identifier; all that is guaranteed is that while the thread is running, no other thread will ever have the same ID. The ID may be recycled later on after the thread has terminated. Multithreading for Rookies[^] Give Me a Handle, and I'll Show You an Object[^] 1.) The benefit of your option is the engineer does not need to call DuplicateHandle. On the negative side your option requires the engineer to manually perform the thread cleanup. 2.) The benefit of the other option is the engineer does no cleanup because m_bAutoDelete defaults to TRUE. On the negative side the engineer has to call DuplicateHandle and then CloseHandle when he is done waiting. Anyway, this debate is meanlingless. If the presented scenario is what is actually occuring then both options presented are good options. This is the beauty of the internet and sites like codeproject.com where professionals can exchange ideas and their experience. Best Wishes, -David Delaune
-
Roger Stoltz wrote:
If the OP's problem was that he called ::WaitForSingleObject() with an invalid handle, it would return immediately with WAIT_FAILED
This is not completely correct. The NT kernel recycles both window and thread identifiers (HANDLES). You can easily test this by creating a test dialog with 2 buttons. On the first button create a thread and on the second button end the thread. Rapidly press the buttons and observe the newly created thread sometimes has the same handle value in your traced output. It is the same for window handles... when they are closed the same value can be assigned to a window created in the near future. An excerpt from: The Birth of a Thread: CreateThread[^] Please do not make any assumptions about this identifier; all that is guaranteed is that while the thread is running, no other thread will ever have the same ID. The ID may be recycled later on after the thread has terminated. Multithreading for Rookies[^] Give Me a Handle, and I'll Show You an Object[^] 1.) The benefit of your option is the engineer does not need to call DuplicateHandle. On the negative side your option requires the engineer to manually perform the thread cleanup. 2.) The benefit of the other option is the engineer does no cleanup because m_bAutoDelete defaults to TRUE. On the negative side the engineer has to call DuplicateHandle and then CloseHandle when he is done waiting. Anyway, this debate is meanlingless. If the presented scenario is what is actually occuring then both options presented are good options. This is the beauty of the internet and sites like codeproject.com where professionals can exchange ideas and their experience. Best Wishes, -David Delaune
Randor wrote:
The NT kernel recycles both window and thread identifiers (HANDLES).
Yep, I know that it does. I don't see how this is relevant in this scenario though; even if the handle gets recycled it can only have one state in the aspect of valid or invalid when
::WaitForSingleObject()
is called, which would mean that if the handle should happen to be invalid when::WaitForSingleObject()
is called it would return WAIT_FAILED. That's why I still think my previous statement is correct even though you seem to think not. Additionally, as I understood it, the OP waits on the thread handle before creating another worker thread (as he should).Randor wrote:
If the presented scenario is what is actually occuring then both options presented are good options.
Of course! :-D
Randor wrote:
Anyway, this debate is meanlingless.
Naah, you provided a perfectly acceptable meaning yourself:
Randor wrote:
This is the beauty of the internet and sites like codeproject.com where professionals can exchange ideas and their experience.
Exactly and I really enjoyed our little discussion.
"It's supposed to be hard, otherwise anybody could do it!" - selfquote
"High speed never compensates for wrong direction!" - unknown -
Randor wrote:
The NT kernel recycles both window and thread identifiers (HANDLES).
Yep, I know that it does. I don't see how this is relevant in this scenario though; even if the handle gets recycled it can only have one state in the aspect of valid or invalid when
::WaitForSingleObject()
is called, which would mean that if the handle should happen to be invalid when::WaitForSingleObject()
is called it would return WAIT_FAILED. That's why I still think my previous statement is correct even though you seem to think not. Additionally, as I understood it, the OP waits on the thread handle before creating another worker thread (as he should).Randor wrote:
If the presented scenario is what is actually occuring then both options presented are good options.
Of course! :-D
Randor wrote:
Anyway, this debate is meanlingless.
Naah, you provided a perfectly acceptable meaning yourself:
Randor wrote:
This is the beauty of the internet and sites like codeproject.com where professionals can exchange ideas and their experience.
Exactly and I really enjoyed our little discussion.
"It's supposed to be hard, otherwise anybody could do it!" - selfquote
"High speed never compensates for wrong direction!" - unknownRoger Stoltz wrote:
I don't see how this is relevant in this scenario though
Is it *very* relevant. I am asserting that there is a small chance that immediately following thread termination it is possible for the same identifier to be assigned to another thread or kernel object. If you like I can create a sample application which continuously spawns/terminates a self deleting thread and then force a context switch before calling WaitForSingleObject. The alertable wait state will return 99% of the time and eventually deadlock. The cause of the deadlock will be a valid handle which is invalid in the context of the process calling WaitForSingleObject. I guess it would be better to use a different terminology as invalid seems to be confusing you. As I have previously mentioned both of our excellent recommendations will avoid the deadlock. Best Wishes, -David Delaune
-
Roger Stoltz wrote:
I don't see how this is relevant in this scenario though
Is it *very* relevant. I am asserting that there is a small chance that immediately following thread termination it is possible for the same identifier to be assigned to another thread or kernel object. If you like I can create a sample application which continuously spawns/terminates a self deleting thread and then force a context switch before calling WaitForSingleObject. The alertable wait state will return 99% of the time and eventually deadlock. The cause of the deadlock will be a valid handle which is invalid in the context of the process calling WaitForSingleObject. I guess it would be better to use a different terminology as invalid seems to be confusing you. As I have previously mentioned both of our excellent recommendations will avoid the deadlock. Best Wishes, -David Delaune
Well, what I have is a button click code: 1. if (m_SomeThread != 0) goto 2. 2. WaitForSingleObject(m_SomeThread->m_hThread, INFINITE); 3. m_SomeThread = AfxBeginThread(...) The thread starts immediatly. What I understood is if you create thread suspended and set autodelete to false, it will not be destroyed as the thread finishes. In that case m_SomeThread->m_hThread will be valid and will not hang the GUI? In that case it needs additional explicit thread destruction between 2. and 3. clauses.
Чесноков
-
Well, what I have is a button click code: 1. if (m_SomeThread != 0) goto 2. 2. WaitForSingleObject(m_SomeThread->m_hThread, INFINITE); 3. m_SomeThread = AfxBeginThread(...) The thread starts immediatly. What I understood is if you create thread suspended and set autodelete to false, it will not be destroyed as the thread finishes. In that case m_SomeThread->m_hThread will be valid and will not hang the GUI? In that case it needs additional explicit thread destruction between 2. and 3. clauses.
Чесноков
-
Roger Stoltz wrote:
I don't see how this is relevant in this scenario though
Is it *very* relevant. I am asserting that there is a small chance that immediately following thread termination it is possible for the same identifier to be assigned to another thread or kernel object. If you like I can create a sample application which continuously spawns/terminates a self deleting thread and then force a context switch before calling WaitForSingleObject. The alertable wait state will return 99% of the time and eventually deadlock. The cause of the deadlock will be a valid handle which is invalid in the context of the process calling WaitForSingleObject. I guess it would be better to use a different terminology as invalid seems to be confusing you. As I have previously mentioned both of our excellent recommendations will avoid the deadlock. Best Wishes, -David Delaune
Randor wrote:
As I have previously mentioned both of our excellent recommendations will avoid the deadlock.
:-) David, no offence taken! I find it interesting and there's a possibility that someone might learn something and it could be me. ;P However, I think you have misread my previous post (or possibly posts). I figure this as I am still under the impression that the OP waits on the thread handle before spawning a new thread, as I've stated previously. - or - Do you really mean that you have experienced a scenario where you have a handle that:
- you provide as argument to e.g.
::WaitForSingleObject()
- is valid so the waiting function doesn't return
WAIT_FAILED
immediately - has not assumed a signalled state (which would make the waiting function return immediately)
- becomes signalled, closed and recycled behind scenes
in a way that makes the waiting function hang? (This is how I interpret your statement.) I must say I seriously doubt that as it would mean a catastrophic design flaw of the synchronization mechanism of the operating system in my opinion.
Randor wrote:
If you like I can create a sample application which continuously spawns/terminates a self deleting thread and then force a context switch before calling WaitForSingleObject. The alertable wait state will return 99% of the time and eventually deadlock.
If you are able to do that and wait on the thread handle before creating a new thread, I'd be delighted to see it. I will even try and create such an abomination myself. But if this is the case I don't see how any of our proposed solutions may prevent this deadlock.
Randor wrote:
The cause of the deadlock will be a valid handle which is invalid in the context of the process calling WaitForSingleObject. I guess it would be better to use a different terminology as invalid seems to be confusing you.
Well, I wasn't confused until I read the underlined part above. But the confusion is more about what you mean rather than whether a handle is valid or invalid.
"It's supposed to be hard, otherwise anybody could do it!" - selfquote
"High speed never compensates for wrong direction!" - unknown - you provide as argument to e.g.
-
Randor wrote:
As I have previously mentioned both of our excellent recommendations will avoid the deadlock.
:-) David, no offence taken! I find it interesting and there's a possibility that someone might learn something and it could be me. ;P However, I think you have misread my previous post (or possibly posts). I figure this as I am still under the impression that the OP waits on the thread handle before spawning a new thread, as I've stated previously. - or - Do you really mean that you have experienced a scenario where you have a handle that:
- you provide as argument to e.g.
::WaitForSingleObject()
- is valid so the waiting function doesn't return
WAIT_FAILED
immediately - has not assumed a signalled state (which would make the waiting function return immediately)
- becomes signalled, closed and recycled behind scenes
in a way that makes the waiting function hang? (This is how I interpret your statement.) I must say I seriously doubt that as it would mean a catastrophic design flaw of the synchronization mechanism of the operating system in my opinion.
Randor wrote:
If you like I can create a sample application which continuously spawns/terminates a self deleting thread and then force a context switch before calling WaitForSingleObject. The alertable wait state will return 99% of the time and eventually deadlock.
If you are able to do that and wait on the thread handle before creating a new thread, I'd be delighted to see it. I will even try and create such an abomination myself. But if this is the case I don't see how any of our proposed solutions may prevent this deadlock.
Randor wrote:
The cause of the deadlock will be a valid handle which is invalid in the context of the process calling WaitForSingleObject. I guess it would be better to use a different terminology as invalid seems to be confusing you.
Well, I wasn't confused until I read the underlined part above. But the confusion is more about what you mean rather than whether a handle is valid or invalid.
"It's supposed to be hard, otherwise anybody could do it!" - selfquote
"High speed never compensates for wrong direction!" - unknownHi Roger,
Roger Stoltz wrote:
Do you really mean that you have experienced a scenario where you have a handle that:
Let me clarify the scenario I am presenting: 1.) Engineer creates a CWinThread object with m_bAutoDelete set to TRUE. 2.) CWinThread is started. 3.) A context switch takes place in GUI thread. 4.) CWinThread completes and exits and the handle is dereferenced. 5.) GUI thread resumes from context switch. 6.) GUI calls WaitForSingleObject on the CWinThread->m_hThread with INFINITE. 7.) The handle is a valid handle, another object has been assigned this identifier. 8.) WaitForSingleObject never returns. Yes, I have encountered this before in the real world. You can produce an application which exibits this handle recyling deadlock behavior.
Roger Stoltz wrote:
I must say I seriously doubt that as it would mean a catastrophic design flaw of the synchronization mechanism of the operating system in my opinion.
I'm surprised that you have not read about this before. Its the same thing with window handles. I have noticed that you often reference Dr. Joseph Newcomer in your posts. What is occuring under the hood is essentially the same principles of the flaw described in Avoiding Multiple Instances of an Application[^] and window handles. Window handles can be closed and/or re-assigned to other windows. This can lead to deadlocks/race conditions. It is the essentially the same for thread/process handles. Handles cannot always be relied upon and they may be valid, invalid or re-assigned. You can probably find other software engineers describing this flaw: DuplicateHandle CWinThread Newcomer[^] Best Wishes, -David Delaune
- you provide as argument to e.g.
-
Hi Roger,
Roger Stoltz wrote:
Do you really mean that you have experienced a scenario where you have a handle that:
Let me clarify the scenario I am presenting: 1.) Engineer creates a CWinThread object with m_bAutoDelete set to TRUE. 2.) CWinThread is started. 3.) A context switch takes place in GUI thread. 4.) CWinThread completes and exits and the handle is dereferenced. 5.) GUI thread resumes from context switch. 6.) GUI calls WaitForSingleObject on the CWinThread->m_hThread with INFINITE. 7.) The handle is a valid handle, another object has been assigned this identifier. 8.) WaitForSingleObject never returns. Yes, I have encountered this before in the real world. You can produce an application which exibits this handle recyling deadlock behavior.
Roger Stoltz wrote:
I must say I seriously doubt that as it would mean a catastrophic design flaw of the synchronization mechanism of the operating system in my opinion.
I'm surprised that you have not read about this before. Its the same thing with window handles. I have noticed that you often reference Dr. Joseph Newcomer in your posts. What is occuring under the hood is essentially the same principles of the flaw described in Avoiding Multiple Instances of an Application[^] and window handles. Window handles can be closed and/or re-assigned to other windows. This can lead to deadlocks/race conditions. It is the essentially the same for thread/process handles. Handles cannot always be relied upon and they may be valid, invalid or re-assigned. You can probably find other software engineers describing this flaw: DuplicateHandle CWinThread Newcomer[^] Best Wishes, -David Delaune
Okay David, now I know what you mean and of course you're right. I am aware if the scenario you're describing, or perhaps I should put it as "I have been"... :~ I've used the technique I described earlier (CWinThread::m_bAutoDelete blah blah) for a rather long time (must be almost a decade :doh: ) which saves me from a lot of troubles, including this one. I just forgot one of the reasons why I always use this technique and had a hard time putting it together in this context. :-\
Randor wrote:
I'm surprised that you have not read about this before.
Well, I take that as a compliment. :-O But in my defence I have to mention that until your last post I interpreted your statement as the handle provided to the waiting function indeed was a valid handle to the worker thread, but it got recycled during the time one thread was waiting on the handle and that caused the waiting function to hang. That's what made me "seriously doubt it" since it would make the whole synchronization mechanism unreliable. So it turns out I misunderstood you, to my relief. I'm grateful for the reminder David, thanks.
"It's supposed to be hard, otherwise anybody could do it!" - selfquote
"High speed never compensates for wrong direction!" - unknown -
Okay David, now I know what you mean and of course you're right. I am aware if the scenario you're describing, or perhaps I should put it as "I have been"... :~ I've used the technique I described earlier (CWinThread::m_bAutoDelete blah blah) for a rather long time (must be almost a decade :doh: ) which saves me from a lot of troubles, including this one. I just forgot one of the reasons why I always use this technique and had a hard time putting it together in this context. :-\
Randor wrote:
I'm surprised that you have not read about this before.
Well, I take that as a compliment. :-O But in my defence I have to mention that until your last post I interpreted your statement as the handle provided to the waiting function indeed was a valid handle to the worker thread, but it got recycled during the time one thread was waiting on the handle and that caused the waiting function to hang. That's what made me "seriously doubt it" since it would make the whole synchronization mechanism unreliable. So it turns out I misunderstood you, to my relief. I'm grateful for the reminder David, thanks.
"It's supposed to be hard, otherwise anybody could do it!" - selfquote
"High speed never compensates for wrong direction!" - unknownHi Roger, Now I am uncertain if this was the problem Yuriy was having. I just read the entire thread again and noticed that in this post[^] Yuri states that "99% of cases the thread is already terminated, when the WaitForSingleObject() is called." :doh: I am starting to think there may have been a serious design flaw. I guess we should have asked to see some code. :) Anyway I am leaving the office soon. Have a good night, -David Delaune
-
Hi Roger, Now I am uncertain if this was the problem Yuriy was having. I just read the entire thread again and noticed that in this post[^] Yuri states that "99% of cases the thread is already terminated, when the WaitForSingleObject() is called." :doh: I am starting to think there may have been a serious design flaw. I guess we should have asked to see some code. :) Anyway I am leaving the office soon. Have a good night, -David Delaune
Well, the OP's problem could still be the one we've been discussing. There is a possibility that the handled gets recycled between thread termination and the call to
::WaitForSingleObject()
. When he claims that the thread is already terminated in 99% of the cases I doubt he really checks the return value; the wait may have failed since the handle has been closed but he doesn't care because it means that the thread doesn't run any longer. As I wrote earlier I still think he has introduced a race condition where the worker thread somehow sends a message to the main thread instead of posting. If the main thread has already assumed its waiting state on the thread handle the deadlock is a reality. This is in my opinion the most common reason for deadlocks when it comes to multithreading and would most definitely result in the behaviour he has described. It may be that he's calling a framework function that disguises the::SendMessage()
call. The probability for a design flaw I guess is very close to 100%. I will have a good night; it's almost 1:00AM in Sweden and I'm about to hit the sack. I wish you a good night as well! -- Rog"It's supposed to be hard, otherwise anybody could do it!" - selfquote
"High speed never compensates for wrong direction!" - unknown