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.



Technology Specification for Implementing Shared Preferences.

author: jgaunt date: Sept 19, 2002

Summary

Preferences are one component that will be using the profile sharing architecture. It also happens to be the first to be tackled as it's access model is what drove the concept of a transaction queue which eventually became the transaction manager. This document describes the specifics of preference sharing as accomplished using the method(s) described in the Shared Profile Tech Spec and the Transaction Manager Tech Spec. It is reccommended the reader become familiar with both of those documents before  reading this one.(profile sharing tech doc homepage)

Requirements

For a single user profile, allow multiple gecko based processes to share the pref data at runtime.
  • Allow multiple processes on a single computer to read a single set of preferences common to a single user profile.
  • Allow a process to declare which preferences it wants to access as shared, and which should be non-shared.
  • Don't interfere with the use of config tools to set up administrator defined preferences.
  • Don't interfere with the mechanism for locking preferences.
  • Ensure preferences do not get overwritten or corrupted

Design

  • Each application needs to be able to specify which prefs it wants shared and which it wants to hold closed. Currently I believe we are leaning towards a 2 tiered approach. First there will be a list of shareable preferences that any gecko process will know are shared. This list will live in a file somewhere. Applications can programatically opt out of sharing a particular pref ( or a branch I suppose ). The second tier is an additional list of prefs that are shared above and beyond the intial list, possibly including standard mozilla prefs and possibly including prefs for new components of mozilla ( installed via xpis etc ).
  • The prefernece system will need to be modified slightly to check the transaction manager when writing prefs and notifying the TM of changes made to prefs.
  • There will be a flag that declares the transaction as a pref transaction and one to differentiate between one of the following pref operations:
    • SetIntPref 
    • SetBoolPref
    • SetCharPref 
    • SetComplexPref 
    • LockPref
    • UnlockPref 
    • DeleteBranch
    • ResetUserPref
    • ResetBranch

Use Cases

  • Startup
  • Shutdown
  • Closing of Pref Panel
  • Reception of notification from TM

WARNING -- THIS DESIGN IS OLD AND IS IN THE PROCESS OF BEING UPDATED TO THE CURRENT ARCHITECTURE. - jgaunt 9/26
    • When a preference is set, a transaction will be placed in the preference queue.
      • If the owner is the only PID on the list, a ref count of -1 will be set. This will signal to new processes that the transaction has not been flushed to the disk and it needs to be picked up. Processes will not change a ref count of -1.
      • If there is more than just the owner watching the queue the ref count will be set to the number of processes - 1 ( for the owner). When the ref count reaches 0 the process that set the ref count to 0 will be responsible for removing the transaction from the queue and adjusting any remaining transactions.
    • Access of the shared memory segment will be guarded by semaphores and the access to the file on disk will be guarded by ccarlen's current locking scheme in use for profiles in general ( it may be neccessary to change this to a model able to wait on the file, such as the semaphores used for the shared memory )
    • There are several patterns of access for the shared memory:
      • Program startup sequence - for all processes starting up regardless of any existing processes
      • Launch the Transaction Service(TS) {
        Open and attach to the Global Header Semaphore(GHS)
        Enter the GHS
        Open and attach to the Global Header(GH)
        Check for the address of the Master Queue List(MQL) in the GH
        Open and attach to the MQL Semaphore(MQLS)
        If the MQL address is not there {
        Enter the MQLS
        Create the MQL
        }
        Else{
        Enter the MQLS
        }
        Open and attach to the MQL
        Increment the count of attached processes in the MQL
        Exit the MQLS
        Exit the GHS
        }

        General instructions for components interested in shared data -- call the TS {
        Enter the MQLS
        Check the MQL for the name of the Transaction Queue(*TQ) desired.
        Open and Attach to the proper *TQ Semaphore (*TQS)
        If there is no entry for the *TQ
        Enter the *TQS
        Create the *TQ
        Set up the *TQ with the proper header data
        Else
        Enter the *TQS
        Open and attach to the *TQ
        Add our PID to the list of listening processes
        Adjust the MTOQI 
        If there are outstanding transactions
        push them down modify any Top of Queue Indexes(TOQI) of existing PID blocks
        Set our TOQI
        Increment the count of processes attached
        Increment the ref count of any outstanding transactions
        Exit the *TQS
        Exit the MQLS

        During pref loading{
        Acquire File Lock (prefs.js, user.js etc... )
        Read the data from the file (the prefs)
        Notify the TS that we are interested in shared prefs (executing the code above)
        Enter the MQLS (shortcut this if queue is brand new)
        Enter the *TQS for prefs (PTQS)
        Retrieve all the outstanding Transactions
        Decrement the ref count for each transaction
        If the ref count on any transaction drops to 0
        Remove the transaction
        Shuffle any remaining transactions upward
        modify and TOQIs that pointed below the old transaction
        Exit the PTQS
        Exit the MQLS
        Release File Lock
        }
      • Set[Int|Bool|Char|Complex]Pref
      • Change local copy of the pref
        Create a transaction
        Enter PTQS
        Place Transaction in the preference queue
        Exit PTQS
        continue
      • Notification of Transaction in PTQ  -  when another process changes the shared memory
      • Enter PTQS
        Get any transactions below our TOQI
        Decrement the ref count for each
        If the ref count drops to 0
        Remove the transaction
        Shuffle any remaining transactions upward
        Modify any TOQIs that pointed below the old transaction
        Exit PTQS
      • Preference flushing - very similar to startup, key diffs are the transactions with -1 ref count and moving the write to below the transaction processing. We need to make sure our copy is up to date with all the transactions before we write it to the disk.
      • Acquire File Lock (prefs.js, user.js etc...)
        Enter the PTQS
        Retrieve all the outstanding Transactions
        Decrement the ref count for each transaction
        If the ref count on any transaction drops to 0 OR equals -1
        Remove the transaction
        Shuffle any remaining transactions upward
        modify and TOQIs that pointed below the old transaction
        Exit the PTQS
        Write the data to the file (the prefs)
        Release File Lock
      • Program shutdown - Update our state, write out our state. Need to make sure if we are deleting the shm/sem that there aren't any other processes coming up and holding on to them while we are going away.
      • Acquire File Lock (prefs.js, user.js etc...)
        Enter the MQLS
        Enter the PTQS
        Retrieve all the outstanding Transactions
        Decrement the ref count for each transaction
        If the ref count on any transaction drops to 0 OR equals -1 {
        Remove the transaction
        Shuffle any remaining transactions upward
        modify and TOQIs that pointed below the old transaction
        }
        Decrement (and capture) the process count
        Remove our PID from the PTQ
        Adjust the MTOQI
        Detach and close the PTQ for our process
        If the process count dropped to 0
        Delete/Destroy the PTQ from the system
        Exit the PTQS
        Drop the PTQS
        If we destroyed the PTQ
        Destroy the PTQS
        Write the data to the file (prefs.js, user.js...)
        Exit the MQLS
        Release File Lock
      • Runtime addition of shared data listener - for instance some piece of a program doesn't get created until a user needs it, but it uses a shared resource - WARNING, the *TQ may not be up and running already!!
      • If TS has not been started already
        Start TS
        Enter the MQLS
        Check the MQL for the name of the Transaction Queue(*TQ) desired.
        Open and Attach to the proper *TQ Semaphore (*TQS)
        If there is no entry for the *TQ {
        Enter the *TQS
        Create and attach to the *TQ
        Set up the *TQ with the proper header data
        }
        Else {
        Enter the *TQS
        Open and attach to the *TQ
        }
        Add ourself to the *TQ {
        Add our PID to the list of listening processes
        increment the count
        Adjust the MTOQI
        If there are outstanding transactions {
        push them down
        Increment their ref count
        modify any Top of Queue Indexes(TOQI) of existing PID blocks
        }
        Set our TOQI
        }
        Retrieve all the outstanding Transactions
        Decrement the ref count for each transaction
        If the ref count on any transaction drops to 0 ( should not be any right? )
        Remove the transaction
        Shuffle any remaining transactions upward
        modify any TOQIs that pointed below the old transaction
        Exit the *TQS
        Exit the MQLS
      • Runtime removal of shared data listener - a component of a program shuts down and no longer needs the transaction notifications. Does not bring it's current state up to date with what is in the transaction queue. Writes it's state out to disk JUST LIKE SHUTDOWN, a question remains in my mind about us writing to disk if we are  just closing down a portion of the program that was interested in transactions. XXX discuss this
      • Acquire File Lock
        Enter the MQLS
        Enter the PTQS
        Retrieve all the outstanding Transactions
        Decrement the ref count for each transaction
        If the ref count on any transaction drops to 0 OR equals -1 {
        Remove the transaction
        Shuffle any remaining transactions upward
        modify and TOQIs that pointed below the old transaction
        }
        Decrement (and capture) the process count
        Remove our PID from the PTQ
        Adjust the MTOQI
        Detach and close the PTQ for our process
        If the process count dropped to 0
        Delete/Destroy the PTQ from the system
        Exit the PTQS
        Drop the PTQS
        If we destroyed the PTQ
        Destroy the PTQS
        Write the data to the file (prefs.js, user.js...)
        Exit the MQLS
        Release File Lock
      • Lock pref
      • Change local copy of the pref
        Create a transaction
        Enter the PTQS
        Place Transaction in the preference queue
        Exit the PTQS
      • Unlock pref
      • Change local copy of the pref
        Create a transaction
        Enter the PTQS
        Place Transaction in the preference queue
        Exit the PTQS
      • Clear User Pref
      • Change local copy of the pref
        Create a transaction
        Enter the PTQS
        Place Transaction in the preference queue
        Exit the PTQS
      • Reset Branch
      • Change local copy of the pref
        Create a transaction
        Enter the PTQS
        Place Transaction in the preference queue
        Exit the PTQS
      • Delete branch
      • Change local copy of the pref
        Create a transaction
        Enter the PTQS
        Place Transaction in the preference queue
        Exit the PTQS
        continue
      • Summary chart X means we definitely do it, x means we might if conditions are right.
        Action
        Lock File
        GHS
        MQLS
        *TQS
        Write Trans
        Write SHM
        Write File
        Startup - init

        X
        X


        X

        Startup - pref loading
        X

        X
        X

        X

        Shutdown
        X

        X
        X

        X
        X
        Set*Pref



        X
        X
        X

        Notifcation of Pref Change



        X

        X

        Flush Preferences
        X


        X

        X
        X
        Add Listener at Runtime

        x
        X
        X

        X

        Remove Listener at Runtime
        X

        X
        X

        X
        X
        Lock Pref



        X
        X
        X

        Unlock Pref



        X
        X
        X

        Reset Branch



        X
        X
        X

        Delete Branch



        X
        X
        X

        Reset User Pref



        X
        X
        X


    • Next transaction entry
    • and the next
  • Transaction Service Public Interface: - The important thing to remember is that this is a messaging service, like the USPS. It should have little (if any) knowledge of what it is passing back and forth.
    • Init()
    • AddTransactionListener(QueueID, nsISupports*) - the queue we want to add ourselves to and a pointer back to the object that cares.
    • RemoveTransactionListener(QueueID, nsISupports*)
    • PostPrefTransaction(TransType, Message)
    • RetrievePrefTransaction(TransType, Message) 
    • Shutdown()
    • PostCookieTransaction(TransType, Message)
    • RetrieveCookieTransaction(TransType, Message)