Prev: MFC Feature Pack App with full frame docking
Next: New control styles with imported VC6 app in VS2008
From: Hector Santos on 2 May 2010 20:03 Joseph M. Newcomer wrote: > COM inteface pointers have their own clearly-stated rules for thread behavior. > > It is not clear that other pointers have similar limitations. > joe > But nice to know what the OP was actually doing. Sounds to me that he was walking/access the object DOM. If this is the case, he has access it to with the IWebBrowser2 instance member in his window, by passing it to a thread: AfxBeginThread(WebDomThreadProc,&m_web); The docs for DocumentComplete() says pDisp is the interface for the Window, so it might be the same as m_web->get_Document(). Maybe not. Either way, however he also stated the page can change, so I suspect that the collections will be altered as he traverses the DOM. I just explored this, and it indeed worked and by adding a small delay in the collection traversal, I changed the page and indeed the last collection no longer valid. If he needs to keep the same collection, then he needs to place the web browser from changing pages. Either that or he makes a copy of the collection before starting the thread and the thread works with the copied DOM collection. Is there a Clone Dom method? -- HLS
From: Goran on 3 May 2010 03:12 On May 3, 12:25 am, Joseph M. Newcomer <newco...(a)flounder.com> wrote: > See below... > > > > On Sun, 2 May 2010 11:52:02 -0700 (PDT), Goran <goran.pu...(a)gmail.com> wrote: > >On May 2, 6:05 pm, Joseph M. Newcomer <newco...(a)flounder.com> wrote: > >> It depends on the tradeoff. The OP suggested that the major delay was in obtaining the > >> remote data, not in processing it (e.g., putting the text in the HTML control). So using > >> a secondary thread to obtain the data allows the main thread to do other things, but it > >> doesn't change the total delay in presenting the information (in the absolute sense, it > >> increases the latency, because of the interthread communication, but that overhead is > >> generally so small as to be unnoticeable compared to the other costs) > > >> But the UI thread is not blocked during the download-data phase, and consequently, in > >> spite of the fact that the desired data is not being displayed, the thread can handle > >> other user interactions. > > >> This leads to potentially nasty race conditions, e.g., the window is up, the user starts a > >> download, and closes the window before the download completes. An attempt to PostMessage > >> to the CWnd * will probably take an access fault, and a PostMessage to the HWND will be > >> discarded, thus resulting, most likely, in a memory leak (and ::IsWindow is insufficient > >> to handle this). So a window that has a pending PostMessage must not actually allow > >> itself to be destroyed (the most common solution I use is to hide the window, thus > >> providing the user with the illusion that the window has been "closed", but the window > >> itself is still live; when the pending transaction completes, it merely discards the data > >> and completes the window destruction...this takes a fair amount of careful coding). > > >Some simple shared_ptr/weak_ptr juggling work wonders for this. Here's > >an example: > > >* Window (UI thread) creates a heap object that needs to passed to the > >main thread and puts it in a shared_ptr. Window keeps that shared_ptr. > >Object contains a copy of window's HWND (NOT a reference/ptr to a > >CWnd). > > **** > It is a common myth that you can't pass a CWnd* across thread boundaries. This is an > oversimplication of a much more subtle issue: you can't access the HWND of a CWnd *, and > you must ensure the lifetime of the CWnd * exceeds the time the thread can access it. Yes, I was thinking about errors that arise due to not respecting that. It's not really lifetime of the CWnd*, it's more the lifetime of HWND that poses problems. I guess that one can say: the code in the thread must either have no expectations WRT CWnd/HWND lifetime, and deal with the consequences (my approach with shared/weak ptr), either what you say. I found that, when I need to deal with views/frames, "no expectations" approach is easier, because user actions, MFC and system control lifetime of CWnd/HWNDs. For e.g. progress dialogs etc, your approach is easier. > > Passing an HWND is more conservative, but leads to a whole NEW set of problems, which are > actually more dangerous to the uninitiated. What the problems are (honest question)? I know of two: one still can't use SendMessage (that has thread affinity), and HWND might get reused. Is there more? > ****>* thread stores a weak_ptr to the object. It turns it into a > >shared_ptr ONLY when it actually needs to use the object (it must > >watch out for NULL). > >* when window gets destroyed, it resets shared_ptr. > >* thread uses PostMessage with object's HWND to inform the window that > >data is ready or whatever. Now... If PostMessage fails because window > >has been closed in the meantime, no biggie: object goes away when last > >shared_ptr goes away. (Remember: shared_ptr is either held > >"permanently" by the window, either "temporarily" by he thread). If > >message is delivered, window can use data that is in the object > >(remember, window has a shared_ptr to it). Thread might or might not > >go away, that's without influence. > > **** > Yes. And this tends to still require that the same heap be visible; as far as I know the > shared_ptr doesn't carry its allocator/deallocator information with it. But I could be > wrong; I haven't looked at this in a couple years > **** Yes, of course, same heap is required. As you stated before, that's most often the case, so it's not a big deal. Goran.
From: nexo on 3 May 2010 12:45 On May 3, 12:12 pm, Goran <goran.pu...(a)gmail.com> wrote: > On May 3, 12:25 am, Joseph M. Newcomer <newco...(a)flounder.com> wrote: > > > > > See below... > > > On Sun, 2 May 2010 11:52:02 -0700 (PDT), Goran <goran.pu...(a)gmail.com> wrote: > > >On May 2, 6:05 pm, Joseph M. Newcomer <newco...(a)flounder.com> wrote: > > >> It depends on the tradeoff. The OP suggested that the major delay was in obtaining the > > >> remote data, not in processing it (e.g., putting the text in the HTML control). So using > > >> a secondary thread to obtain the data allows the main thread to do other things, but it > > >> doesn't change the total delay in presenting the information (in the absolute sense, it > > >> increases the latency, because of the interthread communication, but that overhead is > > >> generally so small as to be unnoticeable compared to the other costs) > > > >> But the UI thread is not blocked during the download-data phase, and consequently, in > > >> spite of the fact that the desired data is not being displayed, the thread can handle > > >> other user interactions. > > > >> This leads to potentially nasty race conditions, e.g., the window is up, the user starts a > > >> download, and closes the window before the download completes. An attempt to PostMessage > > >> to the CWnd * will probably take an access fault, and a PostMessage to the HWND will be > > >> discarded, thus resulting, most likely, in a memory leak (and ::IsWindow is insufficient > > >> to handle this). So a window that has a pending PostMessage must not actually allow > > >> itself to be destroyed (the most common solution I use is to hide the window, thus > > >> providing the user with the illusion that the window has been "closed", but the window > > >> itself is still live; when the pending transaction completes, it merely discards the data > > >> and completes the window destruction...this takes a fair amount of careful coding). > > > >Some simple shared_ptr/weak_ptr juggling work wonders for this. Here's > > >an example: > > > >* Window (UI thread) creates a heap object that needs to passed to the > > >main thread and puts it in a shared_ptr. Window keeps that shared_ptr. > > >Object contains a copy of window's HWND (NOT a reference/ptr to a > > >CWnd). > > > **** > > It is a common myth that you can't pass a CWnd* across thread boundaries. This is an > > oversimplication of a much more subtle issue: you can't access the HWND of a CWnd *, and > > you must ensure the lifetime of the CWnd * exceeds the time the thread can access it. > > Yes, I was thinking about errors that arise due to not respecting > that. It's not really lifetime of the CWnd*, it's more the lifetime of > HWND that poses problems. I guess that one can say: the code in the > thread must either have no expectations WRT CWnd/HWND lifetime, and > deal with the consequences (my approach with shared/weak ptr), either > what you say. I found that, when I need to deal with views/frames, "no > expectations" approach is easier, because user actions, MFC and system > control lifetime of CWnd/HWNDs. For e.g. progress dialogs etc, your > approach is easier. > > > > > Passing an HWND is more conservative, but leads to a whole NEW set of problems, which are > > actually more dangerous to the uninitiated. > > What the problems are (honest question)? I know of two: one still > can't use SendMessage (that has thread affinity), and HWND might get > reused. Is there more? > > > > > ****>* thread stores a weak_ptr to the object. It turns it into a > > >shared_ptr ONLY when it actually needs to use the object (it must > > >watch out for NULL). > > >* when window gets destroyed, it resets shared_ptr. > > >* thread uses PostMessage with object's HWND to inform the window that > > >data is ready or whatever. Now... If PostMessage fails because window > > >has been closed in the meantime, no biggie: object goes away when last > > >shared_ptr goes away. (Remember: shared_ptr is either held > > >"permanently" by the window, either "temporarily" by he thread). If > > >message is delivered, window can use data that is in the object > > >(remember, window has a shared_ptr to it). Thread might or might not > > >go away, that's without influence. > > > **** > > Yes. And this tends to still require that the same heap be visible; as far as I know the > > shared_ptr doesn't carry its allocator/deallocator information with it. But I could be > > wrong; I haven't looked at this in a couple years > > **** > > Yes, of course, same heap is required. As you stated before, that's > most often the case, so it's not a big deal. > > Goran. Well , This has nothing to do with COM threading rules or models (STA/ MTA are the used words..) And I tried to pass the pointer to another thread started from OnDocumentComplete and it worked fine without any problem at all. And while doing this I remembered doing the same in a shell extension that i wrote once( in ATL ) And I know i posted here in MFC community .. but i am creating this BHO in C# !! and i was able to pass the object pDisp easily and safely to the other thread . PS:- dont know what is happening to microsoft.public.vc.mfc on my side, i posted several messages that are not visible here and nor does are your messages visible on the microsoft's site(to me?) ... so finally got in here at google. Thanks to everyone :) Thanks.
From: Joseph M. Newcomer on 3 May 2010 20:25 See below... On Mon, 3 May 2010 00:12:35 -0700 (PDT), Goran <goran.pusic(a)gmail.com> wrote: >On May 3, 12:25�am, Joseph M. Newcomer <newco...(a)flounder.com> wrote: >> See below... >> >> >> >> On Sun, 2 May 2010 11:52:02 -0700 (PDT), Goran <goran.pu...(a)gmail.com> wrote: >> >On May 2, 6:05�pm, Joseph M. Newcomer <newco...(a)flounder.com> wrote: >> >> It depends on the tradeoff. �The OP suggested that the major delay was in obtaining the >> >> remote data, not in processing it (e.g., putting the text in the HTML control). �So using >> >> a secondary thread to obtain the data allows the main thread to do other things, but it >> >> doesn't change the total delay in presenting the information (in the absolute sense, it >> >> increases the latency, because of the interthread communication, but that overhead is >> >> generally so small as to be unnoticeable compared to the other costs) >> >> >> But the UI thread is not blocked during the download-data phase, and consequently, in >> >> spite of the fact that the desired data is not being displayed, the thread can handle >> >> other user interactions. >> >> >> This leads to potentially nasty race conditions, e.g., the window is up, the user starts a >> >> download, and closes the window before the download completes. �An attempt to PostMessage >> >> to the CWnd * will probably take an access fault, and a PostMessage to the HWND will be >> >> discarded, thus resulting, most likely, in a memory leak (and ::IsWindow is insufficient >> >> to handle this). �So a window that has a pending PostMessage must not actually allow >> >> itself to be destroyed (the most common solution I use is to hide the window, thus >> >> providing the user with the illusion that the window has been "closed", but the window >> >> itself is still live; when the pending transaction completes, it merely discards the data >> >> and completes the window destruction...this takes a fair amount of careful coding). >> >> >Some simple shared_ptr/weak_ptr juggling work wonders for this. Here's >> >an example: >> >> >* Window (UI thread) creates a heap object that needs to passed to the >> >main thread and puts it in a shared_ptr. Window keeps that shared_ptr. >> >Object contains a copy of window's HWND (NOT a reference/ptr to a >> >CWnd). >> >> **** >> It is a common myth that you can't pass a CWnd* across thread boundaries. �This is an >> oversimplication of a much more subtle issue: you can't access the HWND of a CWnd *, and >> you must ensure the lifetime of the CWnd * exceeds the time the thread can access it. > >Yes, I was thinking about errors that arise due to not respecting >that. It's not really lifetime of the CWnd*, it's more the lifetime of >HWND that poses problems. I guess that one can say: the code in the >thread must either have no expectations WRT CWnd/HWND lifetime, and >deal with the consequences (my approach with shared/weak ptr), either >what you say. I found that, when I need to deal with views/frames, "no >expectations" approach is easier, because user actions, MFC and system >control lifetime of CWnd/HWNDs. For e.g. progress dialogs etc, your >approach is easier. **** If you pass a CWnd* across a thread boundary, you must not allow the CWnd object to be deleted until the thread is done using it. This is critical, because what will happen is the thread could end up accessing free storage which might actually be reallocated for some other object. The HWND in this case is less critical, because if you manage to destroy the HWND without deleting the CWnd *, any methods you invoke on the HWND (e.g., PostMessage) will assert-fail. In a release version, the data posted will result, typically, in a storage leak, because the receiving window isn't there. Serious, but not really fatal (until you run out of heap!) But if you pass the HWND, the common failure mode is class CMyView : public CView { protected: static UINT handler(LPVOID p); .. } AfxBeginThread(handler, this); /*static */ UINT CMyView::handler(LPVOID p) { CMyView * view = (CMyView *)p; // this works correctly } But I've seen people who are told "You must pass the HWND because I once read a document that said passing CWnd * is a Bad Idea" do the following AfxBeginThread(handler, (LPVOID)m_hWnd); /* static */ UINT CMyView::handler(LPVOID p) { CMyVIew * view = (CMyView*)CWnd::FromHandle((HWND)p); } OK, here's a Quickie Quiz: What's wrong with the above code? And why is it a Really Really Bad Idea? This is one of those subtleties of where MFC Meets System Programming that I talk about in my course. **** > >> >> Passing an HWND is more conservative, but leads to a whole NEW set of problems, which are >> actually more dangerous to the uninitiated. > >What the problems are (honest question)? I know of two: one still >can't use SendMessage (that has thread affinity), and HWND might get >reused. Is there more? **** Yes. And if you don't know the answer to the above Quickie Quiz, I'll give the solution in a subsequent post. But that failure (or more than one failure, as it turns out) is a key problem with passing an HWND across. And while both of your problems are issues (and it isn't a "thread affinity" issue with HWND as such, but the complex synchronization required to get the SendMessage handled in the context of the receiving thread, and HWND value reuse, although a real issue, is a rare problem; the two deeply serious problems are far worse than either of the above), one of the key issues (and yet another issue) is the lifetime of the CWnd* and the fact that you MUST NOT allow it to be deleted while the thread thinks it has a valid pointer! This is a pointer-sharing issue. And using shared_ptr solves only the pointer problem, not the HWND problem, so essentially the rule is: he who owns it must delete it, and shared_ptr allows the (to me, incorrect) passing of ownership to the thread, which shouldn't EVER own the CWnd* object (only the creating thread should own it!) So from my viewpoint, shared_ptr is NOT the correct solution here because it causes a failure in ownership rights, incorrectly transferring the ownership. The problem is much more complex, and the solutions are also complex, but necessary. Only the UI thread that creates the object is permitted to destroy it, and it must not destroy it if the object is in use in any thread. Therefore, a positive protocol is required wherein every thread who might share access to the CWnd* will inform the owner when they have finished, and only the owner is allowed to actually delete the object. **** > >> ****>* thread stores a weak_ptr to the object. It turns it into a >> >shared_ptr ONLY when it actually needs to use the object (it must >> >watch out for NULL). >> >* when window gets destroyed, it resets shared_ptr. >> >* thread uses PostMessage with object's HWND to inform the window that >> >data is ready or whatever. Now... If PostMessage fails because window >> >has been closed in the meantime, no biggie: object goes away when last >> >shared_ptr goes away. (Remember: shared_ptr is either held >> >"permanently" by the window, either "temporarily" by he thread). If >> >message is delivered, window can use data that is in the object >> >(remember, window has a shared_ptr to it). Thread might or might not >> >go away, that's without influence. >> >> **** >> Yes. �And this tends to still require that the same heap be visible; as far as I know the >> shared_ptr doesn't carry its allocator/deallocator information with it. �But I could be >> wrong; I haven't looked at this in a couple years >> **** > >Yes, of course, same heap is required. As you stated before, that's >most often the case, so it's not a big deal. **** Note that a lot of STL objects have an allocator reference that goes with them, and if so, this would be a way to deal with the multiple-heap problem. joe **** > >Goran. Joseph M. Newcomer [MVP] email: newcomer(a)flounder.com Web: http://www.flounder.com MVP Tips: http://www.flounder.com/mvp_tips.htm
From: Hector Santos on 3 May 2010 21:59 Joseph M. Newcomer wrote: > **** > If you pass a CWnd* across a thread boundary, you must not allow the CWnd object to be > deleted until the thread is done using it. This is critical, because what will happen is > the thread could end up accessing free storage which might actually be reallocated for > some other object. Isn't that general true as a rule of thumb of passing a pointer to a thread that could be become invalid outside the thread? What is CWnd* or HWND for the matter special in this regard? Its a simple owner vs non-owner or z-order concept : if you didn't create it, you shouldn't delete it unless it was specifically creates for the thread to delete. Its also relates to cross process or dll boundaries, it is not recommended to create in one boundary and delete in another. > But if you pass the HWND, the common failure mode is > > > class CMyView : public CView { > protected: > static UINT handler(LPVOID p); > .. > } > > AfxBeginThread(handler, this); > > /*static */ UINT CMyView::handler(LPVOID p) > { > CMyView * view = (CMyView *)p; > > // this works correctly > } > > But I've seen people who are told "You must pass the HWND because I once read a document > that said passing CWnd * is a Bad Idea" do the following > > AfxBeginThread(handler, (LPVOID)m_hWnd); > > /* static */ UINT CMyView::handler(LPVOID p) > { > CMyVIew * view = (CMyView*)CWnd::FromHandle((HWND)p); > > } > > OK, here's a Quickie Quiz: What's wrong with the above code? And why is it a Really > Really Bad Idea? Thats easy, two things: 1) you are asking for something that is managed in some global table (a map in this case) 2) if its not found, a new one is created, and the thread will not know that fact in order to delete it. This is old material, but MFC historically used TLS to maintain its recording of Windows and handles per thread and across boundaries (i.e. a MFC DLL) that was problematic, especially for use MFC based objects, i.e, CString So in MFC 4.0, it uses a set of template classes to wrap process level data. For example, this is a little known trick and it works extremely work: struct CMyProcessData : public CNoTrackObject; { CString s; } struct CMyThreadData : public CNoTrackObject; { CString s; } CProcessLocal<CMyProcessData> MyProcessData; CThreadLocal<CMyThreadData> MyThreadData; void SetProcessString(LPCTSTR lpsz) { MyProcssData->s = lpsz; } void SetThreadString(LPCTSTR lpsz) { MyThreadData->s = lpsz; } CString GetProcessString() { return MyProcssData->s; } CString GetThreadString() { return MyThreadData->s; } When you start a thread, it will automatically get its own instance of CMyThreadData. I use this for a few dlls that creates threads and it allows me to maintain MFC safe process and thread state information when using MFC objects. This is documented in a MSDN Tech Note: TN058: MFC Module State Implementation. http://msdn.microsoft.com/en-us/library/ft1t4bbc(VS.80).aspx > This is one of those subtleties of where MFC Meets System Programming that I talk about in > my course. (RAISING HAND!) Isn't it better to as a general practice of pointer usage to understand how memory is used rather than making everything else fit under MFC? Understanding MFC is important, but following basic concepts of z-order programming inherently solves 99.99% (if not 100%) of the design questions to a point it doesn't become a question at all. One of the problems (and goran we had this discussion before) with using higher layer tools, is that it increases the understanding requirements that can be become very subtle and quite often requires a DEEP understanding of the higher layer wrappings itself missing critical points of the basic underlining framework. Example, this is something I'm currently working on with touches based with this idea of z-order and pointer ownerships and usage; Asynchronous RPC using a I/O completion port notification type. There is no MSDN example only a Event based notification type. Googling shows no results and the closest one is so complex with an extremely rich library that is dependent on so many other things that not even downloading the source and reviewing the code was difficult to see what is the basic framework to get it done. This required rolling up the sleeve and trying various ideas using the "raw" IOCP and ASYNC RPC API functions, re-reading the MSDN docs as you see results until you finally understand whats going on and get the layout right. I just now at this moment reached that point and took a timeout to read the MFC forum. The irony is now I will be wrapping it with my own classes to make it easy. I can only do that one you understand the basics. -- HLS
First
|
Prev
|
Next
|
Last
Pages: 1 2 3 4 5 Prev: MFC Feature Pack App with full frame docking Next: New control styles with imported VC6 app in VS2008 |