On Thu, Feb 11, 2010 at 12:18 AM, Andreas Mohr
<[email protected]> wrote:
> On Wed, Feb 10, 2010 at 02:49:03PM +0100, Andreas Mohr wrote:
>> On Mon, Feb 08, 2010 at 01:14:20PM +0100, Koen Deforche wrote:
>> > Hey Andreas,
>> >
>> > 2010/2/1 Andreas Mohr <[email protected]>:
>> > If you want to serve multiple applications from different URLs, but it
>> > is okay if they are served from the same port (e.g. 80 or 8080), then
>> > I think the single-instance config should be no problem. I guess you
>> > are mainly looking for better interaction with the server allowing you
>> > to bind an entry point to a new 'createApplication' callback which you
>> > dlopen() from a plugin, right ?
>>
>> Yes, having a createApplication() implementation which dlopen()s
>> an app plugin would be the only feasible way for me now,
>> but OTOH this doesn't buy me anything either since there's no
>> destroyApplication() callback, thus lifetime of the dlopen() is unknown
>> and the plugin library would remain loaded forever, thus any rebuilds
>> of that plugin library (in order to provide updates to the WApplication)
>> won't make their way into the running process.
>
> Forget what I said: the "missing" destroyApplication function is not a problem
> at all as long as there's proper object encapsulation as it should be.
>
> Web session will run into timeout, witty server will destroy session
> (it will, right? OTOH not doing so would be a _very_ visible memory leak ;),
> thus WApplication object will get deleted together with its _internally_
> managed
> runtime library handling which at this point is able to close the library.
>
> OTOH I still don't know whether that would buy me much since I'd need an
> idle session (plugin not loaded) or hitting the (likely large) session timeout
> in order to reload the plugin by not having it loaded any more.
> Not to mention the "interesting" plugin/dlopen handling itself.
I could see it being pretty easy though. First, make sure your
plugin's have their version number in the name, to keep the names
unique (windows has a nasty habit of loading dll's with the same name,
even if in different areas, from the same area, unsure if *nix is the
same way, or use the timestamp in the name, that is easy to do).
I would then do something like have your plugin libraries expose a few
C functions (for easy linkage across binaries). Perhaps something
like:
unsigned int getFactoriesCount(void);
const char * getFactoryName(unsigned int index);
PluginApplication *getInstanceFromFactory(unsigned int index);
And use it like this (some pseudo-code to cut down on my typing, but
you get the idea:
typedef boost::unordered_map<std::string,boost::tuple<Library,unsigned
int> > FactoryCalls;
FactoryCalls factoryCalls;
boost::rwmutex factoryCallsMutex;
// Spawn an extra thread and have it call a function like this:
void watchPluginDirectory(const std::string &pathname, bool &threadAlive)
{
while(threadAlive)
{
if(directoryContentsHaveChanged())
{
size_t iEnd = directoryContentsChanged();
for(size_t i=0;i<iEnd;++i)
{
const std::string newLibraryPath =
directoryContentChanged(i);
if(endswith_nocase(newLibraryPath, ".wt") // Or
whatever extension you want your plugins to have, or omit this line if
none
{
Library newLibrary(newLibraryPath); // a smart
pointer container with helper class?
if(newLibrary.isValid())
{
unsigned int jEnd =
newLibrary.getFunction<unsigned int(void)>("getFactoriesCount")(); //
of course add error catching...
boost::function<const char*(unsigned int)>
getFactoryName(newLibrary->getFunction<const char*(unsigned
int)>("getFactoryName"));
for(unsigned int j=0;j<jEnd;++j)
{
boost::writer_lock(factoryCallsMutex);
const std::string factoryName =
getFactoryName(j);
factoryCalls[factoryName] =
make_tuple(newLibrary,j);
}
}
else
{
delete newLibrary;
}
}
}
}
boost::thread::sleep(5.0); // check once every 5 seconds or so?
}
}
// then have your app derive from this instead of Wt:WApplication
class PluginApplication : public Wt::WApplication
{
public:
// forward the constructors here of course
public: // or private with setters or a friend function or whatever
Library myLibrary_;
unsigned int myLibraryFactoryIndex_;
}
// then have a creation function
PluginApplication *createApplication(const std::string &pluginName)
{
boost::reader_lock(factoryCallsMutex);
FactoryCalls::const_iterator found = factoryCalls.find(pluginName);
if(found==factoryCalls.end()) return 0;
Library &lib = get<0>(found->second);
unsigned int index = get<1>(found->second);
PluginApplication *app =
lib.getFunction<PluginApplication*(unsigned
int)>("getInstanceFromFactory")(index);
app->myLibrary_ = lib;
app->myLibraryFactoryIndex_ = index;
return app;
}
And so forth, that should get the general idea, but it always looks up
the plugin name in the most recently loaded library (if a newer
library of a certain version has a missing plugin, the older one is
used). You can of course add more smarts for adding the initial
filenames (which I did not handle here, that is too easy and not the
point of this example) to add all plugins in order from oldest to
newest.
But yeah, it watches the directory (different ways on *nix and
windows, so pretend the above is an abstraction), if there is a new
file, iterate over those file and load them. Once the library is
loaded see how many applications it contains and iterate over those
loading them into the map.
Then look in the map for the most recent one (older ones are just
silently overwritten, and with a simple smart-pointer style Library
class can unload the Library once all references to its internal
implementation are gone, *very* simple code for that), load it.
Do note, if you use different memory CRT's between the app and dll's,
nasty crash you get, this can be fixed with a smart pointer to the
applications, if Wt supports smart pointers that is (if not, you are
pretty well screwed without some fancy trickery of passing a
preallocated memory chunk by a class faking to be a WApplication at
the time of its construction of some preset sizes and picking one of
at least the necessary size or larger, yes, very nasty, so hopefully
We supports smart pointers, if not, again, you are pretty well
screwed, so be sure to use the same CRT's in that case).
Hmm, I could see this being useful, if I get time I may implement it
and submit a patch if anyone is interested.
------------------------------------------------------------------------------
SOLARIS 10 is the OS for Data Centers - provides features such as DTrace,
Predictive Self Healing and Award Winning ZFS. Get Solaris 10 NOW
http://p.sf.net/sfu/solaris-dev2dev
_______________________________________________
witty-interest mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/witty-interest