• The Old New Thing

    The Ballard Locks will be empty this week

    • 9 Comments

    The Hiram M. Chittenden Locks (more commonly known as the Ballard Locks) are a common attraction in Seattle. But if you pay a visit for the next week, you'll find that the large set of locks will be empty. And not just empty of boats. Empty of water.

    The chamber has been emptied of water for annual maintenance, and if you stop by, you can take pictures of a big hole in the ground.

    According to The Seattle Times, items found upon draining the chamber include cell phones, a two-way radio, a boot, beer cans, and prescription glasses. Nothing too exciting, I have to admit. Now, if they had found a bicycle...

  • The Old New Thing

    Rob Cockerham investigates those companies that pay cash for gold

    • 9 Comments

    Rob Cockerham seems to have a lot of spare time, which is great for the rest of us, because he investigates all those things we simply don't have the time for, and then posts the results on his Web site ("The sixth-best website in the world").

    Today's highlight is a pair of investigations he performed some time ago which seem to show two sides of one industry.

    That Web site is a sinkhole of time-wastage. If you're not careful, you'll find yourself clicking around from story to story, like the How much is inside? adventures, in which he investigates things like how many threads per inch are there in 360-thread count pillowcases? Or his gallery of costumes (for Hallowe'en or other events), including my favorite: Paparazzi.

  • The Old New Thing

    If you want to consume all the virtual address space, well, then go ahead and consume it, you don't need my help

    • 9 Comments

    Commenter Matthew Chaboud asks if there's an easy way to consume all the virtual address space below 4GB, short of, well, actually allocating it. "It seems like there should be a cleaner way to do this."

    If you want to consume all the virtual address space, then call VirtualAlloc until you turn blue. Programs shouldn't care what address they get back from a memory allocation function; they should handle values below 2GB and above 2GB with equal facility.

    It's not like there's a ConsumeAllAvailableVirtualAddressSpaceAndExhaustTheHeap function. (Is there a AllocateAllRemainingDiskSpaceAndFillExistingFilesWithZeroes function?) What would be the point of such a function? Once you call it, you have run out of memory!

    If Mr. Chaboud is talking about keeping programs away from bottom 4GB of virtual address space on a 64-bit machine, then a much easier way to do this is to set the AllocationPreference configuration setting to specify that memory should be allocated from high addresses first. (But I don't think that's the scenario that prompted the original question, because on 64-bit Windows, the default heap is above the 4GB boundary, so there would be no need to exhaust the heap in order to consume the memory at virtual addresses below 4GB.)

    Correction: Pavel Lebedinsky points out that the default heap is below 4GB on 64-bit machines. It used to be above the 4GB boundary on earlier versions of 64-bit Windows, but I guess they changed it.

  • The Old New Thing

    Microspeak: Whale Boy

    • 9 Comments

    Today is the tenth anniversary of Windows Live Messenger. My colleague Danny Glasser provides some history behind the product, and you can watch a tenth anniversary celebration video created for the occasion. And thus is inspired today's Microspeak: Whale Boy.

    Whale Boy is the nickname for the pawn-shaped Messenger buddy icon. His normal state is green, but he changes color or picks up a piece of flair to indicate various changes in status, such as busy or away.

    I don't know the etymology of the term, but I suspect it came from his somewhat rotund appearance.

    Sample usage (which I made up just now; not from an actual document): "Why don't we light up Whale Boy when the user regains connectivity?"

    Bonus Microsoft trivia: At timecode 3:40 in the video (spoiler alert!) Steve Liffick presents Whale Boy with a strange glass object. That is the Microsoft ten-year service award. Stick around for 25 years and collect the entire set!

  • The Old New Thing

    How do I set an accessible name on an unlabeled control?

    • 9 Comments

    A customer asked for advice on accessibility. This was great news, because it meant that somebody actually cared about accessibility!

    We have a property sheet page that contains an unlabeled list view. The list view is not labeled because its meaning is implied by its placement on the dialog. This works great as long as you can see the screen, but we also need to associate an accessible name to the list view so that screen readers know what it is. We tried Set­Window­Text on the list view, but accessibility didn't pick it up. How do I set the accessibility name on the control?

    Place a static control immediately ahead of the list view in the z-order. If you don't want the static control to be visible, mark it as NOT WS_VISIBLE in the dialog template.

    This is another manifestation of the trick we learned some time ago of using a hidden static control with an accelerator in order to associate the accelerator key with an unlabeled control. In this case, the hidden static control isn't there to provide an accelerator; it's there to provide an accessible name.

  • The Old New Thing

    Microspeak: The bug farm

    • 9 Comments

    In its most general sense, the term bug farm refers to something that is a rich source of bugs.

    It is typically applied to code which is nearly unmaintainable. Code can arrive in this state through a variety of means.

    • Poor initial design.
    • An initial design that has been pushed far beyond its original specification (resulting in features built on top of other features in weird ways).
    • Overwhelming compatibility constraints such that the tiniest perturbation is highly likely to cause some application somewhere to stop working.
    • Responsibility for the code residing in people whom we shall euphemistically describe as "failing to meet your personal standards of code quality."

    The term is most often used as a cautionary term, calling attention to areas where there is high risk that code you're about to write is going to result in a bug farm.

    Aren't we setting ourselves up for a bug farm?
    This could easily lead to a bug farm from different lifetimes for this various state objects.

    The term is quite popular at Microsoft (pre-emptive snarky comment: because Microsoft software is all one giant bug farm). Here are some citations just from blogs.msdn.com:

    Layout runs under disable processing. The reason we did that is because, well, reentrant layout is a bug farm.
    A lot of testers suddenly realized that case sensitivity is a veritable bug farm on a project that thinks it is ready to go, but has not yet tried it.
    That type of implicit vs. explicit inference also turned out to be a bug farm.
    Did you forget to handle an entire set of test cases? Is the features implementation overly complex and going to be a bug farm?
  • The Old New Thing

    Swamping the thread pool with work faster than it can drain

    • 9 Comments

    This scenarios is real, but details have been changed to protect the guilty.

    Consider a window showing the top of a multi-page document. The developers found that when the user clicks the down-arrow button on the scroll bar, the program locks up for 45 seconds, over a dozen threads are created, and then suddenly everything clears up and the window displays the final paragraph of the document (i.e., it scrolled all the way to the bottom).

    The problem was traced to queueing tasks to the thread pool faster than they can drain.

    The document is an object which, unlike a window, has no thread affinity. (Naturally, there are critical sections around the various document methods so you don't have corruption if two threads try to modify the document at the same time.) The way to display a different portion of the document is to call a method which changes the viewport location.

    When the user clicks the down-arrow button on the scroll bar, the main thread creates a periodic timer at four fifths of the double-click speed, and each time the timer elapses, it does the equivalent of document.ScrollDown(). The code cancels the timer once the user releases the mouse button.

    The catch is that the document was so complicated that it takes a long time to change the view top and redraw the new view. (I don't remember exactly how long, but let's say it was 700ms. The important thing is that it takes longer than 400ms.)

    Given that set-up, you can see what happens when the user clicks the scroll down-arrow. The initial scroll is initiated, and before it can complete, another scroll is queued to the thread pool. The document view keeps trying to update its position, but the periodic timer generates scroll requests faster than the document view can keep up.

    If that description was too terse, here's a longer version.

    The code for scrolling went something like this:

    OnBeginScrollDown()
    {
     // Start a timer to do the scrolling
     CreateTimerQueueTimer(&htimer, NULL, ScrollAgain, NULL,
         0, GetDoubleClickTime() * 4 / 5, WT_EXECUTEDEFAULT);
    }
    
    OnEndScrollDown()
    {
     if (htimer != NULL) {
      DeleteTimerQueueTimer(NULL, htimer, INVALID_HANDLE_VALUE);
      htimer = NULL;
     }
    }
    
    ScrollAgain(...)
    {
     document.ScrollDown();
    }
    

    (In reality, the program didn't use the CreateTimerQueueTimer function—it had a custom timer queue and a custom thread pool—but the effect is the same.)

    At time T = 0, the user clicks on the scroll bar down-arrow. The UI thread starts the timer with an initial delay of zero and a period of 400ms. The timer fires immediately, and a thread pool thread is asked to run ScrollAgain. The ScrollAgain function calls ScrollDown, which begins the process of scrolling the document.

    At time T = 400ms, the periodic timer fires, and a new thread pool thread is created to service it. Pool thread 2 calls ScrollDown() and blocks.

    At time T = 700ms, the ScrollDown call on pool thread 1 completes, and now pool thread 2 can begin its call to ScrollDown().

    At time T = 800ms, the periodic timer fires again, and pool thread 1 (now idle) is asked to handle it. Pool thread 1 calls ScrollDown() and blocks.

    At time T = 1200ms, the periodic timer fires yet again. This time, there are no idle threads in the thread pool, so the thread pool manager creates yet another thread to service the timer. Pool thread 3 calls ScrollDown() and blocks.

    At time T = 1400ms, the ScrollDown() call issued by pool thread 2 completes. Pool thread 2 now returns to idle. Now the call to ScrollDown() from pool thread 1 (issued at time T = 800ms) can start.

    At time T = 1600ms, the periodic timer fires again, and pool thread 2 is chosen to service it. Pool thread 2 calls ScrollDown() and blocks.

    At time T = 2000ms, the periodic timer fires again, and a new pool thread is created to service it. Pool thread 4 calls ScrollDown() and blocks.

    You can see where this is going, I hope. Work is being generated by the periodic timer at a rate of one work item per 400ms, but it takes 700ms to carry out each work item, and the tasks are serialized on the document. It's like Lucy in the chocolate factory. The document is frantically trying to carry out all the work, and it never manages to catch up. Eventually, the document scrolls all the way to the bottom, and the mass of pent-up calls to ScrollDown() all return immediately since there is no more scrolling possible.

    Now that the document is idle, it can paint, and that's where the user finally sees the document, scrolled all the way to the bottom.

    There are a number of possible solutions here.

    One way is not to queue up another scroll while an old one is still running. Instead, just wait for it to finish, and then issue a new scroll that accumulates all the scrolling that had taken place while you were waiting for the first to complete. This results in jerky scrolling, however, and it creates a lag of up to 700ms between the user releasing the mouse button and scrolling actually stopping.

    Another approach is to disable repainting the entire document when you detect that you are in the document is too complex to scroll quickly case and just scroll the scrollbar thumb. When the user stops scrolling, re-enable painting and boom the document appears at the user's chosen location. This preserves responsiveness, but you lose the ability to see the document as you scroll it.

    I don't know what solution the customer finally went with. I was just there to help with the debugging.

    Bonus example: Larry Osterman describes another situation with the same underlying cause.

    Hidden take-away: Observe that both of these examples illustrate one of the subtle consequences of a design which moves all processing off the UI thread.

    Update: Note that Set­Timer wouldn't have helped here.

    case WM_TIMER:
      if (wParam == SCROLLTIMER) {
        QueueUserWorkItem(ScrollAgain, NULL, WT_EXECUTEDEFAULT);
      }
      ...
    

    Since the processing has been moved off the UI thread, the WM_TIMER messages are free to keep flowing in and queue up work faster than the background thread can keep up.

  • The Old New Thing

    How do I create a right-aligned toolbar button?

    • 9 Comments

    I didn't think this was all that common a request, but it came in twice from two different customers, so I guess there are still people creating toolbars, and creating them with right-aligned buttons (even though it violates Windows user interface guidelines, though I can't find a citation right now).

    You may have noticed that the toolbar common control doesn't provide a facility for creating right-aligned buttons. Partly because it's a violation of the guidelines anyway, but mostly because the default state of every feature is "not implemented." Adding a feature requires work, and since there is only a finite amount of work available to apply, you have to decide where to spend it. And generally speaking, you don't focus your efforts on helping people violate the guidelines.

    If you want to create a right-aligned toolbar button, you can cobble one together by combining things you already have. I can think of two ways of doing this off the top of my head.

    • Create two toolbars, one for the left-aligned buttons and one for the right-aligned buttons, then size and position them appropriately.
    • Create a separator between the left-aligned buttons and the right-aligned buttons, and set the separator size appropriately.

    Which comes to a third reason why there is no feature for right-aligned toolbar buttons: Because you can already do it yourself without too much effort.

  • The Old New Thing

    The performance improvements of a lock-free algorithm is often not in the locking

    • 9 Comments

    GWO wonders what the conditions are under which the lock-free version significantly outpeforms a simple critical section.

    Remember that switching to a lock-free algorithm should be guided by performance measurements. Switching from a simple algorithm to a complex one shouldn't be done unless you know that the simple algorithm is having trouble.

    That said, here are some non-obvious advantages of a lock-free algorithm over one that uses a simple lock. (Later, we'll see how you can take advantage of these techniques without actually going lock-free.)

    Consider a program that uses a simple critical section to perform something like the singleton constructor. Instead of a fancy lock-free algorithm, we use the much simpler version:

    CRITICAL_SECTION g_csSingletonX;
    X *g_px = NULL;
    
    X *GetSingletonX()
    {
        EnterCriticalSection(&g_csSingletonX);
        if (g_px == NULL)
        {
            g_px = new(nothrow) X();
        }
        LeaveCriticalSection(&g_csSingletonX);
        return g_px;
    }
    

    This simple code can run into trouble if the constructor function itself requires some locks, because now you have to impose a lock hierarchy in order to avoid a deadlock. (And this becomes impossible if the constructor function belongs to code outside your control.)

    When working out what your lock hierarchy should be, you may discover that you need to consolidate some locks. This avoids the inversion problem, but it also reduces your lock granularity. You might decide to use a single lock to cover all singletons, and then you later discover that you also have to extend the lock that protects X's constructor to cover other operations on X.

    CRITICAL_SECTION g_csCommon;
    
    // (updated to remove double-check lock because that just raises
    // more questions that distract from the point of the article)
    X *GetSingletonX()
    {
        EnterCriticalSection(&g_csCommon);
        if (g_px == NULL)
        {
            g_px = new(nothrow) X();
        }
        LeaveCriticalSection(&g_csCommon);
        return g_px;
    }
    
    Y *GetSingletonY()
    {
        EnterCriticalSection(&g_csCommon);
        if (g_py == NULL)
        {
            g_py = new(nothrow) Y();
        }
        LeaveCriticalSection(&g_csCommon);
        return g_py;
    }
    
    void X::DoSomething()
    {
        EnterCriticalSection(&g_csCommon);
        .. something ..
        LeaveCriticalSection(&g_csCommon);
    }
    

    Over time, your quiet little singleton lock has turned into a high-contention lock in your system.

    One nice thing about a lock-free algorithm is that since there is no lock, it can't create inversion in a lock hierarchy. (Of course, you have to be careful not to use the interlocked operations to build a private lock, because that puts you back where you started.)

    Another nice consequence of a lock-free algorithm is that, since there is no lock, you don't have to handle the WAIT_ABANDONED case. The data structure is never inconsistent; it passes atomically from one consistent state to another. Therefore, there's no need to write code to clean up leftover inconsistency. This came in handy in a case we looked at earlier, so that an application which crashes at an inopportune time will not corrupt the shared data and require a server reboot.

  • The Old New Thing

    Ready... cancel... wait for it! (part 2)

    • 9 Comments

    A customer had a question about I/O cancellation. They have a pending Read­File­Ex call with a completion procedure. They then cancel the I/O with Cancel­Io­Ex and wait for the completion by passing TRUE as the bWait parameter to Get­Overlapped­Result.

    Assuming both return success, can I assume that my completion procedure will not be called after GetOverlappedResult returns? It appears that GetOverlappedResult waits non-alertably for the I/O to complete, so I'm assuming it just eats the APC if there was one. But if an APC had been posted just before I called CancelIoEx, will it also cancel that APC?

    Get­Overlapped­Result does not magically revoke completion callbacks. Why should it?

    Recall that completion is not the same as success. Completion means that the I/O subsystem has closed the books on the I/O operation. The underlying operation may have completed successfully or it may have failed (and cancellation is just one of the many possible reasons for failure). Either way, the completion procedure signed up to be notified when the I/O completes, and therefore it will be called to be informed of the completion due to cancellation.

    Besides, as the customer noted, there is a race condition if the Cancel­Io­Ex call is made just after the I/O completed, in which case it didn't get cancelled after all.

    This answers our question from last time, namely, how our fix for the cancellation code was incomplete. If the I/O had been issued with a completion routine (or equivalently, if it had been issued against an I/O completion port), then the code frees the OVERLAPPED structure before the completion routine runs. The kernel doesn't care that you did that (the kernel is finished with the OVERLAPPED structure), but your completion routine is probably not going to be happy that it was given a pointer to freed memory as its lpOverlapped parameter.

    You have to delay freeing the OVERLAPPED structure until the completion routine executes. Typically, this is done by allocating the OVERLAPPED structure on the heap rather than the stack, and making it the completion routine's responsibility to free the memory as its final act.

Page 377 of 458 (4,573 items) «375376377378379»