MACPRINT SPOOLER CDD Design Goals: MacPrint is ported from the OS/2 implementation. The primary design goal is to simplify the porting process as much as possible. The original structure of MacPrint was retained and modified only where necessary. There are 3 main areas of modification: * configuration * synchronization * removal of the despooler component * support for cached dictionaries configuration changes - Where the OS/2 implementation depended on being able to determine at service start time the configuration for all possible shared print queues (this was read from lanman.ini), the NT implementation allows for the dynamic addition and removal of printer objects without specifying configuration information in a data file. This forced the modification of the main queue list structure to be a single linked list instead of an array. In the NT implementation, there is now a single data structure for NT printer objects, and being in this structure implies the existence of a validly configured NT Printer Object. All of the code to read and parse configuration information from LANMAN.INI at service start time was removed and replaced by code to get configuration information from the EnumPrinters() API as new printers are discovered. synchronization changes - NT provides a more robust synchronization mechanism than OS/2. WaitForMultipleObjects() actually works correctly under NT, and it is possible to wait on objects besides semaphores. Mutual exclusion to data is achieved through critical sections as opposed to mutexes or semaphores. Critical sections require fewer resources than either, but can only be shared between threads of the same process. Events are used to process jobs asynchronously. Instead of clearing a semaphore when a read or open occurs, an event is signaled. Semaphores in NT are used as a gating mechanism, and are not designed to be used as a signaling mechanism. Syncronization with the termination of threads is achieved by waiting on the thread object as opposed to waiting on an event that the thread signals just before it terminates. removal of the despooler - The spooler and despooler were somewhat integrated in the OS/2 implementation - especially with regards to configuration information. Data structures pertinent to the despooler only were removed, and the configuration routines are no longer contained in a DLL shared by the spooler and despooler. The only connection between the two components is now through a PostScript comment that is included by the spooler to indicate if ctrl-d's should be stripped by the despooler. support for cached dictionaries - As the caching of PostScript dictionaries achieved only modest performance improvements at best, and since it added significant complexity to the code, support for cached dictionaries will most likely be removed in the NT implementation. If at coding time it becomes clear that more work is involved to remove the feature than to port it, it may stay in. Thread/Process Structure: MacPrint is an NT Service, hence it follows the NT Service Model. main() is the service control dispatcher for the MacPrint service. This thread is used by the NT Service Controller to communicate with the MacPrint Service. MacPrintMain() is the main thread for MacPrint. It is responsible for monitoring the NT Print Manager for configuration changes (creation and removal of printer objects) and for starting and stopping threads to manage each NT Printer Object. Essentially, MacPrintMain polls until a service stop request is received. While polling, it enumerates printers from the NT Print Manager and either creates a control thread for a newly discovered printer or terminates the control thread for each printer no longer configured. QueueServiceThread() is the thread for each active NT Printer Object. This thread listens for jobs directed to the NT printer and processes the job. One thread supports multiple jobs for the printer. This is done by using two events per queue - ReadEvent and OpenEvent. The PAP API have the property that they take an address to store a completion code when the event completes, and this address is specified to be in a per job data structure. The event is common for all jobs in the queue. When a particular event is signaled, the list of pending jobs in that queue is walked to find one that is in the correct state (read_pending or open_pending) and that has it's completion code set. The event is processed for that job, and the job is moved to the end of the list so that events for jobs are processed in round-robin order.