You are currently viewing a snapshot of www.mozilla.org taken on April 21, 2008. Most of this content is highly out of date (some pages haven't been updated since the project began in 1998) and exists for historical purposes only. If there are any pages on this archive site that you think should be added back to www.mozilla.org, please file a bug.



Structure of the XP Event Loop Code

By Travis Bogard
Last Modified: 9-2-99


 This page is devoted to providing information about how the XP Event Loop is implemented.  This does not cover the design of the XP Event Loop, but rather the actually layout of the C++ used to implement the design.  If you haven't already done so and wish to learn more about the design, read the collective documents about the background, types of loops and sets of interfaces found on the main XP Event Loop page.  Hopefully this will serve as an aid to anyone needing to port the event loop, but it should also be informative for anyone curious in how it is setup.

The layout of the code was governed by a few key principles:

  • Performance - The first and foremost principle was to make the event loop as efficient as possible while retaining the ability to be cross platform.  Clearly the best performance would come from writing all native code, however this is difficult to maintain and thus a slight (very, very, very, slight performance hit is ok for the sake of reusable maintainable code).  Beyond this, however all effort was made to make the code as efficient as possible while staying XP.
  • High Percentage of XP code - Though much of the implementation and actual APIs to implement the event loop are native, there is a large percentage of code that is redundant across the platforms.  In these cases all effort was made to move common logic and program flow into XP locations in order to minimize the amount of non-xp code.
  • Minimize Cut and Paste - Not only are there many different platforms that may share similar code, but there are also three types of event loops.  The basic nature of these is very similar in some core respects, yet differ in others.  This often leads to cutting and pasting the code across the variations and then many problems later as bugs are found in each implementation.  Since the actual implementation lives in platform code, the number of places to keep in sync would have been 3 x number of platforms.  So one of the main goals was to consolidate code that would have been cut and paste and instead share it in some way.  Some of this is obviously the sharing that occurs by getting a high percentage of XP code as the previous goal stated.  The other part aims to get sharing on a single platform across the three variations of loops.
  • Minimize dependencies - It was also important to minimize the number of linking dependencies.  Since the interaction with the event loop on many platforms varies depending on the UI toolkit chosen, in order to work with all toolkits we would have to link with each one.  We really needed a way to for any given module to link with only one toolkit at a time.  Yet in doing this we did not want to give up all the other goals expressed above.  Especially the cut and paste goals.
  • Portable - Last but not least, this code needed to be easily ported to other platforms.  This means two things.  First the fact that the code is XP should not force a given platform to give up access to a loop it would normally have.  Plainly said, an app using the XP Event Loop should have the same control over the native event loop that they normally would when not using the native platform API calls.  Secondly, the actual effort to port the code should be straight forward and without many unknown gotchas requiring large amounts of work to track down.  This documentation as well as porting docs is hopefully a step to make this process easier, but the code layout was also designed with this purpose in mind.

The Components

Each type of event loop can be found implemented in a component.  As there are three types of event loops, there are three different components that implement one loop type each.  By putting each one in a component it enables us to then put each component into it's own module.  (In our case all three components live in one module).  But we can then have multiple implementations of the same component.  This allows us to make a runtime decision about which event loop implementation we will used based on which UI Toolkit is chosen.

The Class Hierarchy

In order to get maximum re-use of code and get a large percentage of the code to be XP, I have broken up the implementation into 4 levels of a class hierarchy.  The code is actually structured in such a way where two of those levels live in the xp code side and two live in the platform side.  The platform code is actually named in such a way where it is a build time issue as far as which implementation of a given platform file gets pulled in.  This allows us to get away from having any ifdefs in the code.

The levels can actually be grouped into two groups, the base event loop group (nsCBaseLoop and nsCPlatformBaseLoop) which provides the basic implementation needed for implementing an event loop component. There is one xp and one platform class for this level.  Then there is the class group level that handles implementing the specific types of event loops (nsCBaseAppLoop and nsCAppLoop).  In this level there again is one platform class and one xp class.  Below is the list of classes that make up the 4 levels of the class hierarchy.  (All platform links point to windows implementation).
 

nsCBaseLoop

This class is the base of all the event loops.  It's jobs include:
  • XPCOM Tasks - This level handles all the logic needed to make an event loop and XPCOM component.
  • Interface Error Checking - Provides all error checking on interface boundaries for nsIEventLoop.  This level handles all the interface boundaries.  Those that it can not implement the method in place because the implementation is either different per platform or per event loop, passes off the call to a pure virtual function that must be implemented by one of the sub-classes.  These sub-classes therefore have the luxury and the responsibility to not do error checking.  This is important to keep performance as near to a native event loop as possible.
  • Implement Run() - The Run() implementation is setup to be very stream-lined based on the listeners being requested.  This level handles creating the basic event structures needed and handing off the running to pure virtual functions that are implemented later by loops of a specific type.
  • Event and EventFilter Management - This level handles creating and cloning events.  It also handles extracting the platform data from these objects when it is requested.

nsCPlatformBaseLoop

This class subclasses from nsCBaseLoop.  It is found in the parent hierarchy of all event loops.  This class allows platform specific implementation to interject itself before the the classes branch into the various event loop types.  Native code that is common across all types of events for a given platform should go here. It's jobs include:
  • Avoid Error Checking - All virtual overrides here should not do error checking.  Any over-rides on the real interface boundaries however should.  No one should be over-riding the interface boundaries though.
  • Implement Shared Platform Methods - Any method that can be implemented the same for all types of event loops for the given platform should be implemented here to avoid copy and pasting after the loop type branch in classes.

nsCBaseAppLoopnsCBaseThreadLoop, and nsCBaseBreathLoop

This class subclasses from nsCPlatformBaseLoop.  It is found in the parent hierarchy of all event loops.  This class provides the code that is different for each or event loop type, but is the same for all platforms.  It's jobs include:
  • Implement the various RunWith*() functions - nsCBaseLoop calls on a number of RunWith*() functions to implement the run in different ways based on which listeners are requested.  This is done to avoid repetitive checks that would slow performance.  Beyond this, these Run loops vary depending on event loop type so they must live at this level instead of in nsCBaseLoop.  These are implemented by calling on a number of platform virtuals so this code is able to be XP as the virtual functions are overridden by the platform subclass.

nsCAppLoopnsCThreadLoop, and nsCBreathLoop

This class subclasses from the appropriate nsCBase*Loop (nsCBaseAppLoop for nsCAppLoop etc).  This is the final class in the class hierarchy and is what is instantiated as the implementor of the event loop component for the requested type.  This code is nearly all platform specific.  Note that the name of the class does not have a platform in it.  This is done intentionally to put the burden on which class is instantiated on the build system rather than ifdefs in the code.  It's jobs include:
  • Implement remaining pure virtual functions - Any methods that could not be implemented at a higher level must be implemented at this level.  One can also override implementations off higher classes if only one event loop type varies in a slight way.
  • Implement the Class Factory "Create" function - The code uses the Generic Class factory to handle creation of the event loop components.  Since this is the level that knows the class name for creation, this level handles the creation.

Other Components

There are a couple of other components that are used to make up the complete XP Event Loop.  These components are actually instantiated and created by the event loop code.  Both are completely implemented in platform code, but provide XP interfaces.
 

nsCEvent

This class implements the nsIEvent interface.  The object that is instantiated and passed around is the basic event for the platform.  It is passed around as platform independent entity via it's nsIEvent interface.  A given platform however can quickly get to it's platform data by calling the appropriate Get or SetNativeData() functions.  In fact the core job of nsCEvent is to wrap up this even data and provide access to it via the accessor functions.  The data passed through these functions are a void* which means both sides have to know what the structure of the data is.  Some platforms may be able to re-use structures defined by their OS, or they may need to define another known structure which will be the native data.

nsCEventFilter

This class implements the nsCEventFilter interface.  This object serves as the wrapper for the platform event retrieval filter data.  Much like nsCEvent this class simply provides Get and SetNativeData() functions to allow code to retrieve the internal filter data.  Also like nsCEvent, the data passed through is void*.  This means both sides have to know what the structure of the data is.  Some platforms may be able to re-use structures defined by their OS, or they may need to define another known structure which will be the native data.