locked
Control.Invoke/BeginInvoke and PostMessage RRS feed

  • Question

  • What exactly gets posted to the message queue in case of control.Invoke or control.BeginInvoke?

     

    For example, if you have the following delegate:

     

        Public Delegate Sub StrPtr(ByVal Buffer As string)

     

    and then calls:

     

        Me.BeginInvoke(New StrPtr(AddressOf MyMethod), StringBuffer)

     

    this gets translated to a PostMessage call. This call has only four arguments: hWnd , msg, wParam and lParam, where lParam is normally used for arguments.

     

    1) I have read somewhere that the messages, which gets posted to the message queue, are really just delegates linked together by means of the _PREV field (message queue just a linked list of delegates). In that case, the method and target fields of the delegate could be used to point to MyMethod and the argument for MyMethod could be transferred in lParam. Then it also makes sense that control.Invoke/BeginInvoke has only one argument (not compiler generated like delegate.Invoke/BeginInvoke) because there is only place for one argument in the message structure (lParam), but is this true?

     

    2) If no, how does the message structure, which gets posted, looks like and where is the address of the delegate transferred - in the lParam parameter?

     

    3) What name (WM_??) is used for msg so that WndProc can recognize it and execure the method (MyMethod)?

     

    4) Does WndProc just execute the delegate like this (in case the delegate is transferred in lParam)?

     

    Case WM_??

         m.lParam.Invoke

     

    5) How is the invocation list of a delegate build? Is it just a linked list of delegates (_PREV field), or is it a separate list (only a list of instance.method's).

     

     

    PS. This thread was previously posted to this forum (July 4th), but very quickly moved to the Windows Forms General forum by a moderator. Unfortunately, nobody there seems to know - even after I moved it up front two times, so please let it stay here - at least for a while. It cannot be true that nobody knows how basic functions like the message queue is build, and what goes on behind the scenes with a very common function like control.Invoke/BeginInvoke.


    Monday, September 10, 2007 11:52 AM

Answers

  • Nobody answered because nobody probably knows beyond MS.  Why do you really care though?  It is an implementation detail on how the invoke data gets through the message queue.  Just in case you're getting the idea you should never try to code against the implementation detail as it could easily be broken in a subsequent release.

     

    Internally the framework is going to package up the information, pass it through the message queue and unpack it on the other side.  It is actually pretty trivial to do.  All you have to do is allocate some memory, copy an internal structure into it and send a custom message with a pointer to the memory.  On the client side it gets the message, grabs the data, invokes the method, packages up the results and returns.  When the invocation is complete the server releases the memory.  For Invoke it can actually use the stack rather than allocating memory.  There are a few complications such as sharing memory and whatnot but nothing major.  However this isn't how it works currently from what I can tell as this would require too much work.

     

    The actual message sent is dynamically built based upon an internal property value.  The ending is _ThreadCallbackMessage though.  This message is registered the first time it is needed.  Here is the general algorithm:

    1. Create the custom message if needed
    2. Add the delegate info to an internal list maintained by the control (actually a stack to support multiple invokes)
    3. The control sends the custom message to itself through the message queue
    4. (On the UI thread)The custom message is received
    5. The control pops the callback information from the list and invokes the delegate
    6. The control returns from processing the message

    It is quick and efficient with no memory transfer needed.

     

    Michael Taylor - 9/11/07

    http://p3net.mvps.org

     

     

    Tuesday, September 11, 2007 1:24 PM
  • 1) A message is nothing more than an integral value.  In the Windows header files you will see things like WM_USER and WM_PAINT but this is strictly a C symbolic definition mapped to an integral value.  Windows itself breaks up the message range to help avoid a conflict between custom messages and Windows messages but all that really matters is the numeric value.  That is why when you register a custom message you do so using an integral value and a "friendly" string name.  The name is all but irrelevant.  Nowhere in Windows will you find an API that accepts a string as a parameter and returns its numeric message value or vice versa (AFAIK).  The reason the names are uppercase is because that is how C programmers define symbolic values historically.  The actual name is irrelevant.  In fact several WM_ symbols map to the exact same message number.  Nobody but a programmer cares about the symbolic name.

     

    2)  Messages are sent to specific windows.  Each UI thread has its own message pump.  DispatchMessage is responsible for taking the message from the queue and calling the window procedure (wndproc) for the window associated with the message.  The message structure contains the target window.  When the window was originally created it was associated with the wndproc.  Wndproc is responsible for taking the message apart and either handling it directly or, more likely, passing it along.  It is within here where .NET will call OnPaint or OnLoad or whatever else is appropriate.  Internally though when the custom message arrives the wndproc picks off the call and handles the invocation directly.

     

    Each control in .NET has its own wndproc (you can even override it as it is virtual).  Deep in the bowels of .NET code is responsible for ultimately making sure the control's wndproc is invoked correctly.  You are correct that once the message is in the queue the original control is no longer needed.  However it really doesn't matter whether .NET uses the top-most form or the control itself to process the message.  Either way it'll be running in the right thread and it'll invoke the correct method.

     

    Again, the delegate information is not passed to the message queue.  Internally there is a "stack" of callback objects where the delegate information is stored.  The method called by the wndproc routine only needs to pop the topmost callback from the list to get the delegate information.  It can then invoke it.  I recommend that if you are still confused about how this happens you should open the source code in Reflector and review it.  Look for Control.BeginInvoke and you'll see all the code.  As for the conversion you will see that BeginInvoke calls PostMessage after it pushes the callback data onto the internal list.

     

    While I agree that knowing how something works is good I don't agree that you can't use something well without understanding how it works.  There is just too much to keep track of.  You should pursue those areas that interest you and just let the rest of it go.  For example you can probably assume that you are an expert at sending e-mail or browsing the web but do you really understand how it works?  Do you understand how your machine is able to find other machines even though they aren't on the same network?  Routers and DNS servers are the "high level" implementations.  What about the IP packets?  IMHO the sign of a good technology is one that you can use without knowing the details of how it works.  File IO and INet communication are good technologies because I can use them for just about anything while knowing very little about them.  Graphics programming, Win32 development and security are not because using them requires significant understanding of how everything works together.


    So, in summary, if this is an area that interests you then feel free to search for more information and figure out how everything works.  That is, after all, how experts are made.  However if you are strictly interested so you can use BeginInvoke/Invoke properly then don't worry about it.  It just works.

     

    Michael Taylor - 9/11/07

    http://p3net.mvps.org

    Tuesday, September 11, 2007 4:39 PM

All replies

  • Nobody answered because nobody probably knows beyond MS.  Why do you really care though?  It is an implementation detail on how the invoke data gets through the message queue.  Just in case you're getting the idea you should never try to code against the implementation detail as it could easily be broken in a subsequent release.

     

    Internally the framework is going to package up the information, pass it through the message queue and unpack it on the other side.  It is actually pretty trivial to do.  All you have to do is allocate some memory, copy an internal structure into it and send a custom message with a pointer to the memory.  On the client side it gets the message, grabs the data, invokes the method, packages up the results and returns.  When the invocation is complete the server releases the memory.  For Invoke it can actually use the stack rather than allocating memory.  There are a few complications such as sharing memory and whatnot but nothing major.  However this isn't how it works currently from what I can tell as this would require too much work.

     

    The actual message sent is dynamically built based upon an internal property value.  The ending is _ThreadCallbackMessage though.  This message is registered the first time it is needed.  Here is the general algorithm:

    1. Create the custom message if needed
    2. Add the delegate info to an internal list maintained by the control (actually a stack to support multiple invokes)
    3. The control sends the custom message to itself through the message queue
    4. (On the UI thread)The custom message is received
    5. The control pops the callback information from the list and invokes the delegate
    6. The control returns from processing the message

    It is quick and efficient with no memory transfer needed.

     

    Michael Taylor - 9/11/07

    http://p3net.mvps.org

     

     

    Tuesday, September 11, 2007 1:24 PM
  • Thanks for your description.

     

    The reason why I want to know is probably the same reason why the Americans have spend an awfull lot of money putting a man on the moon - it gives you some useful information about the world we live in (in my case Windows).

     

    I truely believe that deep knowledge about what you are doing is the key to reliable programming. For example, I have been lucky enough to get some inside information about the serial port from Kim Hamilton - the one, who has programmed it, and I would assert that without this knowledge you cannot know how to use the port in a reliable way. Based on the knowledge in the help files, you may as well throw a dice. Of course I have made this knowledge available to everybody on our knowledgebase http://www.innovatic.dk/knowledg/SerialCOM/SerialCOM.htm , but there are still some loose ends to tie up - the reason for this thread.

     

    The message queue is the central part of the GUI, but only described very briefly and usually not very well understood. For example, I have even seen a MVP not knowing the difference between Sleep(0) and Application.DoEvents()! Besides, how can you e.g. override the OnPaint method if you are not absolutely sure how it works?

     

    There are a few details in your description, which does not quite fit into the view I have about how things works.

     

    1) All messages are usually defined with capital letters like WM_PAINT and WM_CLICK, so I find it a little unlogical that the message send by control.Invoke or control.BeginInvoke is called WM_TreadCallBackMessage and why ..Message? WM means Window message. You may be right, but in that case there also ought to be a OnThreadCallBackMessage method and maybe a ThreadCallBackMessage event, which does not sound right in my ears although it may of course be true. Something like WM_USER and OnUser sounds more logical to me.

     

    2) Messages on the message queue are send to WndProc of the right window (hWnd) by DispatchMessage(msg) in the message pump loop so how can the control receive the message and invoke the delegate? As I see it, once the message has been posted to the message queue, the control is no longer needed. WndProc can easily invoke the delegate. You could probably use the API function PostMessage instead and in that case, there is no control. I also find it difficult to find space for the control information within the four arguments of the message, which is actually one of the reasons for this thread - how is the conversion between control.Invoke/beginInvoke and PostMessage done?

     

     

     

    Tuesday, September 11, 2007 2:37 PM
  • What you're asking is an implementation detail of the .NET framework, to some extent proprietary.

     

    Conceptually, Control.BeginInvoke is the same as PostMessage and Control.Invoke is the same as SendMessage.  How they are actually implemented is an implementation detail.  All you need to be concerned with is that Control.Invoke is blocking and will attempt to invoke the code next; where BeginInvoke is asynchronous and will add something on a queue in order to invoke that code after all the other current entries in the queue.

     

    What do you hope to get out of knowing how that is implemented?

    Tuesday, September 11, 2007 4:12 PM
  • 1) A message is nothing more than an integral value.  In the Windows header files you will see things like WM_USER and WM_PAINT but this is strictly a C symbolic definition mapped to an integral value.  Windows itself breaks up the message range to help avoid a conflict between custom messages and Windows messages but all that really matters is the numeric value.  That is why when you register a custom message you do so using an integral value and a "friendly" string name.  The name is all but irrelevant.  Nowhere in Windows will you find an API that accepts a string as a parameter and returns its numeric message value or vice versa (AFAIK).  The reason the names are uppercase is because that is how C programmers define symbolic values historically.  The actual name is irrelevant.  In fact several WM_ symbols map to the exact same message number.  Nobody but a programmer cares about the symbolic name.

     

    2)  Messages are sent to specific windows.  Each UI thread has its own message pump.  DispatchMessage is responsible for taking the message from the queue and calling the window procedure (wndproc) for the window associated with the message.  The message structure contains the target window.  When the window was originally created it was associated with the wndproc.  Wndproc is responsible for taking the message apart and either handling it directly or, more likely, passing it along.  It is within here where .NET will call OnPaint or OnLoad or whatever else is appropriate.  Internally though when the custom message arrives the wndproc picks off the call and handles the invocation directly.

     

    Each control in .NET has its own wndproc (you can even override it as it is virtual).  Deep in the bowels of .NET code is responsible for ultimately making sure the control's wndproc is invoked correctly.  You are correct that once the message is in the queue the original control is no longer needed.  However it really doesn't matter whether .NET uses the top-most form or the control itself to process the message.  Either way it'll be running in the right thread and it'll invoke the correct method.

     

    Again, the delegate information is not passed to the message queue.  Internally there is a "stack" of callback objects where the delegate information is stored.  The method called by the wndproc routine only needs to pop the topmost callback from the list to get the delegate information.  It can then invoke it.  I recommend that if you are still confused about how this happens you should open the source code in Reflector and review it.  Look for Control.BeginInvoke and you'll see all the code.  As for the conversion you will see that BeginInvoke calls PostMessage after it pushes the callback data onto the internal list.

     

    While I agree that knowing how something works is good I don't agree that you can't use something well without understanding how it works.  There is just too much to keep track of.  You should pursue those areas that interest you and just let the rest of it go.  For example you can probably assume that you are an expert at sending e-mail or browsing the web but do you really understand how it works?  Do you understand how your machine is able to find other machines even though they aren't on the same network?  Routers and DNS servers are the "high level" implementations.  What about the IP packets?  IMHO the sign of a good technology is one that you can use without knowing the details of how it works.  File IO and INet communication are good technologies because I can use them for just about anything while knowing very little about them.  Graphics programming, Win32 development and security are not because using them requires significant understanding of how everything works together.


    So, in summary, if this is an area that interests you then feel free to search for more information and figure out how everything works.  That is, after all, how experts are made.  However if you are strictly interested so you can use BeginInvoke/Invoke properly then don't worry about it.  It just works.

     

    Michael Taylor - 9/11/07

    http://p3net.mvps.org

    Tuesday, September 11, 2007 4:39 PM
  • Peter Ritchie

     

    As far as I know, it is not correct that control.Invoke does a SendMessage call although it is similar to SendMessage. To prevent that control.Invoke gets higher priority than control.BeginInvoke and to ensure that control.Invoke becomes thread safe (no direct call from maybe another thread, but a call through the message queue and therefore on the UI thread) they both do a PostMessage call initially.

     

     

    TaylorMichaelL

     

    Thanks a lot for your description. Now it fits into my view of the internal operation. Do you know the name of the routine, which is called by WndProc and strips off the delegate from the stack and invoke it - just so that I know what to call it in my description (above link)?

     

     

    To both.

     

    I am fully aware of how to use control.Invoke/BeginInvoke. It is just the secret below the hood I don't fell I know sufficiently enough about. I have never used Reflector before, but maybe it is a good idea to try it.

     

    If you once should get a lot of time, please read and comment my description of the serial port and basic functions (above link). All comments, suggestions, additions, corrections etc. are very welcome. I have only one intention - to make the description 100% technical correct, but still understandable even by beginners (unlike the help files), and I know that it already has helped a lot of people. Because the description both describes the message queue and WndProc and also describes control.Invoke/BeginInvoke, I want to close the gab and make a case in my pseudo WndProc for control.Invoke/BeginInvoke.

    Tuesday, September 11, 2007 7:04 PM
  • I don't know the name of the function that is ultimately called but you can use Reflector to figure it out.  Start with Control.WndProc.  If it isn't in there then it is either in a base class (unlikely) or a derived class.  Another approach is to go to the underlying callback field in the class (you can find the name by looking at Control.Invoke) and then ask Reflector to find everybody who uses it.  The implementation code is in that list somewhere.  Reflector is a must for understanding the framework.  Most any "non-simple" question on the forums (such as this question, in fact) has me going to Reflector to find out how things work. 

     

    I'm going to recommend that, for improving documentation, that you should consider getting involved in the Community Content initiative at MSDN.  This is where you (as part of the community) can provide more information and comments that are then visible to the live MSDN documentation that everybody uses.  Note that providing too much information is as bad as providing too little so you will never see a case where all possible scenarios of how to use something or how it is implemented would be documented.  This is simply too overwhelming for most people.  Indeed implementation details should almost never be discussed as the author is then responsible for keeping the information up to date for newer versions.  Generally speaking a logical "how it works" discussion is a better approach. 

     

    For example in this threads discussion I would describe the process logically by saying that calling Invoke would package up the delegate information, send it to the UI thread through the message queue and then invoke the delegate on the UI thread.  This is a logical description that avoids the, potentially changing, details of how it actually works yet provides readers with enough information to allow them to understand how it works.  The fact that it uses a callback list vs. passing the raw data is an implementation detail that has no impact on one's understanding of the process.  IMHO.

     

    Michael Taylor - 9/11/07

    http://p3net.mvps.org

     

    Tuesday, September 11, 2007 7:20 PM
  •  Carsten Kanstrup wrote:

    Peter Ritchie

     

    As far as I know, it is not correct that control.Invoke does a SendMessage call although it is similar to SendMessage. To prevent that control.Invoke gets higher priority than control.BeginInvoke and to ensure that control.Invoke becomes thread safe (no direct call from maybe another thread, but a call through the message queue and therefore on the UI thread) they both do a PostMessage call initially.

    Yes, I was trying to say that we don't really know how Control.[Begin]Invoke is implemented.  They may make a call to SendMessage or PostMessage, they may not.  In either case you shouldn't rely on how it is implemented.

     

    Neither Control.Invoke nor Control.BeginInvoke is thread safe.  Using either can cause deadlocks in multithreaded code.

     

    Generally, AsyncOperation is used to invoke delegates with respect to Control.BeginInvoke and Control.Invoke, and usually through a SynchronizationContext object.  It's probably a bit more complicated than simply invoking AsyncOperation.Post or AsyncOperation.Send depending on whether Invoke or BeginInvoke is called...  When running a WinForms application the type of the SynchcronizationContext class that is used is WindowsFormsSynchronizationContext.  You can follow down from the WindowsFormsSynchronizationContext.Post or Send to see what its actually doing.

    Tuesday, September 11, 2007 7:47 PM
  • Thanks again Michael.

     

    I agree with you that a documentation should be as simple and logical as possible. However, it is always so that the real experts can explain thinks simple and understandable where others with less knowledge usually use fuzzy, unlogical explanations, which are difficult to understand, so it requires an expert to make a simple description!!! Once I understand exactly how things works, I can leave out unimportant details (like the stack for delegates) and still have a  technical correct description. The stack is probably only there to reduce the necessary data copying.

     

    Do you have a link to the Community Content initiative at MSDN?

    Tuesday, September 11, 2007 8:00 PM
  • Tuesday, September 11, 2007 8:05 PM
  • Peter Ritchie

     

    Control.Invoke and Control.BeginInvoke are used to marshal a call to another thread (usually the UI thread) in a thread safe way. That's the whole purpose of these calls. Why are they not thread safe? The method(s) pointed to by the delegate is invoked on the UI thread and will therefore automatically become thread safe in the way that the method(s) can call any methods of controls generated on the UI thread.

    Tuesday, September 11, 2007 8:09 PM
  • They marshal to a specific thread, but they're not thread-safe.  For example, if you call SerialPort.Close in a delegate invoked by Control.Invoke you get a deadlock.  Also, Control.BeginInvoke is a non blocking call that "transfers control" to the same thread.  So, you can get into threading deadlocks reasonably easily using synchronization objects that don't check the current thread when locking, like semaphore.  Ian Griffiths has further detail on Invoke/BeginInvoke like http://www.interact-sw.co.uk/iangblog/2004/09/02/eventargsgone and http://www.interact-sw.co.uk/iangblog/2004/04/20/whatlocks

    Tuesday, September 11, 2007 8:44 PM
  • Peter Ritchie

     

    The deadlock in serial port is not coursed by control.Invoke, but by a lock on the underlying SerialStream object.

     

    If you try to close the port from the UI thread while the event handler for the DataReceived event (or any other event) is updating the GUI by means of Invoke (not BeginInvoke), the port will not close and the application freezes. The reason for this is that Close() waits for events to finish executing before it closes the port (due to the lock), but the events cannot finish before the Invoke operation is finished (event handler terminated) and this cannot happen while the UI thread hangs in the Close() call. A workaround for this is therefore either to close the port from a different thread (not the UI thread) so that Close() does not block for the execution of the GUI update (Invoke), or to use BeginInvoke, which does not block the event handler.

     

    As you can see - deep below-the-hood knowledge is not so bad anyway ;-)

     

    Thanks for the links. I will studdy them in details, but they don't seem to have anything to do with control.Invoke and control.BeginInvoke not being thread safe.

    Wednesday, September 12, 2007 6:43 AM
  • TaylorMichaelL

     

    I have now studied the links of Peter Ritchie and the first one actually describes in details what goes on. The method, which invokes the delegates, is InvokeMarshaledCallbacks. It seems that all delegates in the callback queue - not just the top-most one - are executed if control.Invoke is called from the UI thread or there is just one WM_ThreadCallbackMessage on the message queue (InvokeMarshaledCallbacks empty the entire callback queue). It is a really interesting article, which gives you something to think about, and it conferms that deep background knowledge can save you some problems ;-)

    Wednesday, September 12, 2007 11:01 AM
  • It depends on what you consider "thread safe" I guess.  If you consider the possibility that a method could cause a deadlock simply because of the type of thread that it is run from then I wouldn't consider Control.Invoke thread safe.  Read the documentation for Control.Invoke carefully, it is a blocking call (when called from a thread other than the thread that created the control) and executes code on a specific thread.  That UI thread could be blocked on a call to Thread.Join, blocked on a lock statement that the current thread has locked, etc. that would cause a deadlock when Control.Invoke is called.  If using the InvokeRequired/Invoke pattern, this is always in the presence of multiple threads.  That, to me, is not "thread-safe".  Another example of code that deadlocks because Control.Invoke's threading uniqueness (very simplified from in-the-wild examples):

    Code Snippet

            private Object locker = new Object();

            private void ThreadEntry()

            {

                lock (locker)

                {

                    this.Invoke((MethodInvoker)Method);

                }

            }

     

            private void Method()

            {

                lock (locker)

                {

                    Trace.WriteLine("On GUI thread");

                }

            }

     

     

     

    I, personally, don't believe library code can be "thread-safe", there's always scenarios with multiple threads (as with the above) that can't be accounted for in most shared code.  You may want to read through Rico Mariani's blog where he discusses this sort of thing, and I believe he explains why attempts to make framework methods "thread-safe" were removed and avoided because of it's inherit impossibility.

    Wednesday, September 12, 2007 2:15 PM
  • I think that my definition of thread safety is different from yours and perhaps closer to what Microsoft calls thread safety in e.g. their help files.

     

    My definition is based on the fact that because of the preemptive multithreading, where you can never know when you loose control, you are not allowed to call methods of controls generated on other threads except for the four so-called thread safe methods Invoke, BeginInvoke, EndInvoke and CreateGraphics. This is also the definition you find in the help files, and I think that it is a very resonable definition, which is generally accepted - except for you. With cooperative multithreading (no time slicing), all methods would be thread safe except of course for interrupt routines. This is one of the reasons why I regard preemptive multithreading as something the devil has created when he was in a real bad mood, but this is of course a completely different story (everything has to be transferred by value, endless data copying, endless locking with possible deadlocks, complicated delegates with their own methods, low efficiency etc. etc.).

     

    You can always create a deadlock with blocking methods, but to me this is no reason for calling any blocking method not thread safe if it is from a preemptive point of view.

    Wednesday, September 12, 2007 3:09 PM
  • I'm going to chime in here.  Carsten your definition of thread safety is not correct.  MS doesn't define TS that way either.  You are confusing UI thread requirements with general thread safety.  Perhaps I can explain it more clearly.

     

    We say that a method is thread safe IIF you can call the method on different threads simultaneously AND each method invocation does not corrupt the state of another.  In general terms this is only an issue when dealing with shared resources that a method might work with.  For example if a method adds an item to a list and you invoke the method on several different threads then the method is not TS.  The general rule in .NET is that static members are thread safe and instance members are not.  There are exceptions.  To make a method TS you must normally use locking primitives to ensure that only one thread is manipulating a shared resource at any one time.  A TS method, and this is really important, doesn't mean it can't be called on multiple threads but that it will work if that were to occur.

     

    That is not the issue with your post though.  When dealing with controls Windows mandates that all access to a control occurs only on the thread that created the control.  The various Invoke methods and properties on a control are dealing with this and not TS.  Since the message queue is single threaded you can ensure that sequential method invocations occur by sending messages to a control.  This doesn't make the method(s) TS it simply serializes access. Nothing prevents the methods from being invoked from another thread.

     

    When MS implemented the various invoke methods they coded them to be TS so they can be invoked on multiple threads.  They did this by using locking primitives to secure shared resources.  The method itself can be called on multiple threads simultaneously and it'll just work.  In the case of Invoke, for example, the only non-TS aspect of the implementation is when it adds the delegate to the callback list.  Therefore the method uses a primitive to make the method TS.  However the actual sending of the message is not synchronized.  Therefore it is possible for one thread to push a delegate onto the list but then be preempted by another thread calling the same method.  In this case the second invocation might post the message after it pushed its delegate.  So when control returns to the first invocation it might post a message even though nothing will happen.

     

    Preemptive MT is not evil.  Windows prior to Win95 was cooperative.  The general user experience there was that a bad app would deadlock the system requiring a hard reboot.  Preemptive MT OSes are the norm and considered to be the only way to go for non-hard RTOS.  If you disagree then I'm sorry but the industry has had decades of research and experience to back its decision so I doubt we're wrong.  Once you have a complete  understanding of the advantages and disadvantages of each I think you'll agree.  What we lack today is easy to use MT primitives to make MT easier to work with.

     

    IMHO,
    Michael Taylor - 9/12/07

    http://p3net.mvps.org

     

    Wednesday, September 12, 2007 3:33 PM
  •  Carsten Kanstrup wrote:

    I think that my definition of thread safety is different from yours and perhaps closer to what Microsoft calls thread safety in e.g. their help files.

     

    My definition is based on the fact that because of the preemptive multithreading, where you can never know when you loose control, you are not allowed to call methods of controls generated on other threads except for the four so-called thread safe methods Invoke, BeginInvoke, EndInvoke and CreateGraphics. This is also the definition you find in the help files, and I think that it is a very resonable definition, which is generally accepted - except for you. With cooperative multithreading (no time slicing), all methods would be thread safe except of course for interrupt routines. This is one of the reasons why I regard preemptive multithreading as something the devil has created when he was in a real bad mood, but this is of course a completely different story (everything has to be transferred by value, endless data copying, endless locking with possible deadlocks, complicated delegates with their own methods, low efficiency etc. etc.).

     

    You can always create a deadlock with blocking methods, but to me this is no reason for calling any blocking method not thread safe if it is from a preemptive point of view.

    Preemptive/cooperative are no different when it comes to thread-safety.  Whether one thread loses the CPU to another is beside the point, it's because two or more threads are running simultaneously (either effectively or in reality and in all environments that support multiple processors you can't know).

     

    "with cooperative multithreading (no time slicing) all methods would be thread-safe..." clearly you don't understand thread-safety.  Two threads need to synchronize their invariants (part of being "thread-safe") regardless of cooperative or preemptive multi-threading.

     

    In the following example, the method Method is not thread-safe in a cooperative multi-threading environment (which doesn't really exist) or any other multi-threading environment:

     

    Code Snippet

    private int day = 31;

    private int month = 1;

    private int year = 2007;

    public void Method()

    {

    month = 2;

    day = 29;

    year = 2008;

    }

    public DateTime GetDateTime()

    {

        return new DateTime(year, month, day);

    }

     

     

     

     

      The rest of your post is just nonsensical.

    Wednesday, September 12, 2007 3:40 PM
  • Maybe Microsoft don't define thread safety my way, but in that case they really have a lot of help files to change for example this note (just one of many):

     

    "In addition to the InvokeRequired property, there are four methods on a control that are thread safe: Invoke, BeginInvoke, EndInvoke, and CreateGraphics. For all other method calls, you should use one of the invoke methods to marshal the call to the control's thread."

     

    If not even these methods are regarded as thread safe, it has no meaning to talk about thread safety, because nothing would be thread safe as Peter also says.

     

    I don't think that cooperative multithreading is the future either, but my point is that if no methods was interrupted and no messages was posted to the message queue if they were already there, it is quite easy to guarantee that all methods gets a chance of using some data before the data can change again so nothing needs to be transferred by value = no data copying = much higher efficiency and no degeneration. I have actually used such a system since 1978 for highly demanding electronic process control where a stop may easily cost $20,000 per hour (would you do that with Windows?), so I know a little about what I am talking about. There are really other possiblilties than the traditional ones! Except for dual processor systems, it is an illusion that a computer can run many jobs simultaneously so threads are really just one way of handling job priorities. Have you ever thought of it that way?

     

    Please Peter, a comment like "The rest of your post is just nonsensical." does not belong to this forum. I am an electronic engineer and have worked with computer systems since Intel 4004. I am as professional as you. We look differently on many things, but none of us are fools or amateurs, so why not keep it straight technical?

    Wednesday, September 12, 2007 6:03 PM
  • The lack of multi-threaded code has no bearing on whether data needs to be copied or not.  Even in MT code copying data around is not common (hence why there are MT issues).  Copying data is generally used for security and/or caching purposes.  But all that is really beyond the discussion at hand anyway.  And yes I would (and have) run high demand applications under Windows.  I use to write compilers for control software that ran factories ranging from one the of the largest oil companies in the US to one of the major automotive manufacturers.  In all cases reliability, performance and safety were top priorties.  The runtime was heavily MTed.  Windows is definitely capable of doing such tasks provided the code is written properly.  Again, though, we're digressing from the topic at hand.

     

    I still think we are in disagreement over the true definition of TS.  I think you are a victim of "equality by association" whereas you are deriving the definition of TS from Control.Invoke and that is not correct.  They are related but they are not the same.  The documentation is correct, the members mentioned are TS. They are TS irrelevant of the fact that they they send messages.  They are TS because the implementers made them so.  Pretty much any static member of any class is designed to be TS, just for comparison. 

     

    Let's break away from C.I for a minute and just look at a regular old class.  Say a class called Company that is designed to be TS.  It exposes a collection of Employee objects representing the employees in the company.  Let's make it TS.

    A TS member is a member that can be safely called on (ANY) number of threads.  This is all the definition of TS guarantees.  No messages, no controls, no nothing (double negative?).  So, to make our Company class TS, we need to wrap any modifications to the employee collection in some locking primitive.  If we don't then it is possible that 2 different threads could try to add an employee at the same time and corrupt the collection.  Do you agree with this assessment?  Ok that completes TS.  Nothing more is needed.  We can call our class on any number of threads and it'll work.

     

    Now back to C.I.  Windows requires that a control be accessed only on the thread that created it.  This is quite a bit different than TS because with TS we don't care about what thread actually calls a method we just care about them all getting along.  With controls we care about the thread that is used.  In the case of Windows there is only 1 thread that can do that.  Since there is only 1 thread that can do it it is, by definition, TS once there.  C.I still needs to be TS since it can be called on multiple threads but all the other members of Control need not be since Windows requires them to be called on a single (specifically 1 exact) thread.  That is why the message stuff is in the implementation, to ensure that the method runs on a specific thread. 

     

    This really all falls back to old, old Windows where the message queue is not TS.  If MS made the queue TS then we could lift the limitation from UI controls.  This, however, would be a breaking change and would mangle most apps so I doubt it'll ever change.

     

    So I think what we're trying to tell you is that TS is defined as being able to safely call a method on multiple threads. C.I is thread safe but the entire reason for its existence and what it does is not for TS purposes but to ensure that interaction with a control occurs only on 1 specific thread.

     

    As an aside, and contrary to popular belief, you can deadlock a single threaded application.  So even with C.I it is possible to deadlock one's self.  This can happen rather easily when you consider that some functions don't support reentrancy.  One of the peculiar things with Windows is that sending a message to a window in the same thread will skip the message queue under certain circumstances and invoke the wndproc directly.  In this case reentrancy/deadlock can occur if a function isn't designed for it.  Doesn't happen often but it can happen. 

     

    Hope this helps and good luck on your write up on all this stuff.  It is definitely confusing.

     

    Michael Taylor - 9/12/07

    http://p3net.mvps.org

    Wednesday, September 12, 2007 6:28 PM
  • There's many method documented as "thread-safe" that aren't.

     

    Invoke, InvokeRequired, BeginInvoke, and CreateGraphics are the only Control methods that can be called from any thread.  I don't think that's "thread safe".  If you think you don't have to deal with thread-safety using them because the documentation mentions "thread safe", despite what other's say (regardless of what I say), then good luck with that.

     

    I still see no reason to confuse the issue with comments about cooperative/preemptive multi-threading.  We're talking about Windows.  There is a certain amount of cooperative multi-threading and preemptive multi-threading in today's Windows. In past versions of Windows you could consider it "cooperative"; but that didn't mean a thread couldn't be interrupted.  Message-based architectures are a perfect example of being cooperative where you can choose to hog the CPU or post a message to the end of the queue and relinquish the CPU (e.g. BeginInvoke).  With a Windows UI you must be cooperative; you simply can't hog the CPU in the processing of a message (or event) and expect your UI to function.  So, whether you're dealing with cooperative or preemptive multi-threading, it's moot to being thread-safe.

     

    Wednesday, September 12, 2007 6:36 PM
  • TaylorMichaelL

     

    Now I think that I understand what you mean . The thread safery arounds control.Invoke and control.BeginInvoke refers to the fact that they of course have a locking mechanism, which prevents half messages and half delegates to be posted to the message queue and callback queue in case more threads want to post messages. It has nothing to do with inter thread communication.

     

    Both.

     

    Only one thing bothers me now - the one who started this part of the discussion. Michael, Microsoft and me calls the four methods thread safe. Peter says they are not. There must be a technical answer to that question - are they TS, yes or no? If no, why not. They do have the locking mechanism according to Michaels definition (as I understand it) so that they may be called from many threads at the same time.

    Wednesday, September 12, 2007 7:55 PM
  • Something capable of being called from multiple threads at the same time is considered "re-entrant", not "thread-safe".  That's a common confusion.  Re-entrancy is part of being thread-safe, but only part.  Being "Thread-safe" is contextual.  Something can only be "thread-safe" in specific contexts.  Eventually all methods that do anything with any non-local data encounter a context where they are not thread safe.  I don't consider Control.Invoke to be thread-safe because it blocks one thread and transfers control to another without any accessible synchronization to avoid dead-locks.  I don't consider Control.BeginInvoke thread-safe because you can call it from the UI thread and you don't know when its delegate will be executed; by the time the delegate is executed you could have a race-condition.  If you have to add synchronization code simply because you're calling a specific method then it's not thread-safe in my opinion.  Being thread-safe means you need to perform no synchronization when you use that method.  That's not the case with either  Control.BeginInvoke or Control.Invoke.

     

    Control.CreateGraphics is not thread-safe because it makes an unguarded call to Control.Handle which has code similar to:

    Code Snippet

    if(this.IsHandleCreated)

    {

       this.CreateHandle();

    }

     

     

     

    ... which has a race-condition between IsHandleCreated and CreateHandle: One thread could be between IsHandleCreated and CreateHandle while another thread is about to call CreateHandle.

     

    Control.Invoke, Control.BeginInvoke, and Control.CreateGraphics are re-entrant and can be called from any thread (not just the GUI thread); which might be what they meant in the documentation--but I have no idea what they were thinking or what they intended when they wrote that.  But, based upon experience, there are times when you must add synchronization code simply because you're using Control.Invoke or Control.BeginInvoke.

    Wednesday, September 12, 2007 8:25 PM
  • Hmno, that's not it.  The docs for CreateGraphics specifically forbid this scenario.  Quoting:

    "...and CreateGraphics if the handle for the control has already been created. Calling CreateGraphics before the control's handle has been created on a background thread can cause illegal cross thread calls."

    That's putting it a bit too mild, calling CreateHandle() on any other thread than the UI thread is very bad news and quite specifically forbidden by the SDK in the case of a non-toplevel window.

    InvokeRequired, BeginInvoke(), Invoke() and EndInvoke() being safe to call from any thread is rather a necessity, they would be quite worthless otherwise.  Whether "safe to call" equates to "thread safe" could be argued.  Especially in the case of Invoke(), the outcome of the call is well documented (at this forum) to not always have the desired outcome.  Deadlocks are common.  But that's a consequence of threading in general, not of the call.  Personally, I agree with MSFT's definition.  Nothing bad happens when two threads call Invoke() concurrently, no locking is required.

    BTW: Taylor was quite close with his analysis of how Begin/Invoke() is implemented.  The only detail is that the message queue plays a very modest role, it is almost always out-of-sync with the delegate queue.  That's why you can't overflow it.
    Thursday, September 13, 2007 1:47 AM
  • Peter Ritchie

     

    "Something capable of being called from multiple threads at the same time is considered "re-entrant", not "thread-safe".  That's a common confusion."

     

    I don't think that Michael and I confuse things. Any method is re-entrant when called from multiple threads - simply because each thread has its own stack and data area. No locking is necessary for that. It only gets tricky when each call is going to work on the same set of data like the message queue.

     

    To all

     

    I still think that the most resonable definition of thread safety, and the one, which is closest to the help file, is to say that:

     

    "A method is thread safe if it is able to safely use data area of another thread."

     

    This is exactly what happens with the four methods. To queue a message they need to use the data area of another thread (the UI thread), which of course involves a lot of locking. Besides, there is no reason to include the word "thread" if it has nothing to do with threads.

     

    nobugz

     

    How does the MSFT definition sounds?

     

    "... The only detail is that the message queue plays a very modest role, it is almost always out-of-sync with the delegate queue.  That's why you can't overflow it."

     

    Could you please explain that in further details? With my present knowledge (which may not be true), a WM__ThreadCallbackMessage is posted to the message queue for each control.Invoke or control.BeginInvoke. At the same time, the delegate is posted to the delegate/callback stack (or whatever it is called). When WndProc receives the WM__ThreadCallbackMessage, it calls InvokeMarshaledCallbacks, which emties the entier delegate/callback queue - not just the top-most delegate.

     

    How can the message queue and the delegate/callback queue get out of sync except of course that some delegates may be executed earlier than expected if somebody calls control.Invoke on the UI thread (calls InvokeMarshaledCallbacks) or there already is a WM_ThreadCallbackMessage on the message queue?

     

    Why can't you overflow the message queue? I have read somewhere that it has a maximum of 10,000 messages.

     

    Why does Windows use this double queue system (message queue plus delegate/callback queue)? It seems to me that separating the data (delegate) from the message could lead to a lot of unexpected behaviour - especially because the entier queue is emtied for each message! Why not just transfer the delegate in e.g. lParam of the message structure?

     

    EDIT Sep. 14th

     

    Is it so clever that all delegates are just linked together by means of the _PREV field to form the delegate/callback queue, and that a WM_ThreadCallbackMessage is only posted when the first delegate is added? This could save a lot of overhead and prevent overload of the message queue (only one message no matter how many delegates there are in the queue).

     

    Just a thought: Has this delegate queue anything to do with the user mode APC queue of the thread. Maybe it could be the same queue?

     

    Thursday, September 13, 2007 6:35 AM
  •  Carsten Kanstrup wrote:

     Any method is re-entrant when called from multiple threads - simply because each thread has its own stack and data area. 

    No, re-entrant is when a method can safely be executed by multiple threads at the same time (or can be recursed; but I've been focusing on the MT aspect of it).

    Thursday, September 13, 2007 1:29 PM
  • I don't know what you mean with "can safely", but re-entrant just means that a method can be called from more different places without mutual data destruction. It has nothing to do with working on the same set of data. Therefore, any method is automatically re-entrant when called from different threads. The Wiki link does not take multithreading into consideration.

     

    If a method shall be re-entrant when called on a single thread (the Wiki link), all data including intermediate/temporary values must be stored on the stack (no static data as the Wiki link says). In many programming languages like PLM, you can force that with the keyword "Reentrant" or something like that. This is useful if a method shall be able to call itself. In fact, the process control system I mentioned is basically one big re-entrant subroutine of this type, which keeps calling itself for each logic level.

     

    Besides, the Wiki link also gives a definition of Thread safety:

     

    "A piece of code is thread-safe if it functions correctly during simultaneous execution by multiple threads. In particular, it must satisfy the need for multiple threads to access the same shared data (in our case the message queue), and the need for a shared piece of data to be accessed by only one thread at any given time."

     

    This is exactly what I have also said - just in other words. Shared data must of course be located somewhere and because everything is running on a thread, the upper definition is the same as to say that:

     

    "A method is thread safe if it is able to safely use data area (shared data) of another thread."

     

    Friday, September 14, 2007 5:58 AM
  • Found the answer to this thread myself (see the chapter "Control.Invoke/BeginInvoke" on this link: http://www.innovatic.dk/knowledg/SerialCOM/SerialCOM.htm ).

     

    All comments, suggestions, corrections, additions etc. are very welcome!

    Monday, September 17, 2007 4:29 PM