Pierre Phaneuf
2004-01-16 13:27:02 UTC
I think I will be directing my efforts on the module loader in the
coming times, unless someone tells me that I'm a idiot and that
something else urgently needs help... :-)
* My current plan
Modules expose a single symbol that is a ModuleInfo structure (the real
name is a bit different, due to namespace issue, but I'll use this one
in this discussion). The ModuleInfo structure will have (at the
beginning, more on this later) a version field, and three function
pointers: getObject, loadModule and unloadModule.
Of these three, getObject is the only required one, and pretty much
match the method of the same name that IServiceHandler has and that the
module loader implements.
loadModule and unloadModule are optional (can be left NULL), and will be
called at the appropriate times. unloadModule would return a bool and
could veto the unloading of the module.
All three methods would get passed an object implementing IModule that
will have been created by the module loader, in addition to a
IServiceManager*. This IModule interface will have lock and unlock
methods that will be used to reference count the use of the module. When
unlock gets called and the count goes to zero, the module will be ready
to unload.
Additionnal information will get added to this structure in time, for
example a list of UUIDs supported by the module, for caching purposes,
and interface definition information, for reflection purposes (scripting
languages, recreating C/C++ headers from just the shared object, etc).
* Ideas and issues
I'm thinking of having *two* version numbers in ModuleInfo, one being
the version number of the structure used in this module, and the second
being the minimum version number that the module loader should support.
For example, if we add interface definition information, we would bump
the version number, but not the minimum version number, because older
module loaders can safely ignore this information. But if we added
something like the unloadModule pointer at a later time, this would
require bumping the minimum version as well, because a module that would
try to veto the change and gets ignored by the loader (because it
doesn't even get called) might crash or otherwise be unstable. We could
just have a single version number, and make the module loader ignore
version numbers newer than the one it knows about, but this isn't too
hot for backward compatibility...
Something I don't really like (but have a feeling it might be a thing
that Avery might like!) is that of not knowing what UUIDs the module can
provide (for caching reasons, mostly, so that we don't have to load the
modules all the time to figure out where to get a component). I was
thinking of having this list of UUIDs in ModuleInfo, like mentioned
earlier, but that would require module writers to keep the list in sync
with what the module really does. If a UUID is supported and isn't in
the list, it might work the first time (because the module is not in the
cache), then never again. Maybe I could cut down on the non-determinism
by checking the list first, so that it would never work, consistently.
I think Avery wanted to be able to answer to whatever UUIDs he felt
like, for some reason, maybe you could explicitly state that you want to
be called all the time by putting the NULL UUID in your list. This would
be a rather evil thing to do, since it could slow down things noticeably
in a larger program. Maybe these should be used as a fallback only, if
another module has the wanted UUID in his list, it goes straight to him
without loading all the modules who used the NULL UUID in their list.
A variant that I thought of, since I don't like typing in the same thing
twice (and having to maintain both), was to not have the getObject
function pointer at all, but instead to have a list of a small structure
that would have a UUID and a function pointer. The NULL UUID trick would
still apply I suppose. This way, the function at the end (which could
easily be implemented automatically with templates or macros, so it's
not really extra work) wouldn't have to do a number of if's to figure
out whether or not it supports the UUID, it would just give back the
object right away. For module that only have one, two or three
components, this would be really easy.
If that last option is the one, I'm not sure whether the function should
get the UUID or not. Why should it be passed, if the function will only
be good for one? This would breaks the NULL UUID trick/feature, but
could be fixed by putting back the getObject function pointer, but
optional, to implement this trick/feature. Also, in C, I can easily
imagine people not taking advantage of the kind of dispatch table that
this would give you and just make a single function, with all the
pointers pointed at it, but this sounds like kind of a bad idea. It
would be easy to have a macro to help you define the functions, so how
would having it all in one function help?
Thanks for your feedback!
coming times, unless someone tells me that I'm a idiot and that
something else urgently needs help... :-)
* My current plan
Modules expose a single symbol that is a ModuleInfo structure (the real
name is a bit different, due to namespace issue, but I'll use this one
in this discussion). The ModuleInfo structure will have (at the
beginning, more on this later) a version field, and three function
pointers: getObject, loadModule and unloadModule.
Of these three, getObject is the only required one, and pretty much
match the method of the same name that IServiceHandler has and that the
module loader implements.
loadModule and unloadModule are optional (can be left NULL), and will be
called at the appropriate times. unloadModule would return a bool and
could veto the unloading of the module.
All three methods would get passed an object implementing IModule that
will have been created by the module loader, in addition to a
IServiceManager*. This IModule interface will have lock and unlock
methods that will be used to reference count the use of the module. When
unlock gets called and the count goes to zero, the module will be ready
to unload.
Additionnal information will get added to this structure in time, for
example a list of UUIDs supported by the module, for caching purposes,
and interface definition information, for reflection purposes (scripting
languages, recreating C/C++ headers from just the shared object, etc).
* Ideas and issues
I'm thinking of having *two* version numbers in ModuleInfo, one being
the version number of the structure used in this module, and the second
being the minimum version number that the module loader should support.
For example, if we add interface definition information, we would bump
the version number, but not the minimum version number, because older
module loaders can safely ignore this information. But if we added
something like the unloadModule pointer at a later time, this would
require bumping the minimum version as well, because a module that would
try to veto the change and gets ignored by the loader (because it
doesn't even get called) might crash or otherwise be unstable. We could
just have a single version number, and make the module loader ignore
version numbers newer than the one it knows about, but this isn't too
hot for backward compatibility...
Something I don't really like (but have a feeling it might be a thing
that Avery might like!) is that of not knowing what UUIDs the module can
provide (for caching reasons, mostly, so that we don't have to load the
modules all the time to figure out where to get a component). I was
thinking of having this list of UUIDs in ModuleInfo, like mentioned
earlier, but that would require module writers to keep the list in sync
with what the module really does. If a UUID is supported and isn't in
the list, it might work the first time (because the module is not in the
cache), then never again. Maybe I could cut down on the non-determinism
by checking the list first, so that it would never work, consistently.
I think Avery wanted to be able to answer to whatever UUIDs he felt
like, for some reason, maybe you could explicitly state that you want to
be called all the time by putting the NULL UUID in your list. This would
be a rather evil thing to do, since it could slow down things noticeably
in a larger program. Maybe these should be used as a fallback only, if
another module has the wanted UUID in his list, it goes straight to him
without loading all the modules who used the NULL UUID in their list.
A variant that I thought of, since I don't like typing in the same thing
twice (and having to maintain both), was to not have the getObject
function pointer at all, but instead to have a list of a small structure
that would have a UUID and a function pointer. The NULL UUID trick would
still apply I suppose. This way, the function at the end (which could
easily be implemented automatically with templates or macros, so it's
not really extra work) wouldn't have to do a number of if's to figure
out whether or not it supports the UUID, it would just give back the
object right away. For module that only have one, two or three
components, this would be really easy.
If that last option is the one, I'm not sure whether the function should
get the UUID or not. Why should it be passed, if the function will only
be good for one? This would breaks the NULL UUID trick/feature, but
could be fixed by putting back the getObject function pointer, but
optional, to implement this trick/feature. Also, in C, I can easily
imagine people not taking advantage of the kind of dispatch table that
this would give you and just make a single function, with all the
pointers pointed at it, but this sounds like kind of a bad idea. It
would be easy to have a macro to help you define the functions, so how
would having it all in one function help?
Thanks for your feedback!
--
Pierre Phaneuf
http://advogato.org/person/pphaneuf/
"I am denial, guilt and fear -- and I control you"
Pierre Phaneuf
http://advogato.org/person/pphaneuf/
"I am denial, guilt and fear -- and I control you"