• The Old New Thing

    One in five Swedes steal their Christmas tree

    • 5 Comments
    According to Aftonbladet, "Gathering Stockholm's finest news from overheard conversations on the street corner", En av fem stjäl sin julgran. ("One in five steals their Christmas tree.") This of course comes from a highly scientific online reader poll. The question is, "How do you get your Christmas tree?" and the response categories are (from top to bottom, with percentages as of 2:31pm PST): "Buy" (35.7%), "Cut with permission" (13.2%), "Cut without permission" (14.3%), "Steal from some other source" (6.1%), "Get from some other source" (4.1%) and "Don't have a tree" (26.7%).
  • The Old New Thing

    How do I determine whether I own a critical section if I am not supposed to look at internal fields?

    • 16 Comments

    Seth asks how he can perform proper exception-handling cleanup if he doesn't know whether he needs to clean up a critical section.

    I'm using SEH, and have some __try/__except blocks in which the code enters and leaves critical sections. If an exception occurs, I don't know if I'm currently in the CS or not. Even wrapping the code in __try/__finally wouldn't solve my problems.

    Answer: You know whether you own the CRITICAL_SECTION because you entered it.

    Method 1: Deduce it from your instruction pointer.
    "If I'm at this line of code, then I must be in the critical section."
    __try {
      ...
      EnterCriticalSection(x);
      __try { // if an exception happens here
         ...  // make sure we leave the CS
      } __finally { LeaveCriticalSection(x); }
      ...
    } except (filter) {
      ...
    }
    

    Note that this technique is robust to nested calls to EnterCriticalSection. If you take the critical section again, then wrap the nested call in its own try/finally.

    Method 2: Deduce it from local state
    "I'll remember whether I entered the critical section."
    int cEntered = 0;
    __try {
      ...
      EnterCriticalSection(x);
      cEntered++;
      ...
      cEntered--;
      LeaveCriticalSection(x);
      ...
    } except (filter) {
      while (cEntered--)
        LeaveCriticalSection(x);
      ...
    }
    

    Note that this technique is also robust to nested calls to EnterCriticalSection. If you take the critical section again, increment cEntered another time.

    Method 3: Track it in an object
    Wrap the CRITICAL_SECTION in another object.

    This most closely matches what Seth is doing today.

    class CritSec : CRITICAL_SECTION
    {
    public:
     CritSec() : m_dwDepth(0), m_dwOwner(0)
       { InitializeCriticalSection(this); }
     ~CritSec() { DeleteCriticalSection(this); }
     void Enter() { EnterCriticalSection(this);
        m_dwDepth++;
        m_dwOwner = GetCurrentThreadId(); }
     void Leave() { if (!--m_dwDepth) m_dwOwner=0;
        LeaveCriticalSection(this); }
     bool Owned()
       { return GetCurrentThreadId() == m_dwOwner; }
    private:
      DWORD m_dwOwner;
      DWORD m_dwDepth;
    };
    
    __try {
      assert(!cs.Owned());
      ...
      cs.Enter();
      ...
      cs.Leave();
      ...
    } except (filter) {
      if (cs.Owned()) cs.Leave();
    }
    

    Notice that this code is not robust to nested critical sections (and correspondingly, Seth's code isn't either). If you take the critical section twice, the exception handler will only leave it once.

    Note also that we assert that the critical section is not initially owned. If it happens to be owned already, then our cleanup code may attempt to leave a critical section that it did not enter. (Imagine if an exception occurs during the first "...".)

    Method 4: Track it in a smarter object
    Wrap the CRITICAL_SECTION in a smarter object.

    Add the following method to the CritSec object above:

     DWORD Depth() { return Owned() ? m_dwDepth : 0; }
    

    Now you can be robust to nested critical sections:

    DWORD dwDepth = cs.Depth();
    __try {
      ...
      cs.Enter();
      ...
      cs.Leave();
      ...
    } except (filter) {
      while (cs.Depth() > dwDepth)
        cs.Leave();
    }
    

    Note however that I am dubious of the entire endeavor that inspired the original question.

    Cleaning up behind an exception thrown from within a critical section raises the issue of "How do you know what is safe to clean up?" You have a critical section because you are about to destabilize a data structure and you don't want others to see the data structure while it is unstable. But if you take an exception while owning the critical section - well your data structures are unstable at the point of the exception. Merely leaving the critical section will now leave your data structures in an inconsistent state, leading to harder-to-diagnose bugs later on. "How did my count get out of sync?"

    More rants on exceptions in a future entry.

    Exercise: Why don't we need to use synchronization to protect the uses of m_dwDepth and m_dwOwner?

    Update 2004/Jan/16: Seth pointed out that I got the two branches of the ternary operator backwards in the Depth() function. Fixed.

  • The Old New Thing

    German sounds more and more like "Alles Lookenpeepers" every day

    • 3 Comments
    München wird ausgebootet. Maybe someday Alles Lookenspeepers will become proper German.
  • The Old New Thing

    Sometimes, an app just wants to crash

    • 14 Comments
    I think it was Internet Explorer 5.0, when we discovered that a thirdparty browser extension had a serious bug, the details of which aren't important. The point was that this bug was so vicious, it crashed IE pretty frequently. Not good. To protect the users from this horrible fate, we marked the object as "bad" so IE wouldn't load it.

    And then we got an angry letter from the company that wrote this browser extension. They demanded that we remove the marking from their object and let IE crash in flames every time the user wanted to surf the web. Why? Because they also wanted us to hook up Windows Error Reporting to detect this crash and put up a dialog that says, "A fix for the problem you experienced is available. Click here for more information," and the "more information" was a redirect to the company's web site (where you could upgrade to version x.y of Program ABC for a special price of only $nnn!). (Actually I forget whether the upgrade was free or not, but the story is funnier if you had to pay for it.)

    In other words, they were crashing on purpose in order to drive upgrade revenue.

    (Astute readers may have noticed an additional irony: If the plug-in crashed IE, then how could the user view the company's web page so they could purchase and download the latest version?)
  • The Old New Thing

    Rip-it

    • 9 Comments
    Last night I had to frog several dozen rows of knitting because I forgot to change needles. Color changes I remember. Needle changes I always forget. Probably because color changes are much more exciting.
  • The Old New Thing

    How to void your warranty

    • 21 Comments

    MSDN just published an article telling people that it's okay to use reserved fields in an internal structure. Anybody who does this has just voided their warranty. Please put a "This program has a high likelihood of crashing after you install the next Service Pack or upgrade your OS" in your license agreement.

    And you wonder why app compat is so hard.

  • The Old New Thing

    What is the window nesting limit?

    • 10 Comments

    In the old days, Windows didn't bother enforcing a nesting limit because, well, if you want to nest windows 200 deep, that's your decision. Many window operations are recursive, but since everything happened on the application's stack, it was your own responsibility to make your stack big enough so it didn't overflow.

    But Windows NT moved the window manager off the application stack (first into a separate process, then into kernel mode). So now the OS needs to watch out for stack overflow attacks from people creating too many nested windows.

    The window nesting limit was set to 100 for the early days of Windows NT. For Windows XP, it dropped to 50 because increased stack usage in some internal functions caused us to overflow at around 75. Dropping to 50 created some breathing room.

    Disclaimer: I was not personally involved in this issue. I'm just reporting what I was able to figure out from reading checkin logs.

  • The Old New Thing

    When marketing designs a screenshot

    • 17 Comments

    Have you checked out AMD's ads for their AMD Athlon 64 FX processor? The copy reads, "My adrenalin fix isn't what it used to be. Double the dose. AMD me." And it has a picture of a tough-looking guy glaring at the camera, challenging the reader to do their worst.

    But take a look at what's on his screen. It's a giant command window. Now look at what this adrenalin junkie has been typing into his command window:

    Microsoft Windows XP [Version 5.1.2600]
    (C) Copyright 1985-2001 Microsoft Corp.
    
    C:\>fg
    'fg' is not recognized as an internal or external command,
    operable program or batch file.
    
    C:\>
    C:\>
    C:\>
    C:\>
    C:\>sdf
    'sdf' is not recognized as an internal or external command,
    operable program or batch file.
    
    C:\>sdf
    'sdf' is not recognized as an internal or external command,
    operable program or batch file.
    
    C:\>
    C:\>
    C:\>df
    'df' is not recognized as an internal or external command,
    operable program or batch file.
    
    Wow, he's a real l33t h4x0r he iz.
  • The Old New Thing

    Tinkering with the look

    • 6 Comments
    I've been tinkering with the look of the comments section, based on initial feedback. Took me a while but I think I found something that works pretty well.
  • The Old New Thing

    The unsafe device removal dialog

    • 27 Comments

    In a comment, somebody asked what the deal was with the unsafe device removal dialog in Windows 2000 and why it's gone in Windows XP.

    I wasn't involved with that dialog, but here's what I remember: The device was indeed removed unsafely. If it was a USB storage device, for example, there may have been unflushed I/O buffers. If it were a printer, there may have been an active print job. The USB stack doesn't know for sure (those are concepts at a higher layer that the stack doesn't know about) - all it knows is that it had an active channel with the device and now the device is gone, so it gets upset and yells at you.

    In Windows XP, it still gets upset but it now keeps its mouth shut. You're now on your honor not to rip out your USB drive before waiting two seconds for all I/O to flush, not to unplug your printer while a job is printing, etc. If you do, then your drive gets corrupted / print job is lost / etc. and you're on your own.
Page 417 of 434 (4,333 items) «415416417418419»