Sunday, October 18, 2009

Recommended Resources










Recommended Resources


The following websites are relevant to or provide additional information on the topics discussed in this chapter.


Websites for competing technologies to ones discussed in this chapter:


  • Eclipse Community http://www.eclipse.org/community/

  • Eclipse Directories http://www.eclipseplugincentral.com/, http://eclipse-plugins.2y.net/eclipse/

  • MyEclipseIDE.com http://www.myeclipseide.com/

  • Eclipse Technical Articles http://www.eclipse.org/articles/

  • Eclipse Platform Overview (white paper) http://www.eclipse.org/whitepapers/eclipse-overview.pdf

  • Blog about Ward Cunningham's departure from Microsoft http://blogs.msdn.com/edjez/archive/2005/10/17/so_long_Ward.aspx

  • Article: "Eclipse and HSQLDB: Embedding a Relational Database Server into Eclipse, Part 1" http://www-128.ibm.com/developerworks/opensource/library/os-echsql/?ca=lnxw961HSQLDB

  • EclipseCon conference http://www.eclipsecon.org/


Alternative technology websites:


  • Sun Microsystem's NetBeans http://www.netbeans.org/

  • JetBrain's IntellIJ http://www.jetbrains.com/

  • jEdit programmer's text editor http://www.jedit.org/












9.2 A Simple Database








 

 










9.2 A Simple Database



All of the examples in this chapter were tested with the MySQL database management system (DBMS).[3] "Setting Up the MySQL Database Used in This Book" on page 556 provides instructions on downloading and installing MySQL and an associated JDBC driver, in addition to showing you how to create and populate the database used in this chapter.



[3] An exception is the example discussed in "Executing Database Transactions" on page 411, which uses the Cloudscape database because MySQL does not implement database constraints.



The examples in this chapter access a simple database with three tables: Customers, Orders, and Accounts. Figure 9-1 shows the data contained in the Customers table.



Figure 9-1. The Customers Table





Initially, the database contains 20 customers. Each customer has a customer ID, name, phone number, and address. In the interest of simplicity, all of the (fictitious) customers reside in North America.



Figure 9-2 shows the Orders table and the Accounts table.



Figure 9-2. The Orders Table (left) and the Accounts Table (right)





Each order in the database contains an order number, an order date, a customer ID, an amount, and a description. For the sake of brevity, only the first seven customers in the database have orders. All of the customers have accounts. The Accounts table is only used by the example in "Executing Database Transactions" on page 411.



For the sake of illustration, the tables shown in Figure 9-1 and Figure 9-2 use a simplified design; for example, the Orders table keeps track of orders for single items with no notion of quantity and directly stores order amounts and product names that are localized for English. A better design would include an additional Products table that listed product IDs and prices and a modified Orders table that stored product IDs instead of descriptions and quantities instead of amounts. Moreover, you should never store localized data in a database, because you will want to easily localize that data when you display it. In general, you should adhere to the following rules when storing data in a database:





  • Store dates and times according to ISO 8601, which defines locale-independent formats for dates and times.[4] The Orders table adheres to this rule by storing dates in the format YYYY-MM-DD, as defined by ISO 8601.

    [4] See http://www.cl.cam.ac.uk/~mgk25/iso-time.html for more information about ISO 8601.





  • Store products with numeric IDs instead of localized descriptions. You can map those IDs to descriptions with separate tables in the database or with resource bundles in your application. See "Resource Bundles" on page 259 for more information about resource bundles and how you can use them to localize text.





Store prices in a locale-independent manner in a fixed representation, such as the format defined by the java.lang.Long.toString or java.lang.Double. toString methods. The Orders and Accounts tables in Figure 9-2 store their currencies in that manner.



Note that the examples in this chapter are not internationalized. The database design and the lack of internationalization in this chapter are intentional because proper database design and internationalization would obfuscate the concepts that this chapter illustrates, namely, how to use the JSTL SQL actions.














     

     


    21.3 Code Introduction

    Team-Fly
     

     

    TCP/IP Illustrated, Volume 2: The Implementation
    By
    Gary R. Wright, W. Richard Stevens
    Table of Contents
    Chapter 21. 
    ARP: Address Resolution Protocol


    21.3 Code Introduction


    There are nine ARP functions in a single C file and definitions in two headers, as shown in Figure 21.2.



    Figure 21.2. Files discussed in this chapter.


    Figure 21.3 shows the relationship of the ARP functions to other kernel functions. In this figure we also show the relationship between the ARP functions and some of the routing functions from Chapter 19. We describe all these relationships as we proceed through the chapter.



    Figure 21.3. Relationship of ARP functions to rest of kernel.


    Global Variables


    Ten global variables are introduced in this chapter, which are shown in Figure 21.4.



    Figure 21.4. Global variables introduced in this chapter.



    Statistics


    The only statistics maintained by ARP are the two globals arp_inuse and arp_allocated, from Figure 21.4. The former counts the number of ARP entries currently in use and the latter counts the total number of ARP entries allocated since the system was initialized. Neither counter is output by the netstat program, but they can be examined with a debugger.


    The entire ARP cache can be listed using the arp -a command, which uses the sysctl system call with the arguments shown in Figure 19.36. Figure 21.5 shows the output from this command, for the entries shown in Figure 18.2.



    Figure 21.5. arp -a output corresponding to Figure 18.2.


    Since the multicast group 224.0.0.1 has the L flag set in Figure 18.2, and since the arp program looks for entries with the RTF_LLINFO flag set, the multicast groups are output by the program. Later in this chapter we'll see why this entry is marked as "incomplete" and why the entry above it is "permanent."



    SNMP Variables


    As described in Section 25.8 of Volume 1, the original SNMP MIB defined an address translation group that was the system's ARP cache. MIB-II deprecated this group and instead each network protocol group (i.e., IP) contains its own address translation tables. Notice that the change in Net/2 to Net/3 from a stand-alone ARP table to an integration of the ARP information within the IP routing table parallels this SNMP change.


    Figure 21.6 shows the IP address translation table from MIB-II, named ipNetToMediaTable. The values returned by SNMP for this table are taken from the routing table entry and its corresponding ifnet structure.



    Figure 21.6. IP address translation table: ipNetToMediaTable.


    If the routing table entry has an expiration time of 0 it is considered permanent and hence "static." Otherwise the entry is considered "dynamic."





      Team-Fly
       

       
      Top
       


      Chapter 16. File Input/Output




      I l@ve RuBoard











      Chapter 16. File Input/Output



      I am the heir of all the ages, in the foremost files of time.

      �Tennyson




      A file is a collection
      of
      related data. C++ treats a file as a series of bytes. Many files
      reside on disk; however, devices such as printers, magnetic tapes,
      and communication lines are also considered files.



      This chapter discusses three different I/O packages. The first is the
      C++ I/O stream classes. This is the most commonly used I/O system and
      the one we've been using up to now. Next, we examine
      the raw I/O routines that give us direct access to the low-level I/O.
      Finally we look at the C I/O system. Although it is somewhat
      outdated, C I/O calls still appear in old code. Also, in some cases,
      the C-style I/O routines are superior to the ones provided with C++.









        I l@ve RuBoard



        Data Access Technology Survey


        3 4



        Data Access Technology Survey



        The field of data access is large and confusing. So much so that I think it is best if we start with a map to orient ourselves. Table 14-1 lists these properties of many (though by no means all) common data access technologies:




        • COM+ support. A technology is said to support COM+ if it manufactures resources through a Resource Dispenser. In the case of transactional resources, such as RDBMS connections, an RM must be provided and the Resource Dispenser must perform automatic transaction enlistment.


        • Provider-specific API. A technology is said to be provider-specific if it does not support data access through an open standard but instead allows access to only a restricted set of data sources through a proprietary interface. ODBC-based open technologies allow access to data that is backed by some type of SQL query engine. OLE DB-based technologies remove even that restriction and allow access to a much larger variety of data sources.


        • Programming model level. This is the level of abstraction at which the technology offers data access and manipulation to clients. Factors such as exposure of raw handle types, existence of a navigable object model, and exposure of memory buffers play a role in whether an access technology qualifies as high level.


        • Interface type. An access technology can be accessible only by a certain programming language, or it might offer COM+ interfaces.


        • COM+ support anchor. Many high-level data access technologies are layered on top of low-level ones. This column lists the level in the hierarchy at which the COM+ support is provided.





        Table 14-1 Database Interfaces and Their Fundamental COM+-Related Properties


















        API or Object ModelCOM+ SupportProvider- Specific APIProgramming Model LevelCOM+ Interface TypeSupport Anchor
        ODBCYesNo Very lowCNative
        OLE DBYesNo (and not SQL specific)LowCOM+, with pointersNative
        RDOYesNoHighCOM+ODBC
        DAO with ODBC- DirectYesNoHighCOM+RDO and thus ODBC)
        DAO with Jet work- spaceNoJet-connected and installable ISAM data sourcesHighCOM+N/A
        ADOYesNo (and not SQL specific)HighCOM+ (including Java through WFC)OLE DB
        JDBC (via JDBC- ODBC bridge)YesNo HighJavaODBC
        ATLYesNo (and not SQL specific)High C++OLE DB
        MFC Yes NoHighC++ ODBC
        ESQL NoNoQuery languageLanguage extension through precompilationN/A
        Microsoft SQL Server DB-LibraryNo YesVery lowCN/A
        Oracle Pro*CNoYesQuery languageC language extension through precompilation N/A
        OCI No YesVery lowCN/A
        OO4OYesYesHighCOM+Native


        Support that is not provider specific for COM+ is rooted in two technologies: ODBC and OLE DB. I will discuss OLE DB in the next section. ODBC is an older standard for interoperation with relational data stores. I do not know of an RDBMS out there today that does not support ODBC access. When compared to OLE DB, the large number of ODBC-compliant RDBMSs is the advantage of ODBC. However, there is an adapter (referred to as a provider in OLE DB lingo) that converts an ODBC data source to an OLE DB data source. Clients written to the OLE DB specification therefore can take advantage of more data sources than those written to the ODBC specification, at least on Microsoft Windows platforms.



        ODBC's driver manager is a Resource Dispenser as of version 3.0 of ODBC. This means that connections to ODBC data sources are pooled and automatically enlisted in transactions if you have version 3 or later of ODBC installed. In order for this to work, some cooperation also is needed from the ODBC driver you are using. In particular, the driver must support the attribute SQL_ATTR_ENLIST_IN_DTC in the function SQLSetConnectionAttr of the driver API.



        While the ODBC interface is too low level to allow for productive development and maintenance of modern applications, its support for COM+ is still crucial. There is a lot of source code out there that accesses the data store either directly through ODBC or through a technology layered on top of ODBC. This code exists in various forms, including C function libraries, C++ object hierarchies, and unconfigured legacy COM objects. Because ODBC now offers a Resource Dispenser, all this software can be reused without change and can benefit from transaction enlistment and connection pooling. All you have to do is invoke such legacy software in the context of a transactional COM+ object and voila!—your performance might increase, and the work done by your legacy code will be part of the distributed transaction umbrella with ACID properties orchestrated by your COM+ object.



        Technologies layered on top of ODBC include Data Access Objects (DAO), but only when used with ODBC-Direct; Remote Data Objects (RDO); Java Database Connectivity (JDBC); and the MFC classes CDatabase and CRecordset. MFC also offers a set of separate wrapper classes of DAO for C++. Among these second-tier ODBC encapsulation technologies, DAO is the oldest. Microsoft built it originally as a high-level interface for COM clients such as Microsoft Visual Basic to allow access to its Jet desktop database product. Next came RDO, which is a COM object wrapper library around ODBC. With RDO, clients could reach a greater number of local and remote data sources. ODBC-Direct was invented to make this expanded access available to existing DAO clients. In an ODBC-Direct workspace, DAO becomes just a wrapper of RDO. The two MFC ODBC wrapper classes form a very thin veneer on top of ODBC, benefiting C++ desktop applications. An advantage of the MFC approach is that it allows direct access to the ODBC API through the
        wrapper classes. It therefore yields the benefit of elevating the programming model without the loss of flexibility. JDBC remains a strong option even today because it is the only fully portable data access solution for Java. However, when portability is not a concern, the more feature-rich ADO available through the Windows Foundation Classes for Java (WFC) is a better choice.



        OLE DB has provided a Resource Dispenser since its inception. Even though the OLE DB interface is COM+ based, it is still not high level enough to make for maintainable application code. In addition, OLE DB COM+ interfaces contain constructs that cannot be consumed by all COM+ client development systems, in particular Visual Basic (see Chapter 5), and require that clients provide memory buffers with instructions to OLE DB on how to align data from providers within them. This data access idiom of data to memory location binding is certainly not new; it has existed in the earliest versions of proprietary, low-level C interfaces to relational databases. But this idiom is too error prone for business logic development in any modern programming language. OLE DB lies at the core of all data access in the development of modern applications on Windows platforms, and it is the level at which COM+ integration is provided by a Resource Dispenser. Clients normally interface with a wrapper technology on top of OLE
        DB.



        Such wrapper technologies include the ActiveX Data Objects for all COM+ clients and the Active Template Library (ATL) OLE DB consumer templates, exclusively for C++. ADO provides a navigable object model through which many (but not all) OLE DB features can be exploited. ATL offers a set of templates in the tradition of generic programming. ATL operates at roughly the same level of abstraction as ADO but nevertheless offers faster access and a number of other significant benefits that are especially important to C++ clients. ATL also allows unrestricted access to the full set of OLE DB functionality. I will discuss specific C++ data access considerations later, in the "C++ Data Access" section of this chapter.



        By far the two most popular database products used in COM+ projects are Oracle's RDBMS and Microsoft's SQL Server. Like SQL Server, Oracle offers a native OLE DB provider with version 8i, release 2.3 Oracle has implemented this provider directly on top of its low-level Oracle Call Interface (OCI). It now supports updateable and scrollable rowsets, Windows-integrated authentication, access to PL/SQL stored procedures, and rowsets returned from both stored procedures and functions, among other features. Both technologies layered on top of OLE DB—ADO and the ATL consumer templates—allow developers to implement straightforward and maintainable code that can access Oracle databases on Windows and other platforms.



        In addition to the OLE DB provider, Oracle offers Oracle Objects for OLE (OO4O), a set of COM+ objects whose interfaces can be consumed by any client. OO4O has a native Resource Dispenser and is therefore fully integrated with COM+. There are some trade-offs with using OO4O, however. On the plus side, OO4O provides access to Oracle RDBMS-specific features. Oracle also reports better performance with OO4O than the performance you would achieve when going through layers of OLE DB software. On the minus side, fewer developers are familiar with OO4O than, for example, ADO. Therefore, you might need to invest more in training over time. More significantly, OO4O is proprietary technology offering access to Oracle RDBMS products only, both now and in the future. By using OO4O, you lock yourself into using Oracle software. If you are comfortable with that, OO4O is a good option for COM+ projects.



        Like all databases, Oracle also offers lower-level interfaces. These include the C-based Pro*C and OCI. Applications theoretically can be written directly against those interfaces. A number of databases, including SQL Server, support the old Embedded SQL (ESQL) standard, a mechanism that embeds SQL statements directly into the source code of a programming language and uses a preprocessor to make those statements compilable in that language. This is exactly how Oracle's Pro*C works, although Pro*C can be used only in C and C++ source code. SQL Server also supports its own low-level flat API, called DB-Library.



        All of these data access interfaces are so low level that COM+ integration cannot be provided. No Resource Dispensers exist at that level, and services such as connection pooling, which theoretically could be implemented independent of a COM+ Resource Dispenser, are not offered either. These APIs serve as the lowest level RDBMS interface on which middle-tier services such as COM+ integration are meant to be layered. You might consider these APIs the assembly language of data access. Assembly language is the interface to a processor on which you layer programming languages. You wouldn't consider implementing your business logic in assembly, would you? I discourage the use of any data access technology that does not provide COM+ support in a COM+ project. Avoid especially the low-level interfaces because they tend to degrade source code maintainability by another level of magnitude over that caused by forcing business logic to shoulder the burden of manual transaction management or ACID behavior
        simulation. Also, as with any database interface without COM+ support, they force developers to think about controlling database connections efficiently—implementing manual connection management might require substantial effort.



        When integrating legacy code into a COM+ project, you will not have much of a choice to make when it comes to selecting a data access method. If the legacy code is ODBC based, you stand a good chance of being able to integrate it with only moderate effort. If the legacy code is not ODBC based, leveraging the benefits of distributed transactions with ACID properties and heterogeneous participants as well as gaining the advantages of centralized resource pooling could be very difficult. But what about brand-new COM+ code? Given all these options for COM+ integration, which one is best?



        I suggest these guidelines: First, recognize that the future of data access on Windows platforms rests firmly on the OLE DB foundation. OLE DB (with the help of the ODBC provider) is by far the most universal and widespread data access interface on Windows today. Expect new types of data sources to appear in the Windows market with OLE DB providers first, and perhaps no other interface at all. Second, make a choice that suits the development environment you use. The following are good combinations:




        • Use ADO in Visual Basic components.


        • Use ATL OLE DB consumer templates in Visual C++ components. I will discuss conditions for using ADO in C++ and options for doing so later in this chapter, in the "C++ Data Access" section.


        • Use ADO through WFC in J++ components. Use JDBC if Java code must be portable.


        • Consider OO4O in any development environment for access to those data items that are tied to Oracle products.





        Combining Large-Scale Structures and Distillation



        [ Team LiB ]





        Combining Large-Scale Structures and Distillation


        The concepts of large-scale structure and distillation also complement each other. The large-scale structure can help explain the relationships within the CORE DOMAIN and between GENERIC SUBDOMAINS.


        Figure 17.6. MODULES of the CORE DOMAIN (in bold) and GENERIC SUBDOMAINS are clarified by the layers.


        At the same time, the large-scale structure itself may be an important part of the CORE DOMAIN. For example, distinguishing the layering of potential, operations, policy, and decision support distills an insight that is fundamental to the business problem addressed by the software. This insight is especially useful if a project is carved up into many BOUNDED CONTEXTS, so that the model objects of the CORE DOMAIN don't have meaning over much of the project.





          [ Team LiB ]



          4.2 Understanding CVS











           < Day Day Up > 







          4.2 Understanding CVS







          CVS is an open source project; it

          started life as a set of Unix shell scripts in 1986 and came into its

          own with dedicated software in 1989. Support for CVS is available on

          many operating systems today�Unix, Linux, Windows, and others.

          For the full CVS story, take a look at http://www.cvshome.org.





          The idea behind CVS, as with any repository software, is to manage

          and record changes to source code. In fact, there are many types of

          repository software available, and some are more powerful than CVS,

          but CVS is in the most widespread use (perhaps because you can get it

          for free).













          In CVS, a module is the

          basic equivalent of an Eclipse project. Modules are represented by

          directories in CVS. Standard projects correspond to

          physical modules, while

          logical or virtual

          modules are collections of related resources.







          The files you share are stored in the CVS

          repository. When you retrieve a file from the

          repository, you check the file out. After

          you've modified the file, you commit

          the changes to check it back in and send those changes to

          the repository. If you want to refresh your own copy of a file, you

          update it from the repository.





          In general, there are two models for source code repositories:






          Pessimistic locking





          Only one developer can check out a particular file at

          once�after the file is checked out, the file is locked.

          It's possible for someone else to check out

          read-only copies of the file, but not to change it. Access is

          sequential.






          Optimistic locking





          Developers can check out and modify files freely. When you commit

          changed files, the repository software merges

          your changes automatically. If it can't

          do that by itself, it'll notify you that you have to

          resolve the issue yourself. Access is random.









          By default, CVS supports optimistic locking�although some CVS

          software also supports pessimistic locking. We'll be

          using optimistic locking here, which is what Eclipse supports.





          Because each file needs to be kept track

          of, CVS gives each file a version number automatically. Each time a

          file is committed, its version number is incremented. When you first

          commit a file, its version number is 1.1; the next time,

          it's 1.2, and so on (this can depend on your CVS

          server; some will start with 1.0). When you commit files to the

          repository, they'll get a new version number

          automatically. We'll see these version numbers in

          the various views you work with when using source control.





          When you update a file from the repository, your local version of the

          file is not overridden. Instead, Eclipse will merge your local file

          and the one in the CVS repository. If there are conflicts, the

          conflicting lines will be marked with special CVS markup indicating

          potential problems. You get the chance to modify your code to handle

          any conflicts that occurred during the merge operation, after which

          you can commit your new version of the code. Usually, updating goes

          smoothly, especially if each developer working on the project has her

          own set area to work in, but sometimes you've got to

          spend time resolving the conflicts that CVS points out to you

          manually.





          CVS also supports

          development branches. The main development

          stream of a project is called the head, and CVS has a special tag

          name called HEAD that corresponds to that stream.

          Branches are forks from the head, and, in a branch, you can make

          changes that are independent of the main development stream, such as

          when you want to work on a beta version of your project. You tag a

          branch with a name, and CVS will keep track of the branches using

          their names. We'll see how all this works later in

          this chapter.

















             < Day Day Up > 



            1.5 Architecture of Microsoft Windows OS




            < BACK  NEXT >

            [oR]

            1.5
            ARCHITECTURE OF MICROSOFT WINDOWS OS


            If you buy a CPU case with a power supply, a motherboard, a CPU, some RAM, a hard disk, a CD drive, a display card, a keyboard, and a monitor, and if you assemble everything together the right way, you get a computer. But to get the computer to do anything useful, you need software. Computer software can be roughly classified as system software and application software. System software manages the computer and related peripherals to provide support for application software, which solves real problems for users. The most fundamental system software is the operating system, which manages all of the computer's resources and provides a nice interface to the application software.



            The raw machines we are working on are very primitive and awkward to program, although they are much more powerful than their predecessors. One of the operating system's main tasks is to make the raw machine easier to program through a set of well-defined system calls. The system calls are implemented by the operating system in privileged processor mode, and they define the interface between the operating system and user programs running in nonprivileged processor mode.



            Microsoft Windows NT/2000 is a little bit different from traditional operating systems. The Windows NT/2000 operating system consists of two major parts: a privileged kernel mode part and a nonprivileged user mode part.



            The kernel mode part of the Windows NT/2000 OS runs in the privileged processor mode that has access to all CPU instructions and the full address space. On the Intel CPU, this means running in CPU privilege level 0, which has access to 4 GB of address space, IO address space, etc.



            The user mode part of Windows NT/2000 OS runs in the nonprivileged processor mode that only has access to limited CPU instructions and user address space. On the Intel CPU, the user mode code is running in CPU privilege level 3 and has access only to the lower 2 GB of per-process address space with no access to IO ports.



            The kernel mode part of the Windows NT/2000 operating system provides system services and internal processes to the user mode part of the operating system. Microsoft calls this portion Executive. It's the only entry point into the operating-system kernel. For security reasons, Microsoft provides no back doors. The major components are:




            • Hardware Abstraction Layer (HAL): a layer of code that isolates the OS kernel mode parts from platform-specific hardware differences.



            • MicroKernel: low-level operating system functions, such as thread scheduling, task switching, interrupt and exception dispatching, and multiprocessor synchronization.



            • Device Drivers: hardware device drivers, file systems, and network support that implements user I/O function calls.



            • Window Management and Graphics System: implements graphical user interface functions, such as windows, controls, drawing, and printing.



            • Executive: basic operating-system services, such as memory management, process and thread management, security, I/O, and interprocess communication.





            The user mode part of Windows NT/2000 normally consists of three components:




            • System Processes: special system processes, such as logon process and session manager.



            • Services: such as event log and schedule services.



            • Environmental Subsystems: provides native operating-system services to user programs through well-defined APIs. Windows NT/2000 supports the running of Win32, POSIX (Portable Operating System System Interface, an inter national standard for C language level API to the operating system), OS/2 1.2 (an operating system from IBM), DOS, and Win16 programs.





            Hardware Abstraction Layer


            The responsibility of the Hardware Abstraction Layer (HAL) is to provide a platform-specific support for the NT kernel, I/O manager, kernel-mode debuggers, and lowest-level device drivers. Having a HAL in the OS makes Windows NT/2000 not so dependent on a single particular hardware platform or architecture. HAL provides abstractions for device addressing, I/O architecture, interrupt management, DMA (Direct Memory Access) operations, system clocks and timers, firmware, and BIOS inter facing and configuration management.



            When Windows NT/2000 is installed, the actual module for HAL is system32\hal.dll. But actually, there are different HAL modules for different architectures; only one of them gets copied to the system directory and renamed as hal.dll. Check your Windows NT/2000 installation CD, and you will find quite a few HALs, such as HALACPI.DL_, HALSP.DL_, and HALMPS.DL_. ACPI here means Advanced Configuration and Power Interface.



            To check what's really provided by HAL on your system, use dumpbin hal.dll/export. You will find exported functions like HalDisableSystemInterrupt, HalMakeBeep, HalSetRealTimeClock, READ_PORT_UCHAR, WRITE_PORT_UCHAR, etc. Routines exported by HAL are documented in the Windows 2000 DDK, under Kernel-Mode Drivers, References, Part 1, Chapter 3.0: Hardware Abstraction Layer Routines.




            MicroKernel


            The Windows NT/2000 MicroKernel manages the main resource of the machine, the CPU. It provides support for interrupt and exception handling, thread scheduling and synchronization, multiprocessor synchronization, and timekeeping.



            MicroKernel provides its services through objected-based interfaces to its client, much like the objects and handles used in the Win32 API. The main objects provided by the MicroKernel are dispatcher objects and control objects.



            Dispatcher objects are used for dispatching and synchronization. They include events, mutexes, queues, semaphores, threads, and timers. Each dispatcher object has a state, which is either signaled or not signaled. MicroKernel provides routines accepting dispatcher object states as parameters (KeWaitxxx). Kernel-mode threads synchronize themselves by waiting for dispatcher objects or user mode objects that have embedded kernel mode dispatcher objects. For example, the Win32 user level event object has a corresponding MicroKernel level event object.



            Control objects are used to manage kernel mode operations except dispatching and synchronization, which are covered by dispatcher objects. Control objects include asynchronous procedure call (APC), deferred procedure call (DPC), interrupt, and process.



            A spin lock is a lower-level synchronization mechanism defined by the NT kernel. It's used to synchronize access to shared resources, especially in a multiprocessor en vironment. When a routine tries to acquire a spin lock, it �spins� until the lock is acquired, doing nothing else.



            MicroKernel resides in NTOSKRNL.EXE on your system, which contains both the MicroKernel and the Executive. There are two versions of MicroKernel on your installation CD: NTKRNLMP.EX_for multiprocessor systems and NTOSKRNL.EX_for single-processor systems. Although the module has an .EXE extension, it's actually a DLL. Among hundreds of exported functions from NTOSKRNL.EXE, approximately 60 of them belong to the MicroKernel. All routines supported by the MicroKernel start with �Ke.� For example, KeAquireSpinLock lets you acquire a spin lock which enables you to access shared data in a multiprocessor-safe way. KeInitializeEvent initializes a kernel event structure, which can then be used in KeClearEvent, KeReset Event, or KeWaitForSingleObjects. Kernel objects are explained in Windows DDK, under Kernel-Mode Drivers, Design Guide, Part 1, Chapter 3.0: NT Objects and Support for Drivers. Kernel routines are documented under Kernel-Mode Drivers, References, Part 1, Chapter 5.0: Kernel Routines.




            Device Drivers


            We know that the MicroKernel manages the CPU; HAL manages things like bus, DMA, timer, firmware, and BIOS. For the computer to provide real value to users, the operating system needs to interface with lots of different kinds of devices, like the video display, mouse, keyboard, hard disk, CD ROM, network card, parallel ports, serial ports, etc. The operating system needs device drivers to talk to these hardware devices.



            Most of the device drivers on Windows NT/2000 are kernel mode drivers, except Virtual Device Drivers (VDD) for MS-DOS applications and Windows 2000 user-mode printer drivers. Kernel mode device drivers are DLLs loaded into kernel address space, depending on hardware configuration and user settings.



            The Windows NT/2000 operating system's interface to device drivers is layered. User applications call API functions like Win32 CreateFile, ReadFile, WriteFile, etc. The calls are translated to calls to I/O system services provided by the Windows NT/2000 Executive. The I/O manager with the Executive sets up I/O request packets (IRPs), passing them through possibly layered drivers to physical devices.



            Windows NT/2000 defines four basic types of kernel mode drivers, which have different structures and functionality:




            • Highest-Level Driver: mostly file system drivers like the File Allocation Table (FAT) file system (inherited from DOS), NT file system (NTFS), and CD-ROM file system (CDFS) drivers, and the NT network server or redirector. File system drivers may implement a physical file system on a local hard drive; they may also implement a distributed or networked virtual file system. For example, some source-code version-control systems are implemented as a virtual file system. Highest-level drivers depend on lower-level drivers to function.



            • Intermediate Drivers: such as virtual disk, mirror, or device-type-specific class drivers, drivers in the network transport stack, and filter drivers. They provide either value-added features or per-class processing for devices. For example, there is a class driver for parallel-port communication. Intermediate drivers also depend on support from underlying lower-level drivers. There may be multiple intermediate drivers in a driver stack.



            • Lowest-Level Drivers, sometimes called device drivers: such as the PnP hardware bus driver, legacy NT device drivers, and network interface controller (NIC) driver.



            • Mini-Drivers: customization module for generic drivers. A mini-driver is not a full driver. It resides inside a generic driver, which becomes its wrapper, to customize it for specific hardware. For example, Microsoft defines a printer UniDriver, which is an actual printer driver. Printer vendors can develop a printer mini-driver which will be loaded by UniDriver to make it print to the printer provided by the vendor.





            A device driver may not always correspond to a physical device. A device driver is an easy way for programmers to write a module that can be loaded into kernel address space. With your module in kernel address space, you can do useful things that would not be possible otherwise. With well-defined file operations in the Win32 API, your user mode application can easily communicate with your kernel mode driver. For example, www.sysinternals.com provides several very useful NT utilities which use NT kernel mode device drivers to achieve registry, file system, and I/O port monitoring. Chapter 3 of this book shows a simple kernel mode driver that reads data from kernel address space. It is used extensively in analyzing Windows graphics subsystem data structures.



            While most device drivers are part of the I/O stack controlled by the Executive I/O manager and have similar structures, some device drivers are exceptions. Device drivers for the Windows NT/2000 graphics engine�for example, display driver, printer driver, video port driver�use a different structure and a direct calling sequence. Windows 2000 even allows printer drivers to run in user mode. We will say more about display drivers and printer drivers in Chapter 2.



            Most of the modules loaded into kernel mode address space are device drivers. The drivers tool in Windows NT/2000 DDK can list them in a DOS box. You will find tcpip.sys for networking, mouclass.sys for mouse, kbdclass.sys for keyboard, cdrom.sys for CD-ROM, etc.



            For complete discussions on Windows 2000 device drivers, read the Windows 2000 DDK, Kernel-Mode Drivers, Design Guide and References.




            Window Management and Graphics System


            In the earlier versions of Microsoft Windows NT, security was a major concern for OS design, so window management and graphics system ran in user address space. This led to so many performance problems that Microsoft made a major change in design by moving the windowing and graphics system from user mode to kernel mode starting with Windows NT 4.0.



            Window management supports the foundation of the Windows graphic user interface by implementing window class, window, window messaging, window hooks, window property, menu, title bar, scrollbar, cursor, virtual key, clipboard, etc. It's basically the kernel counterpart of USER32.DLL, which implements what's defined in the winuser.h portion of the Win32 API.



            Graphics system implements the real GDI/DirectDraw/Direct3D drawing, calling on a physical device or memory-based device. It relies on graphics device drivers like display drivers and printer drivers. Graphics system is the backbone for GDI32.DLL, which implements what's defined in the wingdi.h portion of the Win32 API. Graphics system also provides strong support for display drivers and printer drivers, by providing a fully featured rendering engine for several standard format bitmap surfaces. We will cover the details of the graphics system in Chapter 2.



            Windowing and the graphics system are packed into a single huge DLL, win32k .sys, of around 1.6 MB. If you look at the exported functions from win32k.sys, you can find graphics system entry points�for example, EngBitBlt,PATHOBJ_bMove To�but not window management entry points. The reason is that window management entry points are never called by other parts of the OS kernel components, while functions in graphics system need to be called by graphic device drivers. GDI32.DLL and USER32.DLL calls WIN32K.SYS through system service calls.




            Executive


            Microsoft defines Windows NT/2000 Executive as the collection of kernel-mode components that form the base Windows NT/Windows 2000 operating system. Besides HAL, Micro-Kernel, and Device Drivers, Executive also includes the Executive Support, Memory Manager, Cache Manager, Process Structure, Interprocess Communication (LPC, local procedure call and RPC, remote procedure call), Object Manager, I/O Manager, Configuration Manager, and Security Reference Monitor.



            Each component within the Executive provides a set of system services which can be used from user mode through interrupts, except Cache Manager and HAL. Every component also provides an entry point that can be called only by modules running in kernel address space.



            Executive Support provides a set of functions callable from kernel mode, which normally starts with the prefix �Ex�. Kernel memory allocation is a main functionality provided by Executive Support. Windows NT/2000 uses two dynamically growing memory regions, called pools, to manage dynamic memory allocation in kernel mode address space. The first is nonpaged pool, which is guaranteed to be resident in physical RAM all the time. Critical routines like interrupt services can use nonpaged pool without worry about generating paging interrupts. The second pool is paged pool, which is much bigger but can be swapped out to the hard disk when physical RAM is low. For example, storage for Win32 device-dependent bitmaps is allocated from paged pool using the family of ExAllocatePoolxxx routines. Executive Support also provides an efficient fixed-size block memory allocation scheme called look-aside lists, using routines like ExAllocatePagedLockasideList. Multiple look-aside lists are allocated from the pools when the system boots up. Executive Support provides a rich set of atomic operations, such as ExInterlockedAddLargeInteger, ExInterlockedRemoveHeadList, InterlockedCompareExchange, etc. Other Executive Support functionality includes fast mutex, callback, throw exception, time translation, Universally Unique Identifier (UUID) creation, etc.



            Memory Manager supports virtual memory management, balance set management, process/stack swapping during thread swapping, modified page writer, mapped page writer, system cache, page file, virtual memory to physical memory mapping, etc. Memory Manager provides routines like MmFreeContiguousMemory, MmGet PhysicalAddress, MmLockPageableCodeSection, etc. For details of Memory Manager, check Chapter 5 of Inside Windows NT, second edition.



            Cache Manager provides data caching for Windows NT/2000 file-system drivers. Cache Manager routines have the prefix �Cc.� Cache Manager exports routines like CcIsThereDirtyData and CcCopyWrite. For details, check Chapter 8 of Inside Windows NT, second edition.



            Process Structure routines are responsible for creating and terminating kernel mode system thread, process/thread notification, and process/thread query. For example, Memory Manager could use PsCreateSystemThread to create a kernel thread to manage dirty page writing.



            Object Manager manages common behavior of objects exposed from the Executive. The Executive supports creation of directory, event, file, key, section, symbolic link, and timer objects, through routines like ZwCreateDirectorObject and ZwCreate File. Once created, Object Manager routine ObReferenceObject/Ob De refer ence Count updates reference count, ObjReferenceObjectByHandle validates object handle and returns the pointer to the object's body.



            I/O Manager translates I/O requests from the user mode program or other kernel mode components into properly sequenced calls to various drivers on driver stacks. It provides a very rich set of routines. For example, IoCreateDevice initializes a device object for use by a driver, IoCallDriver sends an I/O request packet to the next-lower-level driver, and IoGetStackLimits checks the current thread's stack boundary.



            Windows NT/2000 Executive also contains a small runtime library support, which is parallel to the C runtime library but much smaller. The kernel runtime library supports double-link list, Unicode conversion, bit operations, memory, large integer, registry access, time conversion, string manipulation, etc.



            On Windows NT/2000, Executive and MicroKernel are packed into a single module NTOSKRNL.EXE, which exports more than 1000 entry points. Entry points exported by NTOSKRNL.EXE normal have distinguishing double-letter prefixes that indicate the components they belong to�for example, �Cc� for cache manager, �Ex� for Executive Support, �Io� for I/O manager, �Ke� for MicroKernel, �Ob� for object manager, �Ps� for process structure, �Rtl� for runtime library, �Dbg� for debugging support, etc.




            System Services: Native API


            The rich functionality provided by the kernel part of the Windows NT/2000 operating system is finally exposed to user mode modules through a single narrow gate, which is interrupt 0x2E on the Intel CPU. It's served by an interrupt service routine KiSystem Service, which is in NTOSKRNL.EXE but not exported. Because the service routine is running in kernel mode, the CPU switches itself to privileged execution mode auto matically, making the kernel address space addressable.



            Although a single interrupt is used, more than 900 system services are provided by Windows NT/2000 through a service index number in EAX register (on the Intel CPU). NTOSKRNEL.EXE keeps a table of system services named KiServiceTable; so does WIN32K.sys in W32pServiceTable. System service tables are registered by calling KeAddSystemServiceTable. When KiSystemService services a system service call, it checks if the service index is valid and the expected parameters accessible, then dispatches to the right routine handling a particular system service.



            Let's look at a few simple system services, using the Microsoft Visual C++ debugger with the help of the Windows 2000 debug symbol files. If you step into Win32 GDI and call CreateHalftonePalette in assembly code, you will see:





            _NtGdiCreateHalftonePalette@4:
            mov eax, 1021h
            lea edx, [esp+4]
            int 2Eh
            ret 4


            The Win32 USER function GetDC is implemented as:





            _NtUserGetDC@4:
            mov eax, 118bh
            lea edx, [esp+4]
            int 2Eh
            ret 4


            The Win32 KERNEL function CreateEvent is more complicated. Create EventA calls CreateEventW, which then calls NtCreateEvent from NTDLL.DLL. NtCreateEvent is implemented by:





            _NtCreateEvent@20:
            mov eax, 1Eh
            lea edx, [esp+4]
            int 2Eh
            ret 14h


            The Windows NT/2000 system calls are almost completely hidden from programmers. SoftICE/W from Numega provides an ntcall command that lists some kernel system calls. For more information on system calls, refer to Mark Russinovich's article �Inside the Native API,� on www.sysinternals.com. We will cover more details about GDI system calls in Chapter 2.




            System Processes


            The Windows NT/2000 operating system employs a few system processes in order to manage login, services, and user processes. You can find list of system processes in task manager, or you can use the Task List tool (tlist) which comes with Platform SDK.



            When Windows NT/2000 is running, there are basically three hierarchies of processes. The first hierarchy contains a single system process, the system idle process, which always has process id 0. The second hierarchy holds all other system processes. It starts with a process named system, which is the parent of the session manager process (smss.exe). The session manager process is the parent of the Win32 subsystem process (csrss.exe) and the logon process (winlogon.exe). The third hierarchy starts with the program manager process (explorer.exe), which is the parent of all user processes.



            Here is an annotated Windows 2000 process tree, as displayed by tlist �t command:





            System Process (0) System Idle Process
            System (8)
            smss.exe (124) Session Manager
            csrss.exe (148) Win32 Subsystem server
            winlogon.exe (168) logon process
            services.exe (200) service controller
            svchost.exe (360)
            spoolsv.exe (400)
            svchost.exe (436)
            mstask.exe (480) SYSTEM AGENT COM WINDOW
            lsass.exe (212) local security authentication server
            explorer.exe (668) Program Manager
            OSA.EXE (744) Reminder
            IMGICON.EXE (760)


            The Process Walker tool (pwalker.exe) displays more per-process information. With Process Walker, you can find that the System Idle Process has a single thread with 0 as starting address. It may well be a mechanism for the operating system to account for idle time instead of being a real process. The System Process has a valid starting address in kernel address space and dozens of threads all with a starting address in kernel address space. So the System Process is also the home for kernel mode system threads. If you translate the thread starting addresses in the System Process to symbolic names, you will find interesting names like Phase1Initialization, ExpWorkerThread, Exp Worker Thread BalanceManager, MiDereferenceSegmentThread, MiModified Page Writer, Ke BalancedSetManager, FsRtlWorkerThread, etc. Although all the threads listed here are created by the Executive, other kernel components can create kernel threads, too. Both the Idle Process and the System Process are pure kernel mode components which do not have any modules in user mode address space.



            Other system processes like session manager, logon process, etc., are user mode processes, started from a PE format execution file. For example, you can find smss.exe, csrss.exe, and winlogon.exe in the system directory.




            Services


            Microsoft Windows NT/2000 supports a special application type known as a service program, which is normally a console program controlled by the Service Control Manager (SCM). A service program can provide several services. Unlike normal user programs, services can be scheduled to start automatically at system boot time before user logon.



            Several service programs are started when the operating system boots up. To list the service programs and services currently running on your system, use Task List (tlist .exe) with the �s option. Here is a sample list of service programs and services:





            200 services.exe Svcs: AppMgmt, Browser, dmserver,
            Dnscache, EventLog, LanmanServer,
            LanmanWorkstation,
            LmHosts, Messenger, PlugPlay,
            ProtectedStorage, seclogon,
            TrkWks
            212 lsass.exe Svcs: PolicyAgent, SamSs
            360 svchost.exe Svcs: RpcSs
            400 spoolsv.exe Svcs: Spooler
            436 svchost.exe Svcs: EventSystem, Netman, NtmsSvc,
            RasMan, SENS, TapiSrv
            480 mstask.exe Svcs: Schedule


            Of particular interest to this book is the spooler service, which handles print jobs on a local machine and printing to printers over a network. We will talk more about spooler service in Chapter 2.




            Environment Subsystems


            When Windows NT was initially designed, not many Win32 programs had been written to run on it. So Microsoft thought it was important for Windows NT to support environments to run DOS programs, Win16 programs, OS/2 programs, POSIX (UNIX-style interfaces) programs, and Win32 programs on the same operating system. Windows NT/2000 provides different environment subsystems to run these totally different kinds of programs.



            An environment subsystem is a set of processes and DLLs that exposes a subset of the operating-system services to application programs written for the specific subsystem. Each subsystem has a single subsystem process, which manages interaction with the operating system. The DLLs are mapped into application processes to allow interfacing with the subsystem process or direct interaction with the kernel portion of the operating system through OS system service routines.



            The Win32 environment subsystem is gradually becoming the major subsystem supported by the Windows NT/2000 family of operating systems. Its environment subsystem process (csrss.exe) is used to handle all window management and graphic display in the user address. Win32 application programs have to issue local procedure calls to its subsystem process for window management and display, which causes performance problems. Starting from Windows NT 4.0, Microsoft moved the major portion of the Win32 environment subsystem into a kernel mode DLL, win32k.sys, together with all the graphical device drivers. Win32 subsystem DLLs should be very familiar to Windows programmers. We have KERNEL32.DLL, which handles virtual memory, I/O, heap, process, thread, and synchronization; USER32.DLL, which handles window management and message passing; GDI32.DLL, which handles graphic display and printing; ADVAPI32.DLL, which manages registry, etc. Win32 subsystem DLLs allow direct access to the OS kernel system services, and provide extra useful features not provided by OS system services. The enhanced metafile (EMF) is an example of a Win32 API feature not directly supported by win32k.sys.



            The remaining two environment subsystems, OS/2 subsystem and POSIX subsystem, rely on the Win32 environment subsystem to run, although they used to be the major players in the initial design of Windows NT.



            The Win32 environment subsystem now becomes a vital part of the operating system, which is always running. The OS/2 and POSIX subsystems are started only when needed by a program requiring such subsystems.







            < BACK  NEXT >





            Section 1.4.&nbsp; Injecting Dependencies with Spring










            1.4. Injecting Dependencies with Spring



            You're almost through with the setup for Spring.
            It's time to download it and put it into action. In
            this lab, you'll replace the
            RentABikeAssembler object with Spring.



            When I started using Spring instead of J2EE, it changed my life. I
            got more productive. My code got easier for me to write, and easier
            for my customers to understand. Since it was simpler, it often
            executed faster. It was also infinitely easier to refactor. In fact,
            I wrote a book about the value of lightweight frameworks like Spring,
            called Better, Faster, Lighter Java
            (O'Reilly).




            1.4.1. How do I do that?



            You'll first have to
            download
            Spring. Go get it at http://www.springframework.org. That will
            point you to sourceforge, where you'll get the best
            version for your platform. We used Version 1.1. You will need to add
            a new folder to your project, war\WEB-INF\lib,
            and put the Spring libraries there (everything in the
            \dist folder of your Spring distribution).



            Moving a well-designed plain-ordinary-Java-object
            (POJO) application to Spring is straightforward. It only takes three
            steps:



            • Refactor your code to take advantage of dependency injection. Model
              objects are beans, and services are aspects. Usually,
              you'll only have beans.

            • Remove the code that instantiates the objects and sets dependencies.

            • Build a configuration file describing your beans and aspects.

            • Access your code through Spring.


            Since our individual parts are already built to take advantage of
            dependency injection, moving to Spring is an easy exercise. We simply
            replace our assembler with a Spring version, and provide a
            configuration file which will go in the
            \war\WEB-INF folder.



            Example 1-9 shows the configuration file.




            Example 1-9. RentABike-context.xml

            <?xml version="1.0" encoding="UTF-8"?>
            <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
            "http://www.springframework.org/dtd/spring-beans.dtd">

            <beans>

            <bean id="rentaBike" class="ArrayListRentABike">
            <property name="storeName"><value>"Bruce's Bikes"</value></property>
            </bean>

            <bean id="commandLineView" class="CommandLineView">
            <property name="rentaBike"><ref bean="rentaBike"/></property>
            </bean>

            </beans>





            And Example 1-10 is the new assembler that replaced
            the old RentABikeAssembler.




            Example 1-10. RentABikeAssembler.java

            import org.springframework.context.support.ClassPathXmlApplicationContext;

            public class RentABikeAssembler {
            public static final void main(String[] args) {
            ClassPathXmlApplicationContext ctx = new
            ClassPathXmlApplicationContext("RentABikeApp-context.xml");
            CommandLineView clv =
            (CommandLineView)ctx.getBean("commandLineView");
            clv.printAllBikes( );
            }
            }







            1.4.2. What just happened?



            You may be scratching your head and wondering what the big deal is
            all about. These tiny improvements in architecture will have profound
            implications throughout the lifecycle of this application.
            You'll see the benefits almost immediately. I
            won't harp on them here. Instead,
            let's talk about what's happening
            under the covers.





            1.4.3. What about...



            ...Pico, Hive Mind, and Avalon? These are all lightweight containers.
            Each of them has strengths and weaknesses. Neither Avalon nor Hive
            Mind have the critical mass that you'll want out of
            a container, especially if you want services that interoperate. Right
            now, Spring and Pico have the most market share. People tend to use
            Pico if they want a standalone container, but Spring has the most
            comprehensive support for additional services, such as declarative
            transactions and a rich persistence strategy.












              21.6. Boost Libraries in Technical Report 1 (TR1)



              21.6. Boost Libraries in Technical Report
              1 (TR1)


              Several Boost libraries have been
              accepted as part of Technical Report 1 (TR1). TR1 is a description of proposed
              changes and additions to the C++ Standard Library. GCC (GNU Compiler Collection)
              provides a partial implementation of TR1; the site gcc.gnu.org/onlinedocs/libstdc++/ext/tr1.html overviews the TR1 features currently supported.
              GCC, which is a part of the GNU project, provides free compilers for several
              programming languages, including C, C++ and Java. Boost has also released a
              subset of libraries that implement most of the TR1 functionality—this subset is
              included in the latest release of the Boost libraries. Here we introduce the
              Boost libraries that the corresponding TR1 extensions are based on. Later, we
              provide code examples for some of the libraries.


              Array[2]



              [2] Documentation for
              Boost.Array, Nicolai Josuttis, www.boost.org/doc/html/array.html.


              Boost.Array is a wrapper for fixed-size arrays that enhances built-in
              arrays by supporting most of the STL container interface described in Section
              20.1. Boost.Array allows you to use fixed-size
              arrays in STL applications rather than vectors
              (dynamically sized arrays), which are not as efficient when there is no need for
              dynamic resizing.


              Bind[3]



              [3] Documentation for
              Boost.Bind, Peter Dimov, www.boost.org/libs/bind/bind.html.


              Boost.Bind
              extends the functionality of the standard functions std::bind1st and
              std::bind2nd. The bind1st and bind2nd functions are used to adapt binary functions (i.e.,
              functions that take two arguments) to be used with the standard algorithms which
              take unary functions (i.e., functions that take one argument).
              Boost.Bind enhances that functionality by
              allowing you to adapt functions that take up to nine arguments.
              Boost.Bind also makes it easy to reorder the
              arguments passed to the function using placeholders.


              Function[4]



              [4] Documentation for
              Boost.Function, Douglas Gregor, www.boost.org/doc/html/function.html.


              Boost.Function allows you to store function pointers, member-function
              pointers and function objects in a function wrapper. You can also store a
              reference to a function object using the ref and
              cref functions added to the <utility> header. This allows you to avoid expensive copy
              operations. A boost::function can hold any
              function whose arguments and return type can be converted to match the signature
              of the function wrapper. For example, if the function wrapper was created to
              hold a function that takes a string and returns a string, it
              can also hold a function that takes a char* and returns a
              char*, because a char* can be converted to a string,
              using a conversion constructor.


              Mem_fn[5]



              [5] Documentation for
              Boost.Mem_fn, Peter Dimov, www.boost.org/libs/bind/mem_fn.html.


              Boost.mem_fn
              enhances the std::mem_fun and std::mem_fun_ref functions.
              mem_fun and mem_fun_ref take a
              pointer to a member function or a reference to a member function, respectively,
              and create a function object that calls that member function. The member
              function can take no arguments or one argument. Function objects are often used
              with the Standard Library for_each function
              and other STL algorithms. We discussed function objects in Section
              20.7. Boost.mem_fn enhances the standard
              functions by allowing you to create the function object with a pointer,
              reference or smart pointer (Section
              21.8) to a member function. It also allows the
              member functions to take more than one argument. mem_fn is a more flexible version of mem_fun and
              mem_fun_ref.


              Random[6]



              [6]
              Jens Maurer, "A Proposal to Add an Extensible Random Number Facility to the
              Standard Library," Document Number N1452, April 10, 2003, www.open-std.org/jtc1/sc22/wg21/docs/papers/2003/n1452.html.


              Boost.Random allows you to create a variety of random number generators
              and random number distributions. The std::rand and
              std::srand functions in the C++ Standard Library
              generate pseudo-random numbers. A pseudo-random
              number generator
              uses an initial state to produce
              seemingly random numbers—using the same initial state produces the same sequence
              of numbers. The rand function always uses
              the same initial state, therefore it produces the same sequence of numbers every
              time. The function srand allows you to set the
              initial state to vary the sequence. Pseudo-random numbers are often used in
              testing—the predictability enables you to confirm the results.
              Boost.Random provides pseudo-random number
              generators as well as generators that can produce nondeterministic random numbers—a set of random numbers that can't be predicted. Such
              random number generators are used in simulations and security scenarios where
              predictability is undesirable.


              Boost.Random also allows you
              to specify the distribution of the numbers generated. A common distribution is
              the uniform distribution, which assigns the same probability to each number
              within a given range. This is similar to rolling a die or flipping a coin—each
              possible outcome is equally as likely. You can set this range at compile time.
              Boost.Random allows you to use a distribution in
              combination with any random number generator and even create your own
              distributions.


              Ref[7]



              [7]
              Documentation for Boost.Ref, Jaakko Järvi, Peter Dimov, Douglas Gregor and Dave
              Abrahams, www.boost.org/doc/html/ref.html.


              The Boost.Ref library provides reference wrappers that enable you to
              pass references to algorithms that normally receive their arguments by value.
              The reference_wrapper object contains the reference and allows the
              algorithm to use it as a value. Using references instead of values improves
              performance when passing large objects to an algorithm.


              Regex[8]



              [8] Documentation for
              Boost.Regex, John Maddock, www.boost.org/libs/regex/doc/index.html.


              Boost.Regex provides support for processing regular
              expressions in C++. Regular expressions are used to match specific character
              patterns in text. Many modern programming languages have built-in support for
              regular expressions, but C++ does not. With Boost.Regex, you can search for a particular expression in a
              string, replace parts of a string that match a regular
              expression, and split a string into tokens
              using regular expressions to define the delimiters. These techniques are
              commonly used for text processing, parsing and input validation. We discuss the
              Boost.Regex library in more detail in Section
              21.7.


              Result_of[9]



              [9] Documentation for
              Boost.Result_of, Doug Gregor, www.boost.org/libs/utility/utility.htm#result_of.


              The class template result_of can
              be used to specify the return type of a call expression—that is, an expression
              that calls a function or calls the overloaded parentheses operator of a function
              object. The call expression's return type is determined based on the types of
              the arguments passed to the call expression. This can be helpful in templatizing
              the return types of functions and function objects.


              Smart_ptr[10]



              [10] Documentation for
              Boost.Smart_ptr, Greg Colvin and Beman Dawes,
              www.boost.org/libs/smart_ptr/smart_ptr.htm.


              Boost.Smart_ptr defines smart pointers that help you manage
              dynamically allocated resources (e.g., memory, files and database connections).
              Programmers often get confused about when to deallocate memory or simply forget
              to do it, especially when the memory is referenced by more than one pointer.
              Smart pointers take care of these tasks automatically. TR1 includes two smart
              pointers from the Boost.Smart_ptr library. shared_ptrs handle lifetime
              management of dynamically allocated objects. The memory is released when there
              are no shared_ptrs referencing it. weak_ptrs
              allow you to observe the value held by a shared_ptr without assuming
              any management responsibilities. We discuss the Boost.Smart_ptr library
              in more detail in Section
              21.8.


              Tuple[11]



              [11] Documentation for
              Boost.Tuple, Jaakko Järvi, www.boost.org/libs/tuple/doc/tuple_users_guide.html.


              A tuple is a set of
              objects. Boost.Tuple allows you to create sets of objects in a generic way and
              allows generic functions to act on those sets. The library allows you to create
              tuples of up to 10 objects; that limit can be extended. Boost.Tuple is
              basically an extension to the STL's std::pair class template. Tuples are often used to return
              multiple values from a function. They can also be used to store sets of elements
              in an STL container where each set of elements is an element of the container.
              Another useful feature is the ability to set the values of variables using the
              elements of a tuple.


              Type_traits[12]



              [12] Documentation for
              Boost.Type_traits, Steve Cleary, Beman Dawes,
              Howard Hinnant and John Maddock, www.boost.org/doc/html/boost_typetraits.html.


              Boost.Type_traits library helps abstract the differences
              between types to allow generic programming implementations to be optimized. The
              type_traits classes allow you to determine
              specific traits of a type (e.g., is it a pointer or a reference type, or does
              the type have a const qualifier?) and
              perform type transformations to allow the object to be used in generic code.
              Such information can be used to optimize generic code. For example, sometimes it
              is more efficient to copy a collection of objects using the C function
              memcpy rather than by iterating through
              all the elements of the collection, as the STL copy
              algorithm does. With the Boost.Type_traits
              library, generic algorithms can be optimized by first checking the traits of the
              types being processed, then performing the algorithm accordingly.


               


              Chapter 4.&nbsp; Compilation: The Basics










              Chapter 4. Compilation: The Basics


              Before continuing, let's review how computer programs get made. If you're coming to Xcode from long experience with GNU Make or another development environment, this discussion will be extremely familiar to you.


              Programmers use source code to specify what a program does; source code files contain a notation that, although technical and sometimes cryptic, is recognizably the product of a human, intended in part for humans to read and understand. Even the most precise human communication leaves to allusion and implication things that a computer has to have spelled out. When the Linrg tool refers to the local variable slope, for example we cared only that the name slope should consistently refer to the result of a particular calculation; the central processor of a computer running Linrg, however, cares about the amount of memory allocated to slope, the format by which it is interpreted, how memory is reserved for the use of slope and later released, that the memory should be aligned on the proper address boundary, that no conflicting use be made of that memory, and, finally, precisely how the address of slope is to be determined when data is to be stored or retrieved there. The same issues have to be resolved for each and every named thing in a program.












              Elements of Interception


              3 4



              Elements of Interception



              COM+ performs all its transparent magic through a process known as interception. Calls from clients to objects are intercepted by an entity that implements the same interfaces as the object but adds services before passing the call to the object. It is easy to see how such a technique might regulate concurrency through locking: the interceptor could acquire a critical section before passing on the call. In fact, you might have used such a technique years ago while synchronizing access to a library that was not thread safe. We will examine interception implementation in more detail in a moment. For now, suffice it to say that COM+ always implements concurrency management through some form of interception. Because interception is such a central concept in COM+, we will step outside the bounds of examining concurrency management in this section to take a broader look at how interception is used in COM+ middleware.



              Concurrency vs. Reentrancy



              Do not confuse an object's tolerance for being accessed by multiple threads—perhaps simultaneously—with its ability to handle calls back to the object by a logical thread that was used to make a call from the object. If object A can handle a call by object B, which object A is currently in the process of calling (either directly or indirectly), object A is said to be reentrant.



              It is not the place of object middleware to regulate reentrancy. Only your object design can ensure that your object will not be called back while waiting for an outgoing call to return, in the event it cannot handle such a call. Your design might need to provide this assurance because blocking a reentering call would guarantee deadlock. In fact, COM+ puts some effort into ensuring that callbacks are always serviced and therefore never result in deadlock. This implementation is more challenging than you might think, since a callback can occur on threads other than the one waiting for the return of the method invocation. We will look at this issue more closely when we examine locking (in its own section) later in this chapter.



              Interception Implementation



              The concept of interception is quite simple: an arbitrary object is wrapped so that some amount of work can be done before and after calling a method in an interface the object supports. The most familiar example of an interceptor is COM's venerable proxy. A proxy (or more precisely, proxy manager) acts like the object it represents, but its job is to marshal all call parameters for transmission through the channel to the stub, which will unmarshal the parameters, reassemble the stack frame, and then make the call to the actual object. Yet the traditional proxy (as opposed to the newer, stubless proxy) is not a perfect example of an interceptor, since it is not generic. The MIDL compiler generates functions that mirror each method of the interface the proxy wraps.



              The generality of a true interceptor presents a challenge during implementation. A generic interceptor does not know the shape of the interface it will wrap once it is compiled. This lack of information at compile time creates a very interesting set of difficulties.



              The Language Problem



              Procedural high-level languages, including C and C++, generally are not capable of calling a function with an unknown parameter list. By using the ellipsis and va_ set of functions, you can implement a function that does not know what parameters it will be called with at compile time. However, you cannot tell the compiler to make a call to a function and simply pass the parameters that were passed to the function making the call.



              This problem can be overcome only by using a piece of assembly language to make the call from the interceptor to the wrapped object. Essentially this assembly code must make the call to the target function in the wrapped object while leaving the stack frame unchanged. However, the C compiler will already have altered the stack frame with a standard function prologue segment, which lets you access local variables. Microsoft Visual C++ offers the __declspec(naked) storage class attribute, which will prevent function prologue and epilogue generation. Obviously, implementing naked functions is difficult and, along with the necessary assembly segment, requires a thorough understanding of the processor architecture for which you compile your code.



              The Failure Problem



              COM+ interface methods always use the __stdcall calling convention. This convention has the callee, not the caller, clean up the stack before returning from the function. This is no problem if you actually can make the call to the object for which you are intercepting, but what if your interception task fails? What if you would rather not make the call under a certain set of circumstances, or what if the object you want to dispatch the call to is unavailable? Now you are responsible for removing parameters from a stack frame whose shape you don't even know.



              Of course, there are ways to get around this problem. For example, you might require the caller to tell you the combined size of all parameters. However, this approach is somewhat awkward and makes your interception hardly transparent. Or you might try to derive the combined parameter size by querying the ITypeInfo interface of the target object. Of course, the object might not support this interface, in which case you could attempt to create a stub for the interface you want to wrap, and interpret its CInterfaceStubVtbl structure, defined in RpcProxy.h. And your interceptor must create a stub and interpret the structure before your wrapper function is called, since determining stack frame size inside the wrapper cannot tolerate failure. By now you've probably guessed that doing this will require significant effort.



              The Post-Processing Problem



              Your interception task might require work before making the call to the wrapped object, as well as afterward. This means that after the wrapped function is complete, it must return to your wrapper rather than that wrapper's caller. Therefore, you need to change the return address on the stack so that it points within the wrapper function. But how do you remember the address of the caller to which you must return after you finish post processing? After all, there is no place on the stack to store this information.



              You can save the final return address in thread local storage (TLS). But allocating a new TLS slot can be expensive if interceptor calls nest on the same thread, and you might find yourself running out of slots. Therefore, you should manage a stack of return addresses via a single slot, instead of allocating a new slot for each function invocation.



              Make no mistake: implementing generic interception is very challenging and is nonportable. Even if you never need to implement an interceptor in your own software,2 understanding the issues of the task gives you a better grasp of what is happening inside the COM+ middleware, if not sympathy for the developers who created it.



              The Apartment



              The COM+ apartment model lets objects make a statement regarding their thread affinity. An in-process server makes this statement declaratively by setting the ThreadingModel named value under the InprocServer32 key under the class ID key in the registry, generally at registration time. Before the MTS COM era, the apartment defined an object's innermost execution context—that is, the COM run-time environment would never inject itself between objects that resided in the same apartment. COM+ allows each object to choose from one of the following apartment types:




              • The single-threaded apartment (STA). An object created in this apartment is entered only by the unique thread that comprises the apartment. A ThreadingModel value of Apartment indicates that an object requires instantiation within an STA. A user thread can create such an apartment by calling CoInitialize or CoInitializeEx with COINIT_APARTMENTTHREADED. Calls into the apartment are received by the channel via window messages; therefore, each user thread that creates this apartment type must service a message loop until no objects remain in the apartment. Otherwise, calls to objects in the apartment cannot be serviced and will block. Since STA objects can be entered only by their creating thread, no concurrency can exist within them. Microsoft Windows NT and Windows 2000 will place a new STA object in the system STA—unless the caller resides in an STA itself, in which case the new object will be co-located in the caller's apartment. The system STA is an apartment
                owned by a thread created by the COM/COM+ library. The library arranges for this thread to service a message loop for the lifetime of the process. At most, one system STA will be created per process. The system STA is the only STA that can be created in a process by COM preceding the MTS era.


              • The main single-threaded apartment. By omitting the ThreadingModel named value or setting it to Single, an in-process server's object indicates that the object requires instantiation within the unique main STA of a process. This main STA is formed by the first user thread that creates an STA. If no STA exists within a process yet, the system STA will become the main STA.

                Legacy in-process servers sometimes use this setting because their objects were written under the assumption that they could share global in-process server data without requiring locking. An ActiveX DLL created by Microsoft Visual Basic also supports this setting.3 The setting is not recommended for new projects because it can lead to contention among all the COM objects that are forced to share the same thread.



              • The multithreaded apartment (MTA). Objects with ThreadingModel set to Free are created in this apartment. There is only one such apartment per process, and user threads can join it by calling CoInitializeEx with COINIT_MULTITHREADED. Such threads need not service a message loop and can terminate at any time. Objects in the apartment receive calls on arbitrary threads created by the Remote Procedure Call (RPC) run-time library. This apartment type does not imply synchronization, and objects running under COM prior to MTS as well as unconfigured COM+ objects must prepare for concurrent entry by callers. Visual Basic 6 objects cannot use this setting.


              • The thread-neutral apartment (TNA). This apartment type is new in COM+. Its objects are entered directly by the caller's thread, whether it is an STA thread or belongs to the MTA. Threads cannot belong to this apartment; they merely enter it for the duration of a call sequence. Like the MTA, this apartment type does not imply synchronization. Unconfigured COM+ objects must prepare for concurrency. Visual Basic 6 does not support this setting.





              An object can also declare ThreadingModel equal to Both, in which case it will be created in the apartment of its caller. The value Both is used for historical reasons: it originated at a time when COM supported only two apartment types. An unconfigured component using this setting might experience concurrency, as its creator might be an MTA thread or a TNA object. The primary motivation for using this setting is to eliminate an apartment boundary between an object and its instantiator.



              Table 4-1 illustrates which apartment COM and COM+ will choose for instantiation of a new unconfigured object, given that object's ThreadingModel and the instantiating thread's apartment membership. (Of course, the TNA row and columns are relevant to COM+ only.)



              Table 4-1 Instantiation Apartment Selection




















              InstantiatorSingleApartmentFreeNeutralBoth
              Main STA Main STA Main STA MTATNAMain STA
              Secondary STAMain STA Caller's STAMTATNACaller's STA
              MTAMain STASystem STAMTATNAMTA
              On loan to TNAMain STASystem STAMTATNATNA





              Whenever a thread invokes a method on an object across an apartment boundary, the invocation is intercepted by the object's proxy, routed via the channel, and then delegated to the object by the stub on the other side of the channel. The COM+ middleware performs three important functions when intercepting method calls across apartments:




              • Since apartment switches that do not involve the TNA imply thread switches, the proxy and stub are responsible for packaging the stack frame and reassembling it on the object's thread.


              • Notifying the target apartment about incoming COM+ traffic can involve sending window messages or some other interprocess communication (IPC) mechanism. This notification is the channel's job. Crossing an apartment boundary that necessitates a thread switch imposes significant overhead. A ThreadingModel value of Both will eliminate this overhead between instantiator and object, and the TNA will eliminate the overhead in all cases for subsequent callers from other apartments and for the instantiator.


              • Object references from the originating apartment are converted to proxies in the target apartment. This prevents a thread in the target apartment from crossing into the object's apartment without interception. The new proxy is always directly connected to its object's apartment and does not detour through the caller's apartment unless the object resides in the caller's apartment.





              Making a call to an object in the TNA within the same process never requires switching to a different thread. Only the last item in the previous list needs to be performed by an interceptor guarding access to the in-process TNA. Such an interceptor is sometimes called a lightweight proxy. Compared to the overhead of a thread switch, a lightweight proxy is very fast. But the lightweight proxy still needs to perform object reference conversion, as shown in the graphic at the top of the next page. For this reason, a TNA interceptor needs access to the proxy/stub DLL of the interface it is encapsulating, or in the case of a type library-marshaled interface, to its type library. Such access is also necessary for the interceptor to handle the failure problem inherent in general interception.



              One of the consequences of a user thread's inability to join the TNA is that the thread always incurs the overhead of at least a lightweight proxy when creating or calling an object within the thread-neutral apartment. This is not necessarily true for creating or accessing an object in one of the other apartment types. If a user thread performing a watchdog activity or other periodic task needs frequent access to COM+ objects, placing these objects in the TNA could impede performance. However, such situations are relatively rare and in most architectures are confined to "system" type objects rather than objects containing business logic.








              Managing STA Concurrency



              Since only a unique thread can enter an object created in a single-threaded apartment, an STA object naturally avoids concurrency. However, method invocations are serialized not only for an individual object in the apartment, but also for all objects created in the apartment, since they all share the same thread. Therefore, it can be important to actively control which STA will be chosen for an object instantiation; otherwise, you might end up with large groups of objects blocking one another from executing, even though such concurrency in separate objects would be perfectly all right. Often the only thing preventing two objects from executing concurrently is their need to access shared global data. Such data access frequently is better controlled by using explicit locking strategies (described later in this chapter), rather than using the somewhat heavy-handed approach COM+ has for invocation serialization. Given that a group of objects needs to reside in an STA, the question becomes how many objects
              should share one apartment for optimal concurrency. The pressures to be balanced include each individual object's responsiveness as well as the amount of threads the system can handle before the thread scheduler's overhead becomes too significant.



              Under normal circumstances, the instantiator of an object implicitly selects the object's apartment. But it is typical to see a client take control of in-process server concurrency by first creating a single-threaded apartment and then issuing the instantiation call from this new apartment. This approach can be effective in situations where the client process ultimately is aware of how server objects are being used, and how their concurrency can best be exploited.



              MTA-Bound Call Interception

              Since the introduction of the multithreaded apartment on Windows NT, COM developers frequently have asked why a thread executing within an STA object cannot call directly into the multithreaded apartment. With the addition of the thread-neutral apartment under COM+, the question becomes why a thread that originally created a single-threaded apartment cannot enter the multithreaded apartment, whether executing in its own apartment at the time of the call or on loan to the thread-neutral apartment when the call is dispatched. The question generally acknowledges that you would still need lightweight interception to prevent MTA threads from crossing over into the calling apartment when accessing object references passed as interface method arguments, but such interception should be feasible without incurring the expensive thread switch. After all, MTA objects are written to be entered by arbitrary threads and therefore it doesn't matter whether a calling thread actually belonged to a single-threaded
              apartment.

              The justification for switching threads involves an STA thread's need to service a message loop somewhat frequently, and with the expectation of the MTA object developer. This justification is not so much connected to the mechanism the channel uses when making an outbound call from the MTA: normally this mechanism involves blocking the calling thread until the method invocation returns, but the channel has the power to discover a thread's native apartment membership and can enter a message loop waiting for call return if the calling thread is an STA thread, even when making a call from an MTA object. The real problem is that the MTA programming model allows the object developer to unconditionally block threads and to do so for arbitrary periods of time—usually for synchronization purposes or when waiting to access a resource. Therefore, within MTA object implementations, you frequently will find calls to EnterCriticalSection, WaitForSingleObject, and
              WaitForMultipleObjects
              that have long time-outs. The STA thread must protect itself from running into code like this; it does so by waiting in a message loop at the apartment boundary and having an MTA thread block in the synchronization APIs instead.





              In other situations, the server is best equipped for arranging object-to-apartment distribution. This includes cases where server objects do not run in the client's process space, making the client unable to affect target apartment selection. At first it might appear that the server is unable to influence this apartment selection, since COM+ makes all the choices. This impression might have been furthered by my choice of words: to simplify the discussion, I always speak of the apartment in which the object will be created. In fact, the object's class factory—not the object itself—will be instantiated in the apartment by the COM+ library. Therefore, it is up to the class factory to create the actual object. Normally the class factory creates the object inside its own apartment, but it does not have to. The indirection of the class factory in the object creation process gives servers the ability to take control over an STA object's target apartment.



              Visual Basic allows developers to multiplex objects, created externally or internally by means other than New, across a thread pool of a fixed size on a per-project basis. Alternatively, developers can specify that each object created in this manner should be located in a new apartment. These options are available to local servers only. Of course, C++ developers implementing their factories manually can do whatever they like to control target apartment selection or creation. But when using the Active Template Library (ATL), you can achieve multiplexing across a pool by using the macro DECLARE_CLASSFACTORY_AUTO_THREAD. By default, the size of the thread pool used by this mechanism will be four times the number of processors of the system on which your code executes. This dynamic way of determining pool size makes more sense than the Visual Basic approach, because a pool that is too large will degrade performance as much as a pool that is too small will cause insufficient object concurrency.



              The Context



              With MTS, COM started taking on additional services that needed to be handled at the level of the interceptor, including transaction support and role-based security. Such services have expanded under COM+ while becoming more configurable. We will take a survey of these interception services in a moment.



              Before the days of MTS, COM interception was married to the concept of the apartment, and apartments are about threads. But uniting the new services with the thread infrastructure did not make sense, so a tighter execution scope for COM objects was needed. This new innermost execution scope is called the context. An object now resides within a context, and an apartment bounds a context. Extended COM+ run-time services are performed by interceptors, which must exist between any two objects that do not reside in the same context. As long as the interception occurs between contexts in the same apartment or on a TNA-bound call, these interceptors generally are as efficient as any lightweight proxy that does not require a thread switch. But recall that the interceptor still needs access to your proxy/stub DLL or type library, for the same reason a lightweight proxy requires this access. Two objects with similar configurations and the same interception needs may share the same context, but certain services require that an object be created in an entirely new context. Threading model setting permitting, an unconfigured object, which, by its very nature does not ask for and is unaware of extended services, is always co-located in its instantiator's context.



              Of particular interest is a COM+ service specifically dedicated to managing concurrency in an object. I have to admit that I was initially quite confused by this service. Having worked with the apartment model for years, the concepts of thread affinity and synchronization had become indistinguishable in my mind. But upon later reflection, I realized that while a relationship between the concepts exists, the concepts for the most part are independent. There is no reason, after all, to not serialize access to either a multithreaded or a thread-neutral object. In Essential COM (Addison-Wesley, 1998), Don Box speculated about a new apartment type he called the rental-threaded apartment (RTA). This apartment type would have behaved just like the thread-neutral apartment of COM+, but with synchronization built in. This is just the type of idea that was bound to emerge from a mindset that identifies apartments with concurrency management. Yet the decoupling of the COM thread management construct (the apartment) from the synchronization construct (contextual concurrency management) feels conceptually pure and gives us more flexibility: we now can build an object that any thread can enter (as the RTA would have allowed), but we can choose whether to synchronize method invocations (which the RTA would not have allowed).



              Figure 4-1 shows all possible synchronization settings for an object. The values are identical to those available for configuring transaction support. However, instead of being enlisted in or beginning a new transaction, an object with the value Supported, Required, or Requires New participates in or begins what is known in COM+ as a synchronization domain. Under MTS, synchronization domains were known as activities and were configurable only through the method a client chose to instantiate another object. Unfortunately, it therefore was up to the client to determine whether a new object could join its activity. Under COM+, this has become transparent to the instantiator and is controlled solely by the setting in the property sheet shown in Figure 4-1.








              Figure 4-1. Concurrency tab of the property sheet of a configured thread-neutral object.




              Like a transaction, a synchronization domain can include objects in different contexts, apartments, processes, and hosts.4 Also, a synchronization domain is formed through the creation of an object with the setting Required (made by a caller currently outside any synchronization domain) or Requires New (made by any caller); the synchronization domain then flows to any object with the setting Supported or Required at instantiation time. In a synchronization domain, only one physical thread can execute at a time, and each thread must execute as the result of either a direct or indirect synchronous method invocation from the thread that first entered the domain. Figure 4-2 shows a synchronization domain that spans contexts and hosts with several physical threads.









              Figure 4-2. Thread and synchronization domain interaction.




              Threading Model and Synchronization Interaction

              I have championed the fact that synchronization support and thread affinity are independent concepts and now are treated as such by COM+. And it is rather easy to see how synchronization can be applied (or not applied) to objects in either multithreaded or thread-neutral apartments. But understanding how synchronization is applied with the single-threaded apartment is a bit more challenging.5 After all, being single threaded already implies a certain natural synchronization across the entire apartment.

              The fact is that objects in the single-threaded apartment, which do participate in a synchronization domain, act quite differently from objects that do not participate in a synchronization domain. These differences include the following:




              • An object in a synchronization domain will flow domain membership to any object it creates that supports or requires synchronization. As a result, a group of MTA or TNA objects that support synchronization will not experience concurrency when created by an STA object in a synchronization domain. But if the STA object did not participate in a synchronization domain, this group of objects will experience concurrency.


              • Synchronized STA objects cannot be entered by calls coming from a causality other than the one of the call chain currently executing within the synchronization domain. Unsynchronized STA objects can.


              • STA objects that do not require synchronization will be created in the same apartment as the single-threaded instantiator object. But if the instantiator is not inside a synchronization domain and the STA object requires synchronization, the object actually will be created in a different single-threaded apartment. The reverse does not hold, however. If the caller is inside a synchronization domain, the object will be created in the same apartment—even if it does not support synchronization.


              As you can see, for the most part COM+ synchronization layers its benefits on top of single-threaded synchronization. Understanding this relationship certainly will be useful in your own projects.






              An object with the synchronization setting Disabled is unaware of synchronization services and behaves like an unconfigured component. Notice that this setting is not the same as Not Supported: the latter ensures that an MTA or TNA object can receive calls concurrently, while the former can result in the object being located in the caller's synchronized context. Disabled also is not the same as the Supported setting, which will force the object into a context that participates in the same synchronization domain as it would were the caller a member of a synchronization domain. But if the object requires a different context than that of the caller (for example, because the object has a different threading model, or because of other COM+ service configurations), the contexts must communicate to prevent concurrent execution. COM+ might achieve this communication by having the contexts share some type of lock. But when the setting is Disabled, the target context will not participate in such a locking
              scheme. This is why the Disabled setting truly has a unique meaning.



              The Required and Requires New settings mean that the object must run in a synchronization domain. Requires New ensures the object always will be the root of a new synchronization domain. Required creates a new domain only if the instantiator does not already participate in one.



              Synchronization Implementation by Deadlock



              The theory behind context-based synchronization is fantastic, and the sheer number of options now available to developers should tremendously simplify situations that previously required you to build your own plumbing to achieve just the right concurrency behavior. However, when I examine the current COM+ implementation of the synchronization services for single-threaded objects, my enthusiasm for the technology wanes.



              A call into an executing synchronization domain from a caller not participating in that synchronization domain can cause deadlock. A deadlock occurs if the call is made through an object in the synchronization domain whose threading model is Apartment and if that object's thread is currently servicing a message loop (for example, because it is waiting for an Object RPC call return). The message loop does not need to be within the bounds of the object being accessed concurrently for the deadlock to occur. These are the deadlocked entities:




              • The caller making the call from outside the synchronization domain.


              • The entire apartment of the thread receiving the inbound call of a new causality via its message loop, as well as any upstream callers waiting for the return of method invocations that this thread might be executing on behalf of. Such callers will be deadlocked whether they were part of the synchronization domain or whether the caller representing the initial causality was taking ownership of that synchronization domain.


              • The entire synchronization domain of the apartment-threaded object, as well as any threads waiting to enter the synchronization domain.





              All these callers now are stalled because the message loop thread attempts to gain access to a lock on behalf of the new inbound caller. This lock never will become available because releasing it would require returning this very thread from a method invocation that is now further up the stack, as shown in Figure�4-3. Hence, the thread never can return from the DispatchMessage call in the message loop, and the call chain becomes stalled from that level upward.








              Figure 4-3. Example of a deadlock.




              This issue is mitigated by the fact that concurrency often is regulated by a layer of technology in front of the COM+ object layers in modern COM+ architectures. The sharing of object references is discouraged in such highly scalable environments. (For more on this topic, see Chapter 13.) Nevertheless, the issue begs the question of why synchronization support is even an option for STA objects when enforcing it is guaranteed to result in deadlock. It is important to understand the situation precisely, since by its very nature it likely will cause sporadic bugs under just the right timing conditions, and often will be extremely hard to debug. Until Microsoft solves this problem, the only safe thing to do is religiously avoid sharing object references to synchronized STA objects. If you must share object references, be absolutely certain that synchronous calls cannot occur in your architecture. One warning: making concessions at that level of your architecture is likely to introduce brittleness.



              The Message Filter



              Restricting access for a single-threaded object to one causality at a time is common. Such objects often contain an internal state associated with the operation in progress, and receiving a call unrelated to this current operation can cause failure in these objects. As we have seen, using COM+ synchronization services unfortunately is not yet a solution for this kind of problem. But an ancient mechanism is designed to deal with this type of concurrency: the message filter, which stems from the 16-bit world of OLE and is associated with concurrency in user interface applications. Such applications often share single-threaded objects representing graphical entities among clients. A message filter is intended to prevent access to such single-threaded objects when they perform some internal manipulation (perhaps as the result of an inbound COM+ call), and would become confused by new requests if their internal call sequence had not yet completed.



              There is both a server and a client aspect of the message filter mechanism. Server and client applications can install their own message filter by calling CoRegisterMessageFilter, passing an object that implements the IMessageFilter interface:





              IMessageFilter�:�public�IUnknown
              {
              public:
              ����virtual�DWORD�STDMETHODCALLTYPE�HandleInComingCall(�
              ��������/*�[in]�*/�DWORD�dwCallType,
              ��������/*�[in]�*/�HTASK�htaskCaller,
              ��������/*�[in]�*/�DWORD�dwTickCount,
              ��������/*�[in]�*/�LPINTERFACEINFO�lpInterfaceInfo)�=�0;

              ����virtual�DWORD�STDMETHODCALLTYPE�RetryRejectedCall(�
              ��������/*�[in]�*/�HTASK�htaskCallee,
              ��������/*�[in]�*/�DWORD�dwTickCount,
              ��������/*�[in]�*/�DWORD�dwRejectType)�=�0;

              ����virtual�DWORD�STDMETHODCALLTYPE�MessagePending(�
              ��������/*�[in]�*/�HTASK�htaskCallee,
              ��������/*�[in]�*/�DWORD�dwTickCount,
              ��������/*�[in]�*/�DWORD�dwPendingType)�=�0;

              };



              On the server side, COM+ will call HandleInComingCall before dispatching an inbound call to an object. On the client side, COM+ will call RetryRejectedCall when the server does not dispatch the call but flat out rejects it or advises retrying it later. The code calls MessagePending when Windows messages are received by the client while waiting for a COM+ call to return. The dwCallType parameter of HandleInComingCall lets the server know whether an incoming call is from a new causality while the object's thread waits for an outgoing call to return (CALLTYPE_TOPLEVEL_CALLPENDING). Such calls therefore are deferred easily (return SERVERCALL_RETRYLATER).


              This all sounds marvelous, but there are some limitations. First, the message filter shows its user interface orientation by allowing only local servers, not DLL components, to register a message filter. This means no configured object can use this technique and therefore excludes today's most popular and flexible kind of COM+ component. Second, unless all clients can be guaranteed to be in process, rejecting a call with the retry flag can put an unacceptable burden on the caller. Unless the client's message filter specifies that such a rejected call should indeed be retried, an error message immediately will be returned to the caller. This caller might not be able to set its own message filter—for example, it might have been loaded from a DLL in another process or host, or it might have been implemented in a development system that does not permit setting a message filter. It is not reasonable to expect that all clients either have a message filter that retries or performs the retry at the
              level of every single COM+ method invocation. A Visual Basic Standard EXE will install a message filter that does retry for some time and then uses OleUIBusy to bring up the infamous Server Busy dialog box, giving the user a chance to manipulate the user interface of the server application and thus resolve the impasse. But the default COM+ message filter does not do this, instead it propagates all retry rejections directly to the caller. Therefore, even COM+ objects implemented in Visual Basic are subject to this error when operating in a host process implemented with, say, Visual C++.


              The bottom line is that message filters were designed in another era, for a problem more specific than regulating general STA object concurrency. If message filters happen to be applicable to your situation, great! By all means use them—they won't go away any time soon. But chances are that forcing message filters into a modern architecture will be like trying to fit square pegs into round holes.



              Interception Services



              Synchronization is only one service that COM+ performs at the level of the interceptor. COM+ configures lightweight proxies between contexts to perform the specific adjustments to the environment necessary for the crossover. For example, suppose context A has the same synchronization settings as context B but does not support transactions, while context B requires transactions. A lightweight proxy in context A representing an object in context B would create a new transaction but would not attempt to acquire the shared synchronization domain lock. This is part of the reason an object reference can be used only in the context in which it was created.



              Now let's take a look at some of the other COM+ services performed by interceptors:




              • Figure 4-4 shows the transaction support configuration of COM+. All settings except Disabled are familiar from MTS. Disabled simulates the behavior of an unconfigured object, with respect to transactions. In addition, you now can set the transaction timeout on a per-object basis (rather than a per-machine basis). The following grid shows which combinations of instantiator context and transaction support settings will force a new object into a new context. Note that even if the transaction aspect does not force a new context, one still might be required as the result of other settings.













                Caller ContextDisabledNot SupportedSupportedRequiredRequires New
                Has transactionxxxx
                Does not have transactionxxx






              • Object security configuration is also familiar from MTS. If security is enabled for the application, only users who are members of the roles checked in Figure 4-5 or those roles granted access at the interface and method levels will be able to call the object. An object with security checks enabled always will be created in its own context.








                Figure 4-4. Transactions tab of the property sheet of a configured object.









                Figure 4-5. Security tab of the property sheet of a configured object.





              • Just-in-time activation was present but unconfigurable under MTS. It is now controlled by the similarly named check box shown in Figure�46. When this option is set, the object will be created in its own context because you need a unique interceptor to create the object instance when its first method is invoked.








                Figure 4-6. Activation tab of the property sheet of a configured thread-neutral object.




              • Selecting the pooling check box shown in Figure 4-6 enables object pooling. Object pooling can be described as the opposite of just-in-time activation: object instances are returned to a pool instead of being destroyed when an object is deactivated. Interfaces related to pooling were defined under MTS (an object could request to be pooled by implementing IObjectControl in a particular fashion), but the pooling mechanism itself had not yet been implemented. Be sure to review the documentation carefully before checking this box: the system makes very specific demands of the implementation of a pooled object. You also can violate the isolation property of transactions by carrying state in a pooled object. Pooling likely will be effective only in somewhat specialized circumstances.





              An object's threading model can have an effect on what services will be available to it. For example, single-threaded objects cannot be pooled. Interdependency also exists among certain services. For instance, supporting or requiring transactions (including new ones) forces an object to use just-in-time activation. The specific settings of Supported and Required also force a setting of Required for synchronization support. The transaction setting Requires New implies the setting Required or Requires New for synchronization support. Apart from that, enabling just-in-time activation also demands that the object require an existing or new synchronization domain, regardless of the transaction setting.