Abstract:
The coordination and execution of chores in a multiprocessing environment. The coordination of chores is accomplished utilizing a compiler generated correlation that relates blocks of code that execute chores and blocks of code in which the chore can be realized. By tracking the execution of the program and using the compiler-generated correlation, chores can be identified for the currently executing code.

Description:
CROSS-REFERENCE TO RELATED APPLICATIONS 
       [0001]    Not Applicable 
       BACKGROUND 
       [0002]    Computers typically operate by having a processor execute a program. The program is represented using a sequence of instructions that may be executed by the processor. In order to improve processing speed, multi-processor computing systems are in operation. Such multi-processor systems may have as few as two processors, but could also have numerous processors. 
         [0003]    By adding a second processor to a computing system, the system is theoretically twice as powerful and can perform double the computations. In a perfect world, the actual performance of a computing system would scale linearly with the number of processors. Unfortunately, this is typically not the case as there are other bottlenecks within the system that limit the performance increase with each additional processor. 
         [0004]    One such bottleneck is in the program itself. In order for a program to take advantage of multiple processors, it should be able to execute multiple processes in parallel. If a program is not written in such a way to take advantage of multiple processors, it will generally only execute on a single processor resulting in any additional processors simply idling rather than performing actual work. 
         [0005]    If a programmer knows the number of processors available to execute the program, the programmer can write the program in such a way to take advantage of the known processors. One example of such a system would be to assign each window to a processor. In this way, each window would remain responsive with a dedicated processor. 
         [0006]    While such a system would be workable with a small number of processors, it quickly becomes unwieldy as the number of processors increases. Furthermore, if the program is written anticipating a certain number of processors, it may be unable to take advantage of a computing system having more processors than originally contemplated leaving the additional processors idle. Worse yet, the program may not run at all on a computing system with an unanticipated number of processors. 
         [0007]    Some solutions do not require a programmer to explicitly program parallel execution and manage the coordination of execution. One such solution uses the concept of chores. A chore is generally defined as an opportunity for parallelism. Using the concept of chores, a programmer can, at the time of programming, identify portions of code that can be executed in parallel if possible, without worrying about the actual details of load balancing, paging, and communication between processors. Instead, a runtime system determines which chores to run in parallel depending on available processors and resources. 
         [0008]    Often, there are many more opportunities for parallelism than actual processors to execute the chores. Is some cases, there may be several orders of magnitude more opportunities for parallelism at any given time that there are processor available to take advantage of those opportunities. In conventional solutions, there are significant resources used to create each chore in which a block of code is assigned to a particular processor. Furthermore, there are resources required during a “steal” operation in which an available processor takes a chore from another processor. In the environment in which there are many more chores than there are processors, the chore creation operation may be frequent, whereas the chore steal operation may be infrequent. As a result, the overhead associated with creating and executing chores limits the performance of and scalability of a chore-based system. 
       BRIEF SUMMARY 
       [0009]    The concepts described in this application are generally directed to embodiments for managing the execution of chores in a multiprocessing environment. 
         [0010]    The process of developing an application that takes advantages of a chore-based system begins when the executable code is developed. Initially a programmer identifies opportunities for parallelism within the code of the application. Alternately, the opportunities for parallelism can be identified using an automated system at the time the application code is compiled or interpreted into executable code. 
         [0011]    The compiler or interpreter is then used to transform the application code into executable code for execution in a computing system. As part of the generation of executable code, the compiler generates a data structure that relates specific locations of the executable code. Currently compilers generate such data structures in the form of an exception table, but other types of data structures could be generated. 
         [0012]    When the computer application is executed, a processor executes the instructions of the executable code. At some point, if there are enough processors in the computer system, a processor may run out of work to do. When a processor runs out of work, it searches for other work to do. The processor finds additional work by identifying the location of an instruction pointer and looking up the location in the data structure. 
         [0013]    If the location of the pointer is determined to be in a particular block of code, the processor can then proceed to steal work from a different processor by executing chore related code identified by the data structure. Before the processor can actually steal the work, it must first verify that the chore has not already been stolen or is in the process of being stolen. If the chore is available to steal, the processor marks the chore as being stolen and executes it. If the chore is not available to steal, the processor continues to look for other work to do. 
         [0014]    When a processor executing code attempts to execute a portion of code identified as being associated with a chore, the processor first verifies that another processor has not stolen or is not currently stealing the chore. The processor performs the verification and if there is no indication the chore has been or is being stolen, the processor executes the chore. If the chore is indicated as being stolen or in the process of being stolen, then the processor will typically skip the chore. 
         [0015]    Additional features and advantages of the invention will be set forth in the description which follows, and in part will be obvious from the description, or may be learned by the practice of the invention. The features and advantages of the invention may be realized and obtained by means of the instruments and combinations particularly pointed out in the appended claims. These and other features of the present invention will become more fully apparent from the following description and appended claims, or may be learned by the practice of the invention as set forth hereinafter. 
     
    
     
       BRIEF DESCRIPTION OF THE DRAWINGS 
         [0016]    In order to describe the manner in which the above-recited and other advantages and features of the invention can be obtained, a more particular description of the invention briefly described above will be rendered by reference to specific embodiments thereof which are illustrated in the appended drawings. Understanding that these drawings depict only typical embodiments of the invention and are not therefore to be considered to be limiting of its scope, the invention will be described and explained with additional specificity and detail through the use of the accompanying drawings in which: 
           [0017]      FIG. 1  illustrates a multiprocessing environment and the relation of managed stacks to the processors. 
           [0018]      FIG. 2  illustrates an example of a compiler generated data correlation and its relation to the compiler generated executable code. 
           [0019]      FIG. 3  illustrates a flow chart for the allocation of chores among a plurality of processors. 
           [0020]      FIG. 4  illustrates a method for coordinating threads between processors during the execution of a program. 
           [0021]      FIG. 5  illustrates a suitable operating environment for the principles of coordinating chores in a multiprocessor environment. 
       
    
    
     DETAILED DESCRIPTION 
       [0022]    Embodiments described herein relate to the allocation of chores among multiple processors in a multiprocessor environment. In some cases, the embodiments may comprise a special purpose or general-purpose computer including various computer hardware and/or firmware and/or software, as discussed in greater detail below. 
         [0023]    Embodiments within the scope of the present invention also include computer-readable media for carrying or having computer-executable instructions or data structures stored thereon. Such computer-readable media can be any available media that can be accessed by a general purpose or special purpose computer. By way of example, and not limitation, such computer-readable media can comprise RAM, ROM, EEPROM, CD-ROM or other optical disk storage, magnetic disk storage or other magnetic storage devices, or any other medium which can be used to carry or store desired program code means in the form of computer-executable instructions or data structures and which can be accessed by a general purpose or special purpose computer. When information is transferred or provided over a network or another communications connection (either hardwired, wireless, or a combination of hardwired or wireless) to a computer, the computer properly views the connection as a computer-readable medium. Thus, any such connection is properly termed a computer-readable medium. Combinations of the above should also be included within the scope of computer-readable media. 
         [0024]    Computer-executable instructions comprise, for example, instructions and data which cause a general purpose computer, special purpose computer, or special purpose processing device to perform a certain function or group of functions. Although the subject matter has been described in language specific to structural features and/or methodological acts, it is to be understood that the subject matter defined in the appended claims is not necessarily limited to the specific features or acts described above. Rather, the specific features and acts described above are disclosed as example forms of implementing the claims. 
         [0025]    In this description and in the following claims, a “chore” is defined as a subtask of a program that could be performed in parallel with other subtasks of the program in order to achieve some larger computational task. As such, a chore is generally described as an opportunity for parallelism. Because the chore is an opportunity for parallelism and does not require that the opportunity be taken, the chore exists whether or not the chore is actually being executed in parallel. The present examples describe the chores primarily as being identified by the programmer at the time the program is written. However, one of ordinary skill in the art will come to understand, after having read this description that the principles described herein apply when a compiler or interpreter identifies the chores at the time the machine-level executable code is generated. In order to take advantage of the chore, one process may execute one chore, while another process executes another chore. 
         [0026]    In one embodiment, the programmer identifies a computed action (i.e., a computation) in which, during the execution of the computation, a chore can be stolen. That is, a chore originally designated for a particular process may be taken for completion by yet another process. In this specific embodiment, which is by no means limiting of the inventive concept, the computation identified by the programmer is embedded within a try block of the program. The portion of code that is identified by the programmer as a chore associated with the identified computation is embedded within a catch block of the application, where the catch block is associated with that try block. One of ordinary skill in the art will recognize that the use of the terms “try” and “catch” are representative of the exception handling structure of the example embodiment and as such, are not limiting as to the invention itself. Other constructs designed specifically for chore management are applicable and within the scope of the disclosure. 
         [0027]    In this description and in the following claims, a “worker” is defined as a computation agent that performs work for the computing system. One example of such a computation agent is a process managed by an operating system. In one embodiment, there is one worker per processor. However, in other embodiments, the number of workers does not equal the number of processors. 
         [0028]    In this description, reference will be made to an exception table, an instruction pointer, and blocks of code. The exception table is defined as a compiler generated data structure that correlates blocks of code as being associated with either a try block or a catch block. In some embodiments, the terms “try” and “catch” are used in source code as “try statements” and associated “catch statements” to implement exception handling. However, the terms “try block” and “catch block” as used herein are meant to be more broadly interpreted. Specifically, a “try block” is defined to include any set of one or more instructions during which, if an exception is generated, results in a set of one or more instructions being, executed in the associated “catch block”. Additionally, a catch block may have an associated filter wherein the catch block will catch only a particular type of exception within the associated try block. This definition holds regardless of whether the software language used is now existing or to be developed in the future. 
         [0029]    The instruction pointer tracks the location of the currently executed code for a processor. A block of code is a particular section of the executable code generated by the compiler. The exception table contains references to the executable code and identifies particular blocks of code that are to be protected within a try block and code to execute within the associated catch block. For example, the exception table specifies that if an exception occurs while executing a particular try block, that an identified associated catch block will be executed. 
         [0030]    In this description and the claims that follow, a managed stack is defined as a data structure that stores information about the active subroutines of a computer executable program for a process. In the case of a single process being associated with a single processor, therefore, each processor would have a managed stack. For simplicity, each managed stack will now be described as being associated with a particular processor, although the principles of the present invention are not limited to that implementation. The active subroutines are those that have been called by the program, but have not completed execution. The managed stack is used to keep track of the point to which each active subroutine should return control when it finishes executing. Each worker generally manages its own stack that the worker uses to track the execution of its particular subroutines. Within the managed stack are managed stack frames that refer to individual subroutines on the managed stack. 
         [0031]    In this description and the claims that follow, stealing work is defined as a worker taking a chore from another worker&#39;s stack and executing the chore. 
         [0032]    Referring now to  FIG. 1 , an example is shown of a multiprocessor system and related managed stacks. Processors  100 ,  102 ,  104 ,  106 ,  108 , and  109 ach have an associated managed stack  110 ,  112 ,  114 ,  116 ,  118 , and  119 . Within each managed stack are stack frames that are associated with individual program subroutines. For instance, in the example of  FIG. 1 , managed stack  110  contains frames  122  and  124 , managed stack  112  contains frames  126 ,  128 ,  130 , and  132 , managed stack  114  contains frames  134 ,  136  and  138 , managed stack  116  contains frame  140 , and managed stack  118  contains frames  142 ,  144 ,  146 , and  148 . Each frame within the managed stacks has an associated frame pointer that identifies the location of code an associated processor will return to when reaching that stack frame. The number of stack frames for a given managed stack is variable and is dependent upon the particular program and the number of subroutines currently being executed. 
         [0033]      FIG. 2  illustrates the relationship between the executable code  200  and a compiler generated correlation in the form of an exception table  202 . In this specific embodiment, the compiler-generated correlation comprises a compiler generated side table in the form of an exception table  202 . The following description will utilize the example of exception table  202  for simplicity, but it is important to note that the exception table  202  could be any compiler generated data correlation. The exception table  202  is created at the time the executable code  200  is generated by a compiler or interpreter. Within the exception table  202  is an identification of at least two types of code. In keeping with the general description of an exception table, the types of code will be referring to common nomenclature with regard to an exception table and exception handling. However, the principles of the current invention are not limited to these specific examples. The first type of identified code is protected code, or code that is executed as part of a try block  204 . The second section of code identified by the exception table is the exception code, or code that is executed as part of a catch block  206 . In the example exception table shown, the protected area  218  corresponds to try block  204  of code, while the exception area  220  corresponds to a catch block  206  of code. In the examples given, the try block  204  of code is referred to as being protected by the “DoChore” exception, while the catch block  206  of code is referred to as the “DoChore” exception handler. The term DoChore is utilized to express simple example embodiments. However, the principles of the present invention are not limited to the nomenclature used to identify an exception type. 
         [0034]    Additionally,  FIG. 2  shows the use of instruction pointers  208 ,  210 ,  212 ,  214 , and  216 . Instruction pointers  208 - 216  are a reference describing the current location of code that is being executed by the instruction pointer&#39;s respective processor. As an example, instruction pointer  208  associated with processor  100  references the instruction being executed by processor  100 , or alternatively references the instruction next in line to be executed. While the instruction pointer  208  is depicted with the code  200 , the instruction pointer itself is only a reference and need not be physically associated with code  200 . Typically the instruction pointer  208  references a memory address and resides within a register of processor  100 . 
         [0035]    While instruction pointer  208  is associated with processor  100 , the other instructions pointers are associated with the current execution point for other processors. For instance, instruction pointer  210  may be associated with processor  106 , instruction pointer  212  may be associated with processor  102 , instruction pointer  214  may be associated with processor  104 , and instruction pointer  216  may be associated with processor  108 . Furthermore, each stack frame has an associated frame pointer (not shown). 
         [0036]    As code  200  is executed, each worker proceeds to execute the portion of code  200  referenced by the worker&#39;s instruction pointer. Additionally, for each chore, an indicator indicates the status of the chore. Typically, each portion of code corresponds to an active subroutine that must eventually return control to portion of code that called the subroutine. The managed stack contains the address of the code that the worker should return to using the frame instruction pointers. When the end of the managed stack is reached, the worker has run out of work to do. In order to avoid the worker idling while there is potentially work that could be done; the worker begins the process of stealing a chore. 
         [0037]    Referring to  FIG. 3 , the process  300  of stealing a chore begins after the worker recognizes a need to steal work. Examples of a worker recognizing a need to steal work include running out of items on the workers stack or reaching a point in the stack that directs the worker to code for stealing work. The worker that has recognized a need to steal work and now attempting to steal work will hereafter be called the “stealing” worker. 
         [0038]    In act  301 , the execution of the program is tracked using instruction pointers  208 - 216  related to each worker. The instruction pointer generally refers to the memory location of the currently executed instruction, the next instruction to be executed or could be any reference that identifies the instructions being executed. The instruction pointer is typically stored in a register of processors  100 - 109 , but could be stored in any location accessible to processors  100 - 109  such as within the managed stack for a particular processor. 
         [0039]    In act  302 , the stealing worker identifies a chore that can be executed in parallel with the code that is currently being executed by a worker. The worker currently executing the code to be stolen will hereafter be referred to as the “original” worker. The stealing worker scans the managed stacks  110 - 119  combining techniques well known to those of ordinary skill in the art from garbage collection and exception handling. The stealing worker moves from managed stack frame  122 - 148  to managed stack frame  122 - 148  while accessing the exception table  202  to determine if the instruction pointer  208 - 216  or the frame instruction pointers for a particular original worker is within a block of code  200  identified by the exception table  202  as being protected from a “DoChore” exception. If the “DoChore” exception is currently executing, there is an associated block of code  200  identified by the exception table that executes a related chore that can be stolen. 
         [0040]    As an example, in  FIG. 1 , processor  106  (the stealing worker) has run out of work to do as indicated by the single frame  140  on stack  116 . The single frame  140  remaining on the stack  116  may refer to chore stealing code, or the processor  106  may default to chore stealing code once stack  116  is empty. The stealing worker associated with processor  106  will then begin scanning the managed stacks of processors  100 ,  102 ,  104 ,  108 , and  109  in an attempt to find work to do. 
         [0041]    Using techniques from garbage collection and exception handling, the stealing worker will use the exception table to look up areas of code  200  that are currently being executed by other workers. This may be done by referring to the instruction pointer for the other workers until an instruction pointer is found that resides in a protected area. In the example of  FIG. 2 , instruction pointer  210  is within a try block  204 , which is identified in the exception table  202  as being a try a protected area  218 . While only one protected block of code is shown in the code  200  for simplicity, it will be understood that the code  200  will typically contain multiple areas of protected code  218 . Furthermore, it is possible that multiple instruction pointers will be found within the same protected area. For example, there may be two or more instruction pointers within the protected code  218 . Once a protected block of code  218  is found, the exception table  202  is referenced to find a catch block  206  of code corresponding to the identified try block  204 . 
         [0042]    In act  303  a variable indicating the steal status of the identified chore is updated using an atomic operation. The variable is accessible to each of the workers so that any worker can update it in response to stealing the chore. By using an atomic operation, the worker can check whether the chore has been stolen and mark it as stolen in one operation. For example, if the atomic operation is a compare-and-swap, the stealing worker verifies that the variable is a known value and if the values match, updates the location of the variable with a new value in a single operation. This eliminates the possibility that a second worker could steal the chore before the stealing worker has an opportunity to update the variable indicating it has stolen the chore. If the variable indicates that the chore has already been stolen, the stealing worker proceeds to another managed stack to look for a different chore to steal. 
         [0043]    Upon a successful update of the variable, act  304  is performed wherein the stealing worker executes exception code contained in the catch block  206  identified by exception table  202 . In one embodiment using act  305 , the exception code immediately copies data off the stack of the original worker and onto the stealing workers stack. In this way, when the original worker completes its current subroutine, it will return past the point where the chore was previously. 
         [0044]    As an alternative, in act  306  the exception code identified by catch block  206  may contain distinct blocks of code specialized for the stealing worker and/or the original worker. If the catch block  206  contains a block of code specialized for the stealing worker, the data contained on the stack of the original worker is copied onto the stack of the stealing worker. In this case, the variable would then be updated to indicate that the process of stealing is complete. In this way, when the original worker completes its current subroutine, it will recognize that the chore has been stolen and will not attempt to execute the chore code. 
         [0045]      FIG. 4  illustrates a method  400  for coordinating threads between processors using chores. In act  401  the execution of the program is tracked using instruction pointers. There is generally one instruction pointer per worker so that each worker tracks where it is at in the code, and frame instruction pointers identifying where the worker will return to in the code upon completion of a stack frame. The instruction pointers may be a reference to a memory location of the code and are generally stored within a register contained in a processor or some other location that can be accessed at high speed. 
         [0046]    In act  402 , the block of code currently being executed by an original worker is identified as being associated with a chore. This may be accomplished by the code itself declaring that it has an associated chore, or can be identified using exception table  202 . 
         [0047]    When the block of code identified as being associated with a chore is completed by the original worker, act  403  is implemented. In act  403 , an attempt to update a variable corresponding to the chore associated with the block of code is performed using an atomic operation. The variable indicates the status of the chore and indicates whether the chore has been stolen. The variable may additionally indicate that the process is in the process of being stolen. As an example, if the original worker attempts to update the variable using a compare and swap operation, the operation will return either a value that says the operation failed, indicating that the chore has been stolen, or the operation will return a success indicating that a successful update was performed. 
         [0048]    If the atomic operation indicates that the chore has not been stolen (i.e., the compare and swap returned a success), then act  405  is performed as indicated by decision block  404  wherein the original worker executes the chore code identified by the compiler-generated correlation. In act  407  the original worker executes the chore code by raising a chore exception and jumping into the exception block of code identified by the exception table  202 . As an alternative, in act  408  the chore code is duplicated outside of the catch block  206  and the original worker enters a conditional block of code containing the duplicate chore code. Other possibilities are possible for executing the associated chore code and these two examples are not meant to be inclusive of all possible acts for executing the chore code. 
         [0049]    If the atomic operation indicates that the chore has been stolen, (i.e., the compare and swap operation returns an unsuccessful result) then the chore has been stolen and the original worker need not execute the chore code. As indicated by decision block  404 , upon an unsuccessful update act  406  is performed wherein the original worker proceeds in execution past the chore code. If the code is implementing fork/join concurrency, then act  409  is performed wherein the original worker waits until the stolen chore is completed. If the code is not implementing fork/join concurrency, the original worker can perform act  406  immediately and proceed past the chore code and continue the execution of code or, if no code remains to be executed, the original worker could attempt to steal work from another worker. 
         [0050]    In some instances, it may be necessary for the original worker to coordinate with the stealing worker to ensure the stack is not modified by the original worker while the stealing worker is using it. In act  410  this is accomplished by the original worker spinning until the stealing worker updates the variable indicating that the stealing process is complete. Alternatively, in act  411 , the execution of code by the original worker is suspended by the stealing worker during the time the chore code is being stolen. Other methods of ensuring the stack is not modified by the original worker during the time that the chore is being stolen are possible and these two examples are in no way the only ways in which this could be done. 
         [0051]    The described methods may be implemented using a high-level computer programming language that is then compiled into computer executable code that performs the described method. As an example, the following high-level pseudo-code would implement features of the invention in the calculation of a Fibonacci number. 
         [0000]    
       
         
               
               
             
           
               
                   
                   
               
             
             
               
                   
                 public static IntPromise Compute(int n) 
               
               
                   
                 { 
               
               
                   
                   if (n &lt;= 1) { 
               
               
                   
                     return n; 
               
               
                   
                   } 
               
               
                   
                   FibChore chore; 
               
               
                   
                   chore.arg = n−2; 
               
               
                   
                   try { 
               
               
                   
                     Chore.Queue(chorel.resolver); 
               
               
                   
                     IntPromise f1 = Fib(n−1); 
               
               
                   
                     return IntChore.Keep(ref chore.resolver) 
               
               
                   
                       ? f1 + Fib(chore.arg) 
               
               
                   
                       : f1 + chore.resolver.Promise; 
               
               
                   
                     } 
               
               
                   
                   } 
               
               
                   
                   catch (DoChore ex) { 
               
               
                   
                     int arg = chore.arg; 
               
               
                   
                     IntResolver res; 
               
               
                   
                     if (IntChore.Steal(&amp;chore.resolver, out res)) { 
               
               
                   
                       try { 
               
               
                   
                         Fib(arg).when(res); 
               
               
                   
                       } 
               
               
                   
                       catch (Exception ex) { 
               
               
                   
                         res.Break(res); 
               
               
                   
                       } 
               
               
                   
                     } 
               
               
                   
                   } 
               
               
                   
                 } 
               
               
                   
                   
               
             
          
         
       
     
         [0052]    The high-level code, or other code written by the programmer, may then be translated or compiled into computer-executable code. The computer-executable code may then be stored on a computer readable storage medium and later executed by a computing system. 
         [0053]    Referring to  FIG. 5 , in its most basic configuration, a computing system  500  typically includes a plurality of processing units  502  and memory  504 . The memory  504  may be physical system memory, which may be volatile, non-volatile, or some combination of the two. The term “memory” may also be used herein to refer to non-volatile mass storage such as physical storage media. As used herein, the term “module” or “component” can refer to software objects or routines that execute on the computing system. The different components, modules, engines, and services described herein may be implemented as objects or processes that execute on the computing system (e.g., as separate threads). 
         [0054]    Computing system  500  may also contain communication channels  508  that allow the computing system  500  to communicate with other computing systems over, for example, network  510 . Communication channels  508  are examples of communications media. Communications media typically embody computer-readable instructions, data structures, program modules, or other data in a modulated data signal such as a carrier wave or other transport mechanism and include any information-delivery media. By way of example, and not limitation, communications media include wired media, such as wired networks and direct-wired connections, and wireless media such as acoustic, radio, infrared, and other wireless media. The term computer-readable media as used herein includes both storage media and communications media. 
         [0055]    Embodiments within the scope of the present invention also include computer-readable media for carrying or having computer-executable instructions or data structures stored thereon. Such computer-readable media can be any available media that can be accessed by a general purpose or special purpose computer. By way of example, and not limitation, such computer-readable media can comprise physical storage and/or memory media such as RAM, ROM, EEPROM, CD-ROM or other optical disk storage, magnetic disk storage or other magnetic storage devices, or any other medium which can be used to carry or store desired program code means in the form of computer-executable instructions or data structures and which can be accessed by a general purpose or special purpose computer. When information is transferred or provided over a network or another communications connection (either hardwired, wireless, or a combination of hardwired or wireless) to a computer, the computer properly views the connection as a computer-readable medium. Thus, any such connection is properly termed a computer-readable medium. Combinations of the above should also be included within the scope of computer-readable media. 
         [0056]    Computer-executable instructions comprise, for example, instructions and data which cause a general purpose computer, special purpose computer, or special purpose processing device to perform a certain function or group of functions. Although the subject matter has been described in language specific to structural features and/or methodological acts, it is to be understood that the subject matter defined in the appended claims is not necessarily limited to the specific features or acts described herein. Rather, the specific features and acts described herein are disclosed as example forms of implementing the claims. 
         [0057]    The present invention may be embodied in other specific forms without departing from its spirit or essential characteristics. The described embodiments are to be considered in all respects only as illustrative and not restrictive. The scope of the invention is, therefore, indicated by the appended claims rather than by the foregoing description. All changes which come within the meaning and range of equivalency of the claims are to be embraced within their scope.