August, 2008

  • The Old New Thing

    What possible use are those extra bits in kernel handles? Part 3: New object types

    • 23 Comments

    Last time, we saw how those extra bits can be used to multiplex HANDLE with other values. That was a specific case of a more general scenario: Expanding the handle namespace to include things that aren't handles. (You can also view today's example as a generalization of the sentinel value problem, where we need to generate an arbitrary number of sentinel values dynamically. Actually, multiplexing HANDLE with HRESULT is also just another special case: We expanded the handle namespace to include error codes too.)

    As I noted in the base article, the people who are most interested in this sort of thing are people writing low-level class libraries or wrapping kernel objects inside a larger framework.

    Suppose, for example, you are writing a library that manipulates kernel objects, but you also have private types of objects (say, a handle to a remote computer) that you also want this library to be able to manipulate. One way of doing this is to wrap everything inside some base class that virtualizes away what type of handle you're working on:

    class ExtendedHandle {
    public:
      virtual ExtendedHandleType GetType() = 0;
      virtual ~ExtendedHandle() { }
    };
    
    class KernelExtendedHandle : public ExtendedHandle {
    public:
      static KernelExtendedHandle *Create(...);
      ExtendedHandleType GetType() { return KernelHandle; }
      HANDLE GetHandle() { return Handle; }
    private:
      KernelExtendedHandle(...);
      HANDLE Handle;
    };
    
    class RemoteComputerExtendedHandle : public ExtendedHandle {
    public:
      static RemoteComputerExtendedHandle *Create(...);
      ExtendedHandleType GetType() { return RemoteComputer; }
      LPCTSTR GetComputerName() { ... }
      ... other remote computer methods ...
    private:
      RemoteComputerExtendedHandle(...);
      ... stuff necessary for tracking remote computers ...
    };
    

    Now, your library spends only 1% of its time manipulating these private object types; most of the time, it's dealing with regular kernel handles. In other words, 99% of your objects are of type KernelExtendedHandle. What used to be just a HANDLE (4 bytes) is now a EHANDLE that in turn points to an 8-byte structure (4 bytes for the vtable and 4 bytes for the HANDLE). Your memory requirements have tripled and you added another level of indirection (costing you locality), just for that 1% case.

    But you can do better if you have those extra bits to play with. Since 99% of the objects you're wrapping are just plain old kernel handles, you can say that if the bottom bit is clear, then it's just a kernel handle, and if the bottom bit is set, then the upper bits tell us what we are really operating on.

    typedef void *EHANDLE;
    
    BOOL IsKernelHandle(EHANDLE Handle)
    {
      return (!((INT_PTR)Handle & 1));
    }
    
    EHANDLE CreateHandleFromKernelHandle(HANDLE Handle)
    {
      // if this assertion fires, then somebody tried to
      // use an invalid kernel handle!
      assert((!((INT_PTR)Handle & 1));
      return (EHANDLE)Handle;
    }
    
    EHANDLE CreateHandleFromExtendedHandle(ExtendedHandle Handle)
    {
      // if this assertion fires, then somebody allocated
      // a misaligned ExtendedHandle!
      assert(!((INT_PTR)Handle & 1));
      return (EHANDLE)((INT_PTR)Handle | 1));
    }
    
    ExtendedHandle *GetExtendedHandle(EHANDLE Handle)
    {
      assert(!IsKernelHandle(Handle));
      return (ExtendedHandle*)((INT_PTR)Handle & ~1);
    }
    
    ExtendedHandleType GetType(EHANDLE Handle)
    {
     if (IsKernelHandle(Handle)) {
       return KernelHandleType;
     } else {
       return GetExtendedHandle(Handle)->GetType();
     }
    }
    
    void ECloseHandle(EHANDLE Handle)
    {
      if (IsKernelHandle(Handle))
      {
        CloseHandle(GetKernelHandle(Handle));
      } else {
       delete GetExtendedHandle(Handle);
      }
    }
    

    Now the cost of a wrapped kernel handle is just 4 bytes: 4 bytes for the EHANDLE, which also doubles as the actual kernel handle. The cost of wrapped pseudo-handles is the same as before (4 bytes for the EHANDLE, plus the size of the corresponding XxxExtendedHandle class). We used the trick from last time in order to pack 33 bits into only 32 bits: Since we know that the bottom bit of both kernel HANDLEs and ExtendedHandle pointers are zero, we can use it as a discriminator.

    If you are not confident that your ExtendedHandle classes all use aligned pointers, you can use a different packing mechanism by using your own handle translation table:

    ExtendedHandle *ExtendedHandleTable;
    
    ExtendedHandle *GetExtendedHandle(EHANDLE Handle)
    {
      assert(!IsKernelHandle(Handle));
      return ExtendedHandleTable[(INT_PTR)Handle >> 1];
    }
    

    Using this technique, the upper 31 bits of an EHANDLE which refers to an ExtendedHandle is an index into a privately-managed table of ExtendedHandle objects.

    This secondary handle table technique is entirely analogous to the trick which one Posix library uses to distinguish "real" process IDs from "virtual" process IDs, except that they are relying on undocumented behavior because the bottom bits of process IDs are not guaranteed to be zero!

    So there you have it, three scenarios where you can take advantage of knowing that the bottom bits of kernel handles are always zero.

  • The Old New Thing

    The stroke-count-based sort isn't random, although it looks that way if you only see it in translation

    • 13 Comments

    During the NBC coverage of the opening ceremony of the Beijing Olympics, the announcers more than once said that the teams will not be entering in the normal order, but rather in a random order based on the number of strokes in the team's name as translated into Chinese.

    This is an odd use of the word random. You might say that at the Athens Olympics, the teams did not enter in the normal order, but rather in a random order based on the collation of the characters in the team's name as translated into Greek.

    Teams enter the stadium in the collation order customary for the host nation, with the exception that Greece always goes first and the host nation always goes last. (Which created an interesting puzzle for the Athens Olympics: How do you go both first and last?) For Chinese, there are a variety of collations, which fall into two broad categories: pronunciation-based and stroke-count-based, and the organizers chose one of the stroke-count-based sorts. Fair enough.

    Of course, it seems random if you don't get to see the original Chinese names. And for some reason, NBC didn't show the Chinese names for the countries, so the opportunity to see the sort order in action was lost on the viewing audience. Viewers in the United States who saw the Ghanaian, Canadian, and Gabonese teams enter one after the other would just say "Yeah, that's the stupid random order again," because they didn't see that it was , 拿大, and .

    (Conspiracy theorists would say that NBC didn't show the Chinese names to allow them to manipulate the order of the teams and place the United States closer to the end of the list than it normally would appear.)

    Freaky Raymond-trivia that will probably show up on Wikipedia within 30 minutes: The first character of my given Chinese name is 瑞, which happens also to be the first character for the Chinese names of the countries 瑞士 (Switzerland) and 瑞典 (Sweden), so it's only fitting that the first two languages I learned in a classroom setting were Swiss German and Swedish. Alas, life is not as poetic as it should be, for the second character of my given Chinese name sorts after , so I would actually have marched between Sweden and Nauru (瑙魯). Of course, another impediment to my marching between Switzerland and Sweden is the more significant fact that I am not personally an Olympic team.

    Bonus griping about language reporting: The official Chinese-language cheer has been reported by the English-language press as China, add oil! While literally true, it's also a misleading translation. The phrase 加油 does mean, if you take it apart word by word, to add oil, but as a phrase, it means to refuel or, when used metaphorically, to cheer on. Chinese speakers don't think about oil when they say 加油 any more than English-speakers think of objects hurtling through the air when they say throw a party. In both cases, you only think about the phrase literally when you're making a pun or are otherwise playing around with the language.

  • The Old New Thing

    What possible use are those extra bits in kernel handles? Part 2: Overcoming limited expressiveness

    • 22 Comments

    Last time, we saw how those extra bits can be used to develop safe sentinel values. That is a special case of a more general problem: How do you pack 33 bits of information into a 32-bit value? Whereas last time, we weren't forced into the use of a sentinel value because we could develop a (cumbersome) helper class and switch people over to the helper class (or to pass two parameters to every function that used to take one), there are places where you are forced to try to squeeze 33 bits of information into a 32-bit value, and the helper class simply isn't going to work. (I'm going to assume 32-bit Windows for concreteness, but the same considerations apply to 64-bit Windows. Just make the appropriate changes.)

    Suppose you have a window message that does some work and returns a HANDLE, but it can fail, and when it does, you want to return an error code. In other words, you want to return either (TRUE, HANDLE) or (FALSE, HRESULT). But that's 33 bits of information, and you can return only 32 bits. How can you provide 33 bits of information with only 32 bits?

    Well, it turns out that you don't actually have 33 bits of information to return. Since handle values are multiples of four, the bottom two bits are always zero and therefore convey no information. A kernel handle is really only 30 bits. Similarly, a COM error code in the form of an HRESULT always has the top bit set—after all if the top bit were clear, it would be a success code! Therefore, there are only 31 bits of information in an HRESULT error code.

    Okay, so it turns out that (TRUE, HANDLE) is only 1 + 30 = 31 bits of information, and (FALSE, HRESULT) is only 1 + 31 = 32 bits of information. We can fit them inside a single 32-bit value after all!

    LRESULT PackHandleIntoLresult(HANDLE Handle)
    {
      LRESULT Lresult = (LRESULT)Handle;
    
      // if this assertion fires, then somebody tried to
      // pack an invalid handle!
      assert((Lresult & 1) == 0);
    
      return Lresult;
    }
    
    LRESULT PackErrorHresultIntoLresult(HRESULT Hresult)
    {
      // if this assertion fires, then somebody tried to
      // pack a success code!
      assert(FAILED(Hresult));
    
      return ((DWORD)Hresult << 1) | 1;
    }
    

    The bottom bit is our boolean that tells us whether we have a success or failure. If the bit is clear, then the operation succeeded and the entire value is the handle, relying on the fact that valid handles always have the bottom two bits clear. On the other hand, if the bottom bit is set, then we have an error code, and the remaining 31 bits give us the significant bits of the HRESULT. Unpacking the values would then go like this:

    BOOL IsLresultError(LRESULT Lresult)
    {
      return Lresult & 1;
    }
    
    HANDLE UnpackLresultToHandle(LRESULT Lresult)
    {
      assert(!IsLresultError(Lresult));
      return (HANDLE)Lresult;
    }
    
    HRESULT UnpackLresultToHresult(LRESULT Lresult)
    {
      assert(IsLresultError(Lresult));
      return (HRESULT)(0x80000000 | ((DWORD)Lresult >> 1));
    }
    

    In pictures (since people like pictures):

    Success:
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x|0|0| HANDLE
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
     v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x|0|0| LRESULT
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
     v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x|0|0| HANDLE
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    
    Failure:
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |1|e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e| HRESULT
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
     v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e|1| LRESULT
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \
       v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |1|e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e| HRESULT
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    

    That bottom bit tells us whether the upper 31 bits are the meaningful bits from a HANDLE or the meaningful bits from an HRESULT. Once we know which case we are in, we can take those upper bits and put them back into meaningful parts of the source data.

    If you want to put this trick on a more formal footing, you could express the multiplexing in the form of a discriminant in a union:

    // Type-specific value for HANDLE is the upper 31 bits
    LRESULT TypeSpecificValueFromHandle(HANDLE Handle)
    {
      LRESULT Lresult = (LRESULT)Handle;
    
      // if this assertion fires, then somebody tried to
      // pack an invalid handle!
      assert((Lresult &1) == 0);
    
      // discard the bottom bit since we know it is zero
      return Lresult >> 1;
    }
    
    HANDLE HandleFromTypeSpecificValue(LRESULT Lresult)
    {
      // regenerate the bottom bit which we know is zero
      return (HANDLE)(Lresult << 1);
    }
    
    // Type-specific value for HRESULT is the lower 31 bits
    LRESULT TypeSpecificValueFromHresult(HRESULT Hresult)
    {
      // if this assertion fires, then somebody tried to
      // pack a success code!
      assert(FAILED(Hresult));
    
      // discard the top bit since we know it is 1
      return (DWORD)Hresult & 0x7FFFFFFF;
    }
    
    HRESULT HresultFromTypeSpecificValue(LRESULT Lresult)
    {
      // regenerate the top bit which we know is 1
      return (HRESULT)(Lresult | 0x80000000);
    }
    
    // Oh boy, let's pack and unpack these puppies
    #define TYPE_HANDLE  0
    #define TYPE_HRESULT 1
    
    typedef struct PACKEDLRESULT {
     int Type:1;
     LRESULT TypeSpecificValue:sizeof(LRESULT)*8-1;
    } PACKEDLRESULT;
    
    typedef union PACKEDLRESULTHELPER {
     PACKEDLRESULT Structure;
     LRESULT Lresult;
    } PACKEDLRESULTHELPER;
    
    LRESULT PackLresult(int Type, LRESULT TypeSpecificValue)
    {
      PACKEDLRESULTHELPER Helper;
      Helper.Structure.Type = Type;
      Helper.Structure.TypeSpecificValue = TypeSpecificValue;
      return Helper.Lresult;
    }
    
    int GetPackedLresultType(LRESULT Lresult)
    {
      PACKEDLRESULTHELPER Helper;
      Helper.Lresult = Lresult;
      return Helper.Structure.Type;
    }
    
    HANDLE GetHandleFromPackedLresult(LRESULT Lresult)
    {
      PACKEDLRESULTHELPER Helper;
      Helper.Lresult = Lresult;
      return HandleFromTypeSpecificValue(Helper.Structure.TypeSpecificValue);
    }
    
    HRESULT GetHresultFromPackedLresult(LRESULT Lresult)
    {
      PACKEDLRESULTHELPER Helper;
      Helper.Lresult = Lresult;
      return HresultFromTypeSpecificValue(Helper.Structure.TypeSpecificValue);
    }
    

    This more explicit form may be more helpful if you have more than just two types to discriminate among, but in our case, the extra typing probably just confuses the matter more than clearing it up.

    This type of trick is actually quite common. For example, the LresultFromObject function uses a variation of this technique in order to pack a marshallable object and a COM error code into a single 32-bit value. It's also common in lisp systems, where it is known by the name tagged pointers.

  • The Old New Thing

    What does each country claim for its own?, greatest hits

    • 25 Comments

    A little while back, I invited readers to describe what students are taught (or end up believing) are the greatest contributions of their country. Many people strayed from the "what students are taught" part of the exercise, but I didn't mind too much as long as you were funny.

    Here are some of my favorites:

    Representing Greece is Pi, who writes,

    In Greece I was also taught that Greeks invented democracy. Other than that Greeks are supposed to have laid the groundwork for the development of philosophy, mathematics, physics, biology and pretty much every other greek named thing as a science (except for economics).

    Greeks claim to have organized the first olympic games some 2800 years ago. And back then there was some guy named Homer whose stories are still read today occasionally. He was also the template for the creation of a character in the Simpsons.

    The sad thing is that my compatriots often think they are cool by default because of these things and they don't have to accomplish anything by themselves.

    Dan reminds us that

    Sweden is pretty proud of Dynamite (Alfred Nobel), and the safety match.

    I enjoy that juxtaposition. Do you use a safety match to light your dynamite?

    For France, we have bahbar (who pseudonym is a reference to another great French contribution to humanity):

    - beheadings (just kidding)

    Rafael Vargas points out a Spanish invention that is very important to students:

    chupachups

    Rob points out that some inventions can be used for evil:

    - the first moving picture was shot in Leeds, West Yorkshire, though by a Frenchman (so we're not responsible for Charlie's Angels: Full Throttle).

    Leonardo Brondani Schenkel adds an important Brazilian contribution I had overlooked:

    Pelé

    Dan summarizes how all these claims are manifested on Wikipedia:

    "John Doe was an Italian-born[1] Jew[2] of Dutch[3] and Lithuanian[4] descent who was raised in Canada[5] and lived in Argentina for several years as an adult[6]. He is perhaps best known for inventing the belly-button-lint remover[citation required]."

    JS Bangs points out one of Romania's great contributions for which it doesn't get enough credit:

    [W]e defeated the Turks over and over, and thus kept the Ottomans from raping and pillaging their way all the way to France. So we like to take credit for the survival of Western Europe.

    At least it beats being known for providing the soundtrack to the Numa Numa video.

    Canadian Ens happens to mention "the CanadArm" in an extensive list of Canadian inventions. From what I can tell, Canadians are taught that NASA's job is to launch the CanadArm into space so it can move stuff around.

    Zheng Hua was the first of many to call out the Four Great Inventions of ancient China which students are drilled in from a young age.

    Omer van Kloeten explains the Israeli approach:

    In Israel we pretty much take credit for every invention ever made by any Jewish person in the 5000 year history of the religion.

    Also, even though it's not inventions, we celebrate the fact that we survived (which for us is the same as "won") the wars of 1947, 1956, 1967, 1969, 1973, etc. while mostly being heavily outnumbered.

    I remember it being explained to me by a Jewish friend that nearly all Jewish holidays are based on a celebration of the fact that "They didn't kill all of us!"

    Laurent points out a common theme: A country will claim credit for the deeds of an immigrant, and will also claim credit for the accomplishments of somebody who was born in the country but made the discovery while an expatriate. Heck, I wouldn't be surprised if there was a case of a country who claimed credit for somebody who merely stopped in the country to have lunch.

    A South African friend mentioned to me privately that South African are taught that their country invented the Kreepy Krauly pool vacuum cleaner and the dolos.

    Glenn S tells us what Norway is proud of. It's too long to quote here, but it's worth reading because, unlike many other people who posted lists of accomplishments, Glenn's is written with the right sense of humor, playfully acknowledging that some of the claims may not be entirely fair.

  • The Old New Thing

    What possible use are those extra bits in kernel handles? Part 1: Sentinels

    • 13 Comments

    Kernel handles are always a multiple of four; the bottom two bits are available for applications to use. But why would an application need those bits anyway?

    The short answer is extending the handle namespace. The long answer will take a few days to play out. (This series was written in response to Igor Levicki being unable to imagine "how this can save anything (in terms of performance)". Then again, who said that it had anything to do with performance? Actually, I'm surprised that my dear readers weren't familiar with the techniques described in this series. Perhaps I shouldn't have written this series and merely replied, "If you can't think of how this could be useful, then you are not my target audience." On the other hand, reader Aaargh! believes that whoever thought to make the bottom two bits of handles available to applications should receive an asswhooping.)

    But we'll start with a warm-up. If you need some sentinel values for a HANDLE, you need to make sure your chosen sentinel value will never conflict with a valid HANDLE value. If you decide that your sentinel value is something like

    // code in italics is wrong
    #define DEBUGWINDOW_HANDLE ((HANDLE)0x1234)
    

    then your program is going to start acting really strange if the kernel ever gave you handle value of 0x1234. Knowing that kernel handles are always multiples of four means that you can choose a value that isn't a multiple of four and use it as your sentinel value.

    #define DEBUGWINDOW_HANDLE ((HANDLE)0x1233)
    

    Since 0x1233 is not a multiple of four, you can rest assured that no actual kernel handle will have this value, and you can write your logging function like this:

    void LogOutput(HANDLE hOutput, LPCVOID pv, DWORD cb)
    {
     if (hOutput == NULL) {
       // logging disabled
     } else if (hOutput == DEBUGWINDOW_HANDLE) {
      AddToDebugWindow(pv, cb);
     } else {
      DWORD cbWritten;
      WriteFile(hOutput, pv, cb, NULL, &cbWritten);
     }
    }
    

    Since you can't WriteFile to a window handle, your logging function has to do something special if somebody decided that their output should go to the debug window. But if they chose to log to a normal kernel object (a file, the console, a serial port, whatever) then you can just write the data to that kernel object.

    You've already seen this before; you just didn't realize it. The special values for INVALID_HANDLE_VALUE and kernel pseudo-handles such as GetCurrentProcess are not multiples of four for exactly this reason.

    Now, sure, you could have defined your own LogHandle type and have all the logging go through that type instead of just logging to HANDLEs:

    class LogHandle {
    public:
      static LogHandle *GetDebugLogHandle();
      BOOL IsDebugWindow();
      HANDLE GetKernelHandle();
      static LogHandle *CreateFromKernelHandle(HANDLE KernelHandle);
      ~LogHandle() { }
    
    private:
      LogHandle(BOOL IsDebugWindow, HANDLE KernelHandle);
      static LogHandle DebugWindow;
    
      BOOL IsLogToDebugWindow;
      HANDLE RegularKernelHandle;
    };
    

    Throughout, your program would use pointers to LogHandles instead of actual handles, using functions like these to convert between them:

    // Does not take ownership of the handle
    LogHandle::LogHandle(BOOL IsDebugWindow, HANDLE KernelHandle)
        : IsLogToDebugWindow(IsDebugWindow)
        , RegularKernelHandle(KernelHandle)
    {
    }
    
    LogHandle LogHandle::DebugWindow(TRUE, NULL);
    
    LogHandle* LogHandle::GetDebugWindowLogHandle()
    {
      return &DebugWindow;
    }
    
    BOOL LogHandle::IsDebugWindow()
    {
      return IsLogToDebugWindow;
    }
    
    HANDLE LogHandle::GetKernelHandle()
    {
      assert(!IsDebugWindow());
      return RegularKernelHandle;
    }
    
    LogHandle *LogHandle::CreateFromKernelHandle(HANDLE KernelHandle)
    {
      return new LogHandle(FALSE, KernelHandle);
    }
    

    Or you could make everybody pass two parameters instead of one. For example, a class that went

    class SomeObject {
    public:
      SomeObject(int SomeParameter, BOOL SomeParameter,
                 HANDLE LogHandle);
    ...
    private:
      ...
      HANDLE LogHandle; // log to this handle
    };
    

    would have to change to

    class SomeObject {
    public:
      SomeObject(int SomeParameter, BOOL SomeParameter,
                 BOOL LogToDebugWindow, HANDLE LogHandle);
    ...
    private:
      ...
      BOOL LogToDebugWindow; // if TRUE, log to window
      HANDLE LogHandle; // if not logging to window, then log to here
    };
    

    Either way is an awful lot of work just to define a sentinel value. But still, at least you can avoid the need for a sentinel value by just passing more parameters. But sometimes that option isn't available. We'll look at that next time.

  • The Old New Thing

    DHS: The Television Series - the next chapter

    • 1 Comments

    Okay, so first there was the story about the planned DHS television series. And then there was the follow-up reporting that the whole thing was an investment scam and there was no such show after all. Following through on the story, it looks like the ringleader behind the scam has been sentenced to one year in prison and an accomplice to five years probation and $250 a month in restitution. The TV Squad article says retribution, which would be strange if true. But it's not. (Read the announcements of the accomplice's indictment and guilty plea.) At $250 a month, it's going to take a long time to repay the court-ordered $3.4 million in restitution.

    Two years later, the Ninth Circuit Court of Appeals ruled that the judge who imposed the one-year sentence for the ringleader failed to follow sentencing guidelines (which call for a minimum of 57 months), and ordered that he be sentenced again.

    Meanwhile, the accomplice appears to have violated the terms of her probation, so she was sentenced to ten days in jail.

    Who knows what'll happen next? This story may never reach its final chapter.

  • The Old New Thing

    Microspeak: Pencils down

    • 37 Comments

    I'm particularly fascinated by Microspeak terms which nobody actually knows the meaning of. You can defend jargon by saying that it's a shorthand way of talking in order to improve communication, but if nobody actually knows what it means, the in order to improve communication part is completely turned on its head. The Microspeak that allegedly allows people to communicate better ends up making them communicate worse.

    A colleague of mine introduced me to this new type of Microspeak. Our conversation takes place in an impromptu hallway meeting between the development manager and a few members of the team.

    Team member: "What does this mean for our milestone? I'm assuming it means that our features are code complete."

    Development manager: It means you are pencils down.

    Team member: (confused) "What does 'pencils down' mean?"

    — It means your features are done for the milestone.

    "Where did that term come from?"

    — A meeting with «senior executive».

    "Okay, so what does 'done' mean?"

    — It means you are pencils down.

    "What exactly does that mean?"

    — It means you don't have to write any new code for your features.

    "Oh, okay. So it means code complete."

    — No, that means something different.

    "How are they different?"

    — I don't know.

    "Has anyone sent any email defining what 'pencils down' means?"

    — Not that I know of.

    My colleague has yet to find anybody who can provide a definition of the term pencils down.

    This sort of confirms what my colleague Michael Grier mentioned in a comment: The intended purpose for this jargon is not to communicate with the people who work for you but to impress the people you work for.

  • The Old New Thing

    Wedding + two-year-old flower girl = wildcard

    • 22 Comments

    I was in San Francisco this weekend for a wedding. The flower girl was the bride's two-year-old niece, and when you add a two-year-old to the wedding party, you never know what's going to happen, because two-year-olds don't understand the world the same way adults do.

    During the unity candle ceremony, the two-year-old pushed her way to the table, joining the bride and groom, in order to get a front-row seat for the ritual. And then when the unity candle was lit, she tried to blow it out.

    Because when you're two years old, the only time you see people make a big deal about lighting a candle is when it's atop a birthday cake, and you know what to do with birthday candles!

  • The Old New Thing

    Why was the RAM drive removed from Windows 95?

    • 59 Comments

    Commenter Travis Owens asks, "Why was the RAM drive removed from Windows 95?"

    As with many of these types of accusatory questions, this comes with a false hidden assumption, in this case, that the RAM drive was in Windows 95 to begin with.

    Remember that Windows 95 introduced a new driver model, so any RAM drive would have had to be written from scratch. (Well, okay, you could use the old 16-bit RAM drive, but then you'd be going through the real-mode mapper, at which point your performance would go down the tubes.) As you admitted, it was a very niche feature that usually hurts but can sometimes help in very specific circumstances. Which Windows 95 file system feature would you have cut so that the geeks could have their RAM drive? Should we even be showing RAM drives to typical users, who would just turn it on without understanding its consequences, winding up with a computer that ran worse than if it had never been there?

    If you want a RAM drive, you can sit down with the DDK and write one. But the Windows 95 folks decided that adding support for SCSI hard drives would be a more effective use of the time and skills of the Windows 95 storage device driver development team—which, as I recall, consisted of three people.

    Mind you, if you really want your precious RAM drive, I've heard rumors that it's tucked away on your Windows Server 2003 CD under the names RAMDISK.IN_ and RAMDISK.SY_, and that there is a way to add a RAM drive to your Windows PE disk. (And no, I haven't heard any rumors about Windows Server 2008.)

  • The Old New Thing

    Keeping a visit a surprise after people have already guessed that you're coming

    • 16 Comments

    Last year, a friend of mine who lives out of state—let's call her Lisa—wanted to pay her family in Seattle a surprise visit, and I was enlisted as an accomplice. (Specifically, my rôle was to pick her up from the airport and take her home.) Everything was going smoothly until she made the mistake of telling one of another out-of-state relative about her plans. That relative then told a family member in Seattle, "Don't tell anybody, because it's a secret, but Lisa is making a surprise visit next week."

    And that family member told two more family members, "Don't tell anybody, because it's a secret, but Lisa is making a surprise visit next week."

    Soon this "secret" was known to half of the family. How do you recover from this?

    The solution Lisa employed would not have occurred to me. When a family member who "knew" about her surprise visit called to say, "Hey, I know you're coming," she would naturally act as if nothing of the sort was in the cards. "Where did you get that crazy idea? I just took a vacation a few weeks ago, remember? What do you think the odds are that my boss is going to let me take two week-long vacations in the span of a month?" Fine, everybody would probably think to do that.

    But then she sold the deception by calling one of the family members back a few days later. "Hey, it's Lisa. I'm at the front door. Open up, I don't have my key." With a shout of "I knew it!" the family member excitedly rushed to the front door, threw it open, and...

    Darkness there and nothing more.

    "Ha-ha! Psyche!"

    Over the next few days, she repeated this prank on everybody who "knew" that she was coming to visit. In other words, she mocked her family for believing the rumor! (That, to me, was the stroke of brilliance.)

    Upon her arrival in Seattle, I took her home, where she stood on the doorstep, took out her mobile phone, and called the house.

    "Hey, it's Lisa. I'm at the front door. Open up, I don't have my key."

    "Cut it out, Lisa, this isn't funny any more," was the response, followed by a click.

    And then Lisa rang the doorbell.

    Psyche!

    [Raymond is currently away; this message was pre-recorded.]

Page 1 of 4 (37 items) 1234