GNOME Bugzilla – Bug 550278
Engine kickstart should be more automatic
Last modified: 2009-02-17 15:04:14 UTC
* Starter objects A Starter object should have a single method, say "init", which receives &argc, &argv and an Ekiga::ServiceCore reference, and returns a value from a failed/partial/complete enum. Said method shouldn't mind being called several times. Example of "init" method: first, check that the needed services are present, and that the provided service isn't already in ; if all is well, use the needed services as argument to create new objects, and register services, then return 'true' ; if something is wrong (and finding the service you wanted to provide is wrong), return 'failed'. Notice that since argc and argv are available, it's possible to check if the user disabled something. Example of "init" method: check if the "gtk-init" service is there (return 'failed'), and if not, call gtk_init then register the "gtk-init" service and return 'complete'. Example of "init" method: check that all needed deps are here and a service isn't provided, and provide it. See that needed deps for a second service aren't satisfied and return 'partial' (you managed to provide one, so it can't be 'failed'). Next call, only check if the second service can be enabled, since the first is already, and return 'failed' or 'complete'. * Firing the starters There is a list of Starter objects to which all Starters belong at first. The initialization algorithm is the following: (1) Take each Starter and try to "init" it: if 'failed' keep it there ; if 'partial' keep it there, but notice something good happened ; if 'complete' remove it and notice something good happened. (2) If something good happened, then get back to (1). * Algorithmic considerations Since we're looping on mostly the same list over and over again, the worst situation is when the most-needed dependancies are at the end of the list, because we could end up always getting to the end of the list to enable the feature -- this means a quadratic cost! If however they are just in the right order, then the cost is optimal: linear. So we really should put the built-in Starter objects in the right order to speed up starting time. Perhaps we should have a fourth value for complete failure? That would modify the algorithm that such an event would mean to remove the Starter from the loop while not noticing something good happened? (call it 'dead') Notice that the algorithm does end, since there is a strictly decreasing loop invariant : the number of not-yet-initialized services. There is a finite number of Starter objects, which want to initialize a finite number of services, and we loop only when we get a 'partial' or 'complete' initialization, ie when one of those services was started. !WARNING! This means an "init" method should really return 'failed' if it didn't manage to register a new service at this time, and not 'partial' because it registered one on the previous run! A good example to make it clear ; take a Starter has a "foo" and "bar" service to enable : + first "init" run: the "foo" service is enabled, but "bar" can't be -- return 'partial' ; + second "init" run: the "bar" service still can't be enabled ; we still had a success with "foo", but we don't care -- return 'failed' . * Why so complex? We need such a complex system because when external plugins will enter the picture, we will get them in filesystem order, so we need a way to order them correctly and automatically. Another advantage of this system is that if the Ekiga::ServiceCore stores the Ekiga::Service in FILO order, then if we uninitialize the services in that order all will go well. This is something std::tr1::smart_ptr would have given too -- but they have a size cost and dynamic_cast can't be used on them.
Ok, I implemented something like this and started to make use of it for some modules. What do you think about it at this point?
No complaints : it must be good enough.