History

  • The Old New Thing

    The dreaded "main" threading model

    • 7 Comments

    In the absence of an explicit threading model for your COM object, you get the "main" threading model. The "main" threading model is little-known, and that's a good thing. It's a relic from the days before multi-threading.

    The first thread in a process to initialize COM becomes declared the "main" thread. (It might be the first thread to initialize COM in apartment model; I forget.) When a "main" threaded object is created, COM marshals the creation call to the main thread, creates the object, then marshals the result back to the creator's thread. Similarly, when you invoke any method on the object, the call is marshalled to the main thread, invoked, then the result is marshalled back.

    In other words, a "main" threaded object is like an apartment threaded object, with the additional constraint that the only apartment that can use it is the one that the "main" thread belongs to.

    As you can imagine, this is a horrific performance penalty in any multithreaded application, since there is so much marshalling going on. Even worse, it completely bottlenecks the main thread because there are now all these objects that must be serviced on that thread and no other thread.

    Even worse than worse, all this marshalling creates new opportunities for re-entrancy. While waiting for the main thread to do its thing, the calling thread will likely process messages, which means that you can receive a window message at a time when you didn't expect it.

    So why does this awful threading model exist at all?

    For backwards compatibility with COM objects written before multithreaded support was added to COM. Back in those days, there was only one thread, so COM objects could be extremely lazy with their synchronization. In fact, they didn't need any! If you have only one thread, then you certainly don't need to coordinate your actions with other threads because there are none.

    That's also why "main" threading model is the default. Threading models were invented when multithreading support was added to COM. Before then, there were no threads, so no threading models. All old objects therefore didn't specify a threading model in their registration.

    The only reason you should even be aware of this ancient threading model in the first place is that if you forget to specify a threading model in your object registration, you will get the dreaded "main" threading model by default.

    And then you will wonder why your application's performance is horrible, and why you have all these strange re-entrancy problems.

  • The Old New Thing

    Why is the default 8-bit codepage called "ANSI"?

    • 14 Comments

    Reader Ben Hutchings wanted to know why the 8-bit codepage is called "ANSI" when it isn't actually ANSI.

    But instead of saying, "Oh well, some things mortals were never meant to know," he went and dug up the answer himself.

    A quick Google for Windows ANSI misnomer found me exactly what I was looking for [pdf]:

    "The term "ANSI" as used to signify Windows code pages is a historical reference, but is nowadays a misnomer that continues to persist in the Windows community. The source of this comes from the fact that the Windows code page 1252 was originally based on an ANSI draft, which became ISO Standard 8859-1. However, in adding code points to the range reserved for control codes in the ISO standard, the Windows code page 1252 and subsequent Windows code pages originally based on the ISO 8859-x series deviated from ISO. To this day, it is not uncommon to have the development community, both within and outside of Microsoft, confuse the 8859-1 code page with Windows 1252, as well as see "ANSI" or "A" used to signify Windows code page support.
  • The Old New Thing

    When you change the insides, nobody notices

    • 74 Comments

    I find it ironic when people complain that Calc and Notepad haven't changed. In fact, both programs have changed. (Notepad gained some additional menu and status bar options. Calc got a severe workover.)

    I wouldn't be surprised if these are the same people who complain, "Why does Microsoft spend all its effort on making Windows 'look cool'? They should spend all their efforts on making technical improvements and just stop making visual improvements."

    And with Calc, that's exactly what happened: Massive technical improvements. No visual improvement. And nobody noticed. In fact, the complaints just keep coming. "Look at Calc, same as it always was."

    The innards of Calc - the arithmetic engine - was completely thrown away and rewritten from scratch. The standard IEEE floating point library was replaced with an arbitrary-precision arithmetic library. This was done after people kept writing ha-ha articles about how Calc couldn't do decimal arithmetic correctly, that for example computing 10.21 - 10.2 resulted in 0.0100000000000016.

    (These all came from people who didn't understand how computers handle floating point. I have a future entry planned to go into floating point representations in more detail.)

    Today, Calc's internal computations are done with infinite precision for basic operations (addition, subtraction, multiplication, division) and 32 digits of precision for advanced operations (square root, transcendental operators).

    Try it: 1 / 3 * 10000000000 - 3333333333 =. The result is one third exactly. Type 1/x - 3 = and you get zero back. (Of course, if you don't believe that, then repeat the sequence "* 10000000000 - 3333333333 =" until you're bored and notice that the answer always comes back as 0.33333333333333333333333333333333. If it were fixed-precision, then the 3's would eventually stop coming.)

    Thirty-two positions of precision for inexact results not good enough? The Power Calculator PowerToy uses the same arithmetic engine as Calc and lets you crank the precision to an unimaginable 512 digits.

    Anyway, my point is that - whether you like it or not - if you don't change the UI, nobody notices. That's so much effort is spent on new UI.

  • The Old New Thing

    How did those FILE_SHARE_* values come to be?

    • 0 Comments

    Larry Osterman picks up the history ball and describes how those FILE_SHARE_* values came to be.

  • The Old New Thing

    Why did InterlockedIncrement/Decrement only return the sign of the result?

    • 16 Comments

    If you read the fine print of the InterlockedIncrement and InterlockedDecrement functions, you'll see that on Windows NT 3.51 and earlier and on Windows 95, the return value only matches the sign of the result of the increment or decrement. Why is that?

    The 80386 instruction set supports interlocked increment and decrement, but the result of the increment/decrement operation is not returned. Only the flags are updated by the operation. As a result, the only information you get back from the CPU about the result of the operation is whether it was zero, positive, or negative. (Okay, you also get some obscure information like whether there were an even or odd number of 1 bits in the result, but that's hardly useful nowadays.)

    Since those operating systems supported the 80386 processor, their implementations of the InterlockedIncrement and InterlockedDecrement functions were limited by the capabilities of the processor.

    The 80486 introduced the XADD instruction which returns the original value of the operand. With this additional information, it now becomes possible to return the result of the operation exactly.

    Windows NT 4 dropped support for the 80386 processor, requiring a minimum of an 80486, so it could take advantage of this instruction. Windows 98 still had to support the 80386, so it couldn't.

    So how did Windows 98 manage to implement an operation that was not supported by the CPU?

    Windows 98 detected whether you had a CPU that supported the new XADD instruction. If not, then it used an alternate mechanism which was mind-bogglingly slow: It called a driver whenever you wanted to increment or decrement a variable. The driver would then emulate the XADD instruction by disabling interrupts and performing the operation in locked memory. Since Windows 98 was a uniprocessor operating system, it didn't have to worry about a second processor changing the memory at the same time; all it needed to ensure was that the single processor didn't get interrupted while it was performing the "atomic" operation.

  • The Old New Thing

    Chris Pratley's history lesson

    • 9 Comments

    If you haven't read it yet, check out Chris Pratley's voluminous discourse on various aspects of the history of Word. It packs more history into one entry than I do all year.

    And that was a sequel! You can read the first half, too.

    In fact, the good stuff keeps on coming. just read it all.

  • The Old New Thing

    Broadcasting user-defined messages

    • 5 Comments

    When you broadcast a message (via HWND_BROADCAST) remember that the message you broadcast must have global meaning. I discussed earlier what the various message ranges mean. Notice that only the system-defined range (0..WM_USER-1) and the registered message range (MAXINTATOM .. MAXWORD) have global meaning. The other two ranges have class-specifc or application-specific meanings.

    In other words, you can't broadcast a message in the WM_USER range since that message has a different meaning for each window class. Similarly, a message in the WM_APP range has a different meaning for each application.

    We ran into this problem in Windows 95. There were programs that decided to broadcast private messages like WM_USER+0x0100, intending them to be delivered to other instances of that program. Of course, when those messages reached some other windows, they interpreted WM_USER+0x0100 as some other private message and either acted funny or crashed.

    On the other hand, the programs really wanted the message to reach the windows of other copies of itself, so we couldn't just block the broadcast of the programs would stop working. Progams were relying on the system not trying to stop them from crashing other programs!

    The solution was to split the difference. If you broadcast a message that was not safe to broadcast, Windows 95 would send it only to old-style programs. New-style programs (marked as version 4.0 or higher) would not receive the messages.

    That way, old programs continued to affect each other as they always did, but new programs followed the new rules.

    Moral of the story: When you broadcast a message, make sure it's one that every receiving window will be able to handle.

  • The Old New Thing

    Why doesn't C# have "const"?

    • 45 Comments

    I was going to write about why C# doesn't have "const", but Stan Lippman already discussed this in A Question of Const, so now I don't have to.

    (And another example of synchronicity: After I wrote up this item and tossed it into the queue, Eric Gunnerson took up the topic as well.

  • The Old New Thing

    Why can't the system hibernate just one process?

    • 48 Comments

    Windows lets you hibernate the entire machine, but why can't it hibernate just one process? Record the state of the process and then resume it later.

    Because there is state in the system that is not part of the process.

    For example, suppose your program has taken a mutex, and then it gets process-hibernated. Oops, now that mutex is abandoned and is now up for grabs. If that mutex was protecting some state, then when the process is resumed from hibernation, it thinks it still owns the mutex and the state should therefore be safe from tampering, only to find that it doesn't own the mutex any more and its state is corrupted.

    Imagine all the code that does something like this:

    // assume hmtx is a mutex handle that
    // protects some shared object G
    WaitForSingleObject(hmtx, INFINITE);
    // do stuff with G
    ...
    // do more stuff with G on the assumption that
    // G hasn't changed.
    ReleaseMutex(hmtx);
    

    Nobody expects that the mutex could secretly get released during the "..." (which is what would happen if the process got hibernated). That goes against everything mutexes stand for!

    Consider, as another example, the case where you have a file that was opened for exclusive access. The program will happily run on the assumption that nobody can modify the file except that program. But if you process-hibernate it, then some other process can now open the file (the exclusive owner is no longer around), tamper with it, then resume the original program. The original program on resumption will see a tampered-with file and may crash or (worse) be tricked into a security vulnerability.

    One alternative would be to keep all objects that belong to a process-hibernated program still open. Then you would have the problem of a file that can't be deleted because it is being held open by a program that isn't even running! (And indeed, for the resumption to be successful across a reboot, the file would have to be re-opened upon reboot. So now you have a file that can't be deleted even after a reboot because it's being held open by a program that isn't running. Think of the amazing denial-of-service you could launch against somebody: Create and hold open a 20GB file, then hibernate the process and then delete the hibernation file. Ha-ha, you just created a permanently undeletable 20GB file.)

    Now what if the hibernated program had created windows. Should the window handles still be valid while the program is hibernated? What happens if you send it a message? If the window handles should not remain valid, then what happens to broadcast messages? Are they "saved somewhere" to be replayed when the program is resumed? (And what if the broadcast message was something like "I am about to remove this USB hard drive, here is your last chance to flush your data"? The hibernated program wouldn't get a chance to flush its data. Result: Corrupted USB hard drive.)

    And imagine the havoc if you could take the hibernated process and copy it to another machine, and then attempt to restore it there.

    If you want some sort of "checkpoint / fast restore" functionality in your program, you'll have to write it yourself. Then you will have to deal explicitly with issues like the above. ("I want to open this file, but somebody deleted it in the meantime. What should I do?" Or "Okay, I'm about to create a checkpoint, I'd better purge all my buffers and mark all my cached data as invalid because the thing I'm caching might change while I'm in suspended animation.")

  • The Old New Thing

    A story about USB floppy drives

    • 9 Comments

    A friend of mine used to work on the development of the USB specification and subsequent implementation. One of the things that happens at these meetings is that hardware companies would show off the great USB hardware they were working on. It also gave them a chance to try out their hardware with various USB host manufacturers and operating systems to make sure everything worked properly together.

    One of the earlier demonstrations was a company that was making USB floppy drives. The company representative talked about how well the drives were doing and mentioned that they make two versions, one for PCs and one for Macs.

    "That's strange," the committee members thought to themselves. "Why are there separate PC and Mac versions? The specification is very careful to make sure that the same floppy drive works on both systems. You shouldn't need to make two versions."

    So one of the members asked the obvious question. "Why do you have two versions? What's the difference? If there's a flaw in our specification, let us know and we can fix it."

    The company representative answered, "Oh, the two floppy drives are completely the same electronically. The only difference is that the Mac version comes in translucent blue plastic and costs more."

    This company was of course not the only one to try to capitalize on the iMac-inspired translucent plastic craze. My favorite is the iMac-styled George Foreman Grill. (I'm told the graphite ones cook faster.)

Page 41 of 50 (495 items) «3940414243»