Thread: WinAPI & threading

  1. #1
    Registered Abuser
    Join Date
    Jun 2006
    Location
    Toronto
    Posts
    591

    WinAPI & threading

    I have created a function that does some processing in a thread so as not to block the window loop (otherwise Windows flags it as "not responding"). It is fairly straightforward code, since all I did was cut the in-line code out of the case, place it into a __stdcall function and reassigned some variables as passed parameters. I know the code is okay, because it works fine if I call the threadfunc explicitly, but whenever I call it through _beginthreadex it stops responding (the thread is launched fine and runs for a bit but crashes afterwards, using breakpoints throughout the code did not limit the scope of the error either). So what is causing the function to not respond when called in a threading context (i.e. what about it is not thread-safe)?

    I've checked all parameters passed to the threadfunc from the caller (and its caller) and made sure that any local vars passed around are not sent off the stack before the thread can finish executing. The only other source of error I can guess would be a WinAPI call or C call from within the thread that is not threadsafe (though am I am positive I am using no un-thread-safe C calls).

    To be specific, the parameter passed through the "void *pArgs" parameter of the thread is a pointer to a structure I defined consisting of 3 members, each holds a handle to window/control. I use these members (handles) with SendMessage() and GetDlgItem() from within the thread to send messages to controls. Are these winAPIs thread-safe? I know winAPI checks that some operations on handles can only be done if the calling process is owned by the Window's process (which I'm pretty sure is my case here right?) What else could potentially be causing this problem?
    Thanks for any help.

  2. #2
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,660
    > is a pointer to a structure I defined consisting of 3 members, each holds a handle to window/control.
    Where was that structure allocated, in order for you to pass a pointer to it?

    Also check your libraries (like msvcrt), as they come with (and without) multi-thread support.

    Though if this is a worker thread, why does it need to bother with any UI interaction?
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  3. #3
    Registered Abuser
    Join Date
    Jun 2006
    Location
    Toronto
    Posts
    591
    The thread will concurrently update certain controls as information is processed (i.e. I do some calculations on an item, then add the item to a ListView, so the ListView handle is needed. I guess optimally, it would be better to do all calc's first, then pass back a list of items, then add all items at once...).

    The structure was allocated in the caller's caller, here's what the execution trace looks like
    (I've highlighted in red all references to the "args" structure along the way):
    Code:
    DialogFunc1(hwnd, msg, lparam, wparam)
    {
       case BN_CLICKED:
          ...
          mystruct args = {handle1, handle2, handle3};
          DialogBoxParam(..., DialogFunc2, &args);
          //mystruct will be on stack at least until EndDialog()
          ...
    }
    
    DialogFunc2(..., lparam)
    {
       ...
       WM_INITDIALOG:
          //lparam = &args from func1
          //pass along &args so we give this window a chance to initialize
          PostMessage(hwnd, MY_MSG,0, lparam);
          break;
    
       MY_MSG:
         hThread = (HANDLE)_beginthreadex(null, 0, Thread, lparam, 0, null);
         WaitForSingleObject(hThread, INFINITE);
         CloseHandle(hThread);
         EndDialog(hwnd, null);
         //args still on stack from Func1, which is waiting for Func2,
         //which is waiting for Thread
         ...
         // yes, even after all that work, I *still* end up blocking the window func :D
         // (I'll change it later if I ever get the thread working)
    }
    
    __stdcall Thread(void *pArgs)
    {
       mystruct *args = (mystruct *)pArgs;
       HWND handle1 = args->handle1;
       HWND handle2 = args->handle2;
       ...
       loop:
          Do some calculations
          UpdateGUIInfo(handle1, handle2, handle3);
       ...
       return 0;
    }
    Also, I have MingW's libmsvcrt and also link shlwapi, ole, and comctrl32, but use only limited functions from shlwapi (some string path handling) and comctrl (listview messages) in the thread.
    Last edited by @nthony; 10-13-2007 at 07:20 PM.

  4. #4
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    >> hThread = (HANDLE)_beginthreadex(null, 0, Thread, lparam, 0, null);
    >> WaitForSingleObject(hThread, INFINITE);
    Yeah, that's basically the same as just calling Thread() directly...so I don't see anything wrong in what you've posted so far.

    >> Also, I have MingW's libmsvcrt
    That's the multi-threaded dll version - so you're good there.

    gg

  5. #5
    Hurry Slowly vart's Avatar
    Join Date
    Oct 2006
    Location
    Rishon LeZion, Israel
    Posts
    6,788
    Code:
    mystruct args = {handle1, handle2, handle3};
          DialogBoxParam(..., DialogFunc2, &args);
    args is allocate on stack is lives only till the BN_CLICKED event handler is finished

    when MY_MSG arrives the above portion of the stack contains some other random data

    So you should rethink your design, to store the data a little longer
    All problems in computer science can be solved by another level of indirection,
    except for the problem of too many layers of indirection.
    – David J. Wheeler

  6. #6
    erstwhile
    Join Date
    Jan 2002
    Posts
    2,227
    >>args is allocate on stack is lives only till the BN_CLICKED event handler is finished<<

    Actually it should be okay because it's followed by a DialogBoxParam call which doesn't return until the modal dialog it creates is dismissed with EndDialog.
    CProgramming FAQ
    Caution: this person may be a carrier of the misinformation virus.

  7. #7
    Registered Abuser
    Join Date
    Jun 2006
    Location
    Toronto
    Posts
    591
    Quote Originally Posted by Codeplug View Post
    >> hThread = (HANDLE)_beginthreadex(null, 0, Thread, lparam, 0, null);
    >> WaitForSingleObject(hThread, INFINITE);
    Yeah, that's basically the same as just calling Thread() directly...so I don't see anything wrong in what you've posted so far.
    That's what's really eating me up about this... aside from _beginthreadex doing the calling, Thread() is called with exactly the same args and yet works fine when called explicitly.
    The only other thing I can think of to explain it is that maybe its failing in both cases, just that in the case where I call it explicitly, I just happen to get lucky (err.. unlucky) with undefined behaviour. But then again, I've tested it numerous times now, and it would seem highly unlikely that by now, any undefined behaviour wouldn't cause at least some noticeable problem yet.

  8. #8
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Have you tried to check if your handles are the same in both places?

    Just to make sure that it's not a stack problem, try allocating and then freeing the "mystruct" data instead.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  9. #9
    Registered Abuser
    Join Date
    Jun 2006
    Location
    Toronto
    Posts
    591
    Good idea, that ruled out the stack problem entirely then: I malloc'd the struct this time (not even bothering to free it), and it still hung, so it definately must be a problem elsewhere.

    The handles always remain the same (always passed as void *pArgs). They work as expected in the explicit call, and I wouldn't expect them to change as a result of a call to _beginthreadex.

  10. #10
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Can you debug the thread itself - where does it get to, what call is it hanging in, what are the arguments to the call, etc, etc. [Basicly: Basic debug techniques].

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  11. #11
    Registered Abuser
    Join Date
    Jun 2006
    Location
    Toronto
    Posts
    591
    I did use breakpoints, traced execution, etc., but they were unhelpful.
    I.E.: place breakpoints at top and bottom of suspect code, narrow down until top break is reached but not bottom break. I would eventually narrow it down to one line, then thinking I nailed it, comment out the line, only to have another completely unrelated line in some other section of code break.

    This is what leads me to suspect that it is not any one obvious point/line in the thread where the code breaks, but rather a general failure happening discretely somewhere that is only showing itself at a random time/location during execution of the thread.

    But exactly where this occurs is what's mystifying me: if it occurs in the thread, then it would also show itself when calling the thread explicitly, the same with if it occurs beforehand. Leading back to what I mentioned before, either being a failure in _beginthreadex (but how? why? it returns succesful) or some error that ONLY makes itself apparent in a threading context, yet mysteriously hides itself completely everytime when it is not... too weird...
    Last edited by @nthony; 10-14-2007 at 05:20 PM.

  12. #12
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Is it possible that some other thread is using the ListView.

    Does it hang or crash? If it hangs, you should be able to break into it with the debugger and see where the thread is at.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  13. #13
    Registered Abuser
    Join Date
    Jun 2006
    Location
    Toronto
    Posts
    591
    Not any thread I've created at least...

    One very important thing I forgot to mention:
    I don't know how this slipped my mind, but its what brought me to suspect WinAPI in the first place. Of the many unsure things in the thread, the one thing that is certain is that, if I put a call to MessageBox() anywhere in the thread it will then hang at that point. Sorry, I was remiss in not mentioning this before, but I think this is pretty damning evidence as to some of the API calls' performance (or lack there of) in threads. A simple:
    MessageBox(hwnd, "test", "test", 0);
    system("pause");
    Gets me the "beep" sound of the message box, but never actually makes it to the pause (but message box works fine when Thread is called explicitly), which tells me execution is getting trapped somewhere in the MessageBox API call...

    It hangs, but as I said, even though I am able to find out at what point execution gets up to, it is not descriptive of the problem at all. It may hang at line X, but then when I comment that line out, it then hangs at line C (completely unrelated to X), then line G, line Z, and so on.
    I guess I'll play with some of the more advanced features of the debugger, but I think I'd sooner just rewrite the entire function as it would probably take less time (and would give me an excuse to improve it).
    Thanks for the help anyways guys.
    Last edited by @nthony; 10-15-2007 at 02:36 AM.

  14. #14
    train spotter
    Join Date
    Aug 2001
    Location
    near a computer
    Posts
    3,868
    MessagerBox() is not thread safe. IIRC (been three years...) MessageBox() creates a new thread to poll for a response, allows the calling thread to continue, thus negating any critical sections.

    Should you be using PostThreadMessage()?
    "Man alone suffers so excruciatingly in the world that he was compelled to invent laughter."
    Friedrich Nietzsche

    "I spent a lot of my money on booze, birds and fast cars......the rest I squandered."
    George Best

    "If you are going through hell....keep going."
    Winston Churchill

  15. #15
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    Er, what? MessageBox creates a modal dialog, with its own message loop executed by the thread that called MessageBox. There's absolutely no need for an additional thread. It would, in fact, be quite mad to have an additional thread, because MessageBox is designed to be usable even in low memory conditions, and creating a new thread is a really bad idea then.

    However, there is one potential thread safety problem, and that's when you have multiple message boxes in different threads that specify the same parent window. The message box disables the window on being displayed and enables it on being dismissed. If you have multiple boxes, the first one to be dismissed might enable the window, thus ruining the modal nature of the other boxes.

    That said, modal message boxes from any thread but the UI thread are a bad idea anyway. UI should be confined to the UI thread. If something in a worker thread warrants UI interaction, post or send a message to the UI thread to instruct it to initiate the interaction.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. problem threading a member function
    By Syneris in forum C++ Programming
    Replies: 10
    Last Post: 12-31-2008, 07:26 PM
  2. c++ threading
    By Anddos in forum C++ Programming
    Replies: 4
    Last Post: 12-28-2005, 03:29 PM
  3. Problem with threading
    By osal in forum Windows Programming
    Replies: 6
    Last Post: 07-21-2004, 12:41 PM
  4. do i still need winAPI
    By datainjector in forum Windows Programming
    Replies: 8
    Last Post: 07-12-2003, 01:43 AM
  5. references for the winapi
    By stallion in forum Windows Programming
    Replies: 9
    Last Post: 01-28-2003, 02:56 AM