> On Feb. 28, 2014, 8:41 p.m., Matthew Dawson wrote: > > While I'm fine with the idea behind this optimization, I worry that this > > implementation could create situations were a configuration change is not > > picked up by the system. For instance, what happens if the user doesn't > > immediately call readConfig? This could create some subtle bugs in > > downstream code. > > > > I had two ideas for how this optimization could be implemented: > > 1) Lazy load the KConfig object the first time it is used. Then, in > > readConfig, the call to be reparseConfiguration could be avoided if the > > KConfig is created due to its call. This would retain the current > > behaviour, while ensuring readConfig reads in the most configuration. > > Other uses of the KConfig will have to ensure the KConfig object has > > already been created, and if the user calls one of those functions before > > readConfig, they will still double read the configuration. But since this > > is just status quo, I'm not too worried. > > 2) Alternately, create a set of construction functions, like make_shared, > > that imitate the creation of a KConfigSkeleton subclass, and then reading > > the configuration through readConfig. Internally, it can use a private > > readConfig function to ensure the configuration is no re-read. This would > > require changes to applications to avoid the extra configuration call, > > unfortunately. > > > > I saw RR #115964, and I assume that some of the reductions to the readings > > of oxygenrc are caused by the sharing the KConfig between some > > KConfigSkeleton's? If so, I'd suggest implementing solution 1 for the > > general case, and then making a special construction function to handle > > shared KConfig's. I don't want to avoid calling reparseConfiguration > > without some warning around this, as it may again cause some surprises. A > > new appropriately named function shouldn't be too bad though, as opposed to > > changing the behaviour of the constructor. > > David Faure wrote: > I've been thinking about this too, but what good is a KConfigSkeleton if > you don't call readSettings() on it? You can't read any settings from it > then, so all you can do is a) keep it around for later or b) use it purely > for writing out a new config file. Use case b) is no problem, so I think > we're talking about use case a). Yes in theory an app could see a behavior > change in that the config file is loaded from disk at the time of creating > the skeleton rather than at the time of calling readConfig the first time. > But this is why I'm making this change in 5.0 and not in 4.13. I think it's > an acceptable behavior (matching KConfig's behavior more closely - it parses > in the ctor, not in readEntry) and I doubt it affects many apps, since all I > see everywhere is singletons - i.e. the KConfigSkeleton is created on demand > at the time where it's first needed, therefore the ctor is immediately > followed by readConfig. > > My alternative idea (let's call it "3" to complete your list) was to pass > a flag to the KConfig constructor to tell it "don't parse now", and setting > that flag from the KConfigSkeleton constructor. Then readConfig can keep > always calling reparseConfiguration(). This would work, right? > > Your suggestion 1 is somewhat equivalent, but since one of the ctors for > KCoreConfigSkeleton takes a KSharedConfig::Ptr as input, it's not applicable, > we can't delay the creation of the kconfig within KCoreConfigSkeleton since > it's created beforehand by the application. > Your suggestion 2 requires changing all the apps, which lowers the > benefits of the optimization. > > Matthew Dawson wrote: > I agree, it is a weird use case, and the software should probably be > adjusted. However, if an app does rely on that, it is very hard for the > author of the software to notice the change, even with the port to 5. If I > just looked at the functions names, I'd expect readConfig to read the file > all the time. Following the principle of least surprise, I'd like to avoid > readConfig ever not reading the file. > > I'm fine with your alternate idea. I prefer that over my first idea, as > it effectively does the same thing while being less invasive. > > For my second suggestion, I realize its downsides. I just like following > the principle of least surprise. If your alternate idea is implemented, I > believe that would cover most cases. Suggestion 2 can then be implemented, > and its related constructor could be marked deprecated. This would allow for > existing programs to continue working, while allowing developers to change > their code to take advantage of the optimization. > > As I stated earlier, I'm not sure about who KDE wants to handle source > compatibility with kdelibs. I also wouldn't mind just removing the second > constructor, forcing all users to upgrade their code. Since the function is > a drop in replacement, it wouldn't be that hard for developers to upgrade. > > David Faure wrote: > I don't agree that "readConfig" necessarily implies reading from disk. > All of the KConfig API says things like "readEntry", which does not read from > disk, but uses the in-memory cache. Doing the same one level up, in > KConfigSkeleton, would be rather consistent. > > Your second suggestion is still a bit unclear to me; some function > calling "new Configuration" (assuming that's the name of the derived class, > like in kdnssd-framework/src/settings.h) is already what the generated code > has with its self() method that takes care of calling the constructor anyway. > So the app code is then just Configuration::self()->readConfig(); > Configuration::someValue() (see e.g. > kdnssd-framework/src/mdnsd-domainbrowser.cpp). > I'm not sure what you'd do in there to avoid the double parsing. > > My own alternative idea leads to > http://www.davidfaure.fr/2014/delayedparsing.diff which indeed helps less - > still 3 parsings of oxygenrc, because of the three skeletons sharing the same > KSharedConfig, and each of them calling readConfig. As you anticipated in > your paragraph about RR #115964. What's your suggestion then for this issue? > You suggest a named function - but changing names doesn't matter, this is > called from generated code there too. See > kde-workspace/libs/oxygen/oxygeninactiveshadowconfiguration.cpp (in the > builddir) for instance. > > My initial patch (the one in this RR) fixes that testcase fully because > all skeletons share the same ready-to-use KSharedConfig, and none of them > call reparseConfiguration in the first call to readConfig(). I.e. with > sharing in mind, it makes more sense to say that it's the job of the KConfig > to do the initial parsing than of KConfigSkeleton (of which there could be > more than one). Of course this approach doesn't help with subsequent > reparsings though (on a "config changed" signal) ... each readConfig() will > trigger a full reparsing of the config file behind the scenes.... not great. > It's starting to sound like we should offer a variant of readConfig() which > just never calls reparseConfiguration(), i.e. leave that up to the > application to deal with. At least for oxygen that would be perfect :-) For > simple apps readConfig() would stay. > > Conclusion: if you don't like this RR, I'm ok with > http://www.davidfaure.fr/2014/delayedparsing.diff + a readConfig(NoReparse) > or a separate method for which I can't find a good name right now. > > Matthew Dawson wrote: > While readConfig doesn't necessarily imply reading values from disk, the > documentation specifically states that it does ( > http://api.kde.org/frameworks-api/frameworks5-apidocs/kconfig/html/classKCoreConfigSkeleton.html#a3772e0cd58790c312b8d0802bc78a70c > ). As we both agree, most applications don't care about a change in > behaviour here. I just worry some might in a subtle way that is hard to > detect with this change. If readConfig broke in obvious way because of this > change, I wouldn't care as developers would know to make this change. > > For my second suggestion, I'm basically suggesting a static function that > looks like: > template<typename T> > T *makeConfigSkeletonWithoutReparse<T>(KSharedConfig::Ptr ptr) > { > T *obj = new T(ptr); > obj->privateReadConfigWithoutReparse(); > > return obj; > } > The oxygen code can just call this function to get the right object back, > and the privateReadConfigWithoutReparse will avoid the reparseConfiguration > call. Generated code from kconfig_compiler can easily use this for > construction, and projects can automatically pick up the enhancement. It > would be similar to using the KConfig::openConfig call. > > I'd be fine with an alternate readConfig (name suggestion: parseConfig, > thus (re-)parsing the given KConfig). Then my second suggestion can just go > away. I do worry that readConfig is currently a virtual, and thus > parseConfig won't make use of it. Probably best would be to make readConfig > non-virtual, and make it basically call KConfig::reparseConfiguration, then > call parseConfig. Then users just override parseConfig instead. Code make > break because of this, but any code that moves to using c++11's override will > break in an obvious way. Any other code will break if readConfig is called > through virtual dispatch, so it should be relatively obvious. Add some > documentation updates, and it I think it will go over ok. > > For delayedparsing.diff, can you throw it up on RB? I'd like to get it > in too, but I have a few thoughts on it and I rather do that in a separate > request. > > David Faure wrote: > I don't like the template-factory-method, it makes for horrible API IMHO > :) > > But I love your parseConfig() suggestion, so I shall go ahead an > implement it. It is much better because it fixes two issues: the N reparse on > startup when sharing the same config object between N skeletons, but *also* > the N reparse whenever we are notified that the config changed. (all my > patches until now didn't fix that). If e.g. oxygen takes care of calling > reparseConfiguration by itself upon a change, then it can simply call > parseConfig in all skeletons. > > The one thing that might be confusing is the naming: > * in kconfigskeleton, read(Config) = from disk, parse(Config) = in > memory > * in kconfig, read(Entry) = in memory, reparse(Configuration) = from > disk > !!! > > If only I could rewrite the past, I would swap that in kconfigskeleton. > > Alternatively, it's probably better to find another name for the new > method... loadConfig? (as in, loading from kconfig to skeleton members) > > Matthew Dawson wrote: > I'm not sure about loadConfig, as it kinda of implies loading from disk, > so I think the best solution would be rewriting history :) > > Instead, maybe the best is to use use loadConfig to mean reading from > disk. So the matrix looks like: > > * kconfigskeleton: load(Config) = from disk, parse(Config) = in memory > * kconfig: (re?)load(Configuration) = from disk, read(Entry) = > memory -> though maybe even change this to parseEntry? > > Assuming readEntry stays the same, the load* functions are just a rename. > So the old names can be marked as deprecated, and the documentation points > to the names. SC/BC is simple, since the one function just calls the other. > And people still using readConfig get a huge warning about the fact they need > to change to parseConfig. > > For more symmetry, readEntry can change to parseEntry, with a similar > deprecation. I'm not sure this is really necessary, as parseConfig is > different from readEntry. Also, this change can be done separately, so it > shouldn't hold up the more useful optimization. > > Alternate thought: why are the methods even named readEntry? That > implies they actually parse the data returned. They could just as easily be > named fetchEntry, getEntry, or just entry. writeEntry is the same, it > doesn't actually hit the disk, so it could be put* or set*. Thoughts? > > David Faure wrote: > I like load(). Anyone seeing two calls to load() on the same object will > wonder about the possible bad effect on performance, since it sounds heavy :) > > Renaming readEntry: veto! This is the most called method of them all, the > porting effort would be huge, just to make the two of us happy with naming :-) > And read/write from memory doesn't seem so crazy (QBuffer, QMetaProperty, > Q*Item etc. do this too). > > Overall I like your suggested matrix, but now we should look at > minimizing the porting. > > KConfig::reparseConfiguration() wasn't misleading, before KConfigXT. The > only reason for "reparsing" is reading the ini file on disk an updating the > in-memory cache. However the problem with KConfigSkeleton is that it's one > layer on top of KConfig. So parsing/loading there is all about getting stuff > from KConfig. So yeah, to align naming, I could see loadConfiguration() [Qt > says: no abbreviations] in both layers for "loading from disk". (Not > "reload", given the new Delayed flag (to be renamed to DelayedLoading), in > which case this would also be about the initial loading from disk). > > It just occured to me that all this "Config" or "Configuration" in the > method names is rather useless, this is about KConfig* anyway. > So how about just load()? > > With all this in mind my current suggestion for a game plan would be: > 1) New method KConfigSkeleton::read() for reading from KConfig without > loading (i.e. without disk usage) > 2) KConfigSkeleton::readConfig() deprecated for new name > KConfigSkeleton::load() > 3) KConfig::reparseConfiguration() deprecated for new name KConfig::load() > > What do you think? > > > Matthew Dawson wrote: > I agree the porting effort for renaming readEntry would be huge. I just > threw it out there as it popped into mind. > > I like your final game plan, so I think it is the best path forward. I > just have a couple of additions. > > Since we are going down the renaming path, I was thinking of the write > side. I think it be good to standardize the naming. Right now KConfig uses > sync(), while KCoreConfigSkeleton uses writeConfig(). Since writeEntry() > exists, and is the larger porting effort, I think both should become sync(). > So, the game plan would get: > > 4) Deprecate KCoreConfigSkeleton::writeConfig() for the new name > KCoreConfigSkeleton::sync(). > > Also, don't forget the usr*Config functions. I don't think they should > just be renamed, as quite a bit of software relies on them. I think > replacement functions could be created, and those replacements call the > originals. So, also add: > > 5) Deprecate KCoreConfigSkeleton::usr*Config() for > KCoreConfigSkeleton::usrLoad()/KCoreConfigSkeleton::usrSync(). > > David Faure wrote: > Hmm, sync() could sound a bit like what load() does, i.e. "the file > changed on disk, sync the kconfig object with it, i.e. load it". But of > course historically it didn't mean that. > > Since it's the opposite of load(), naming it save() would have seemed > much more intuitive to me. > > Especially since (see documentation), while it merges with independent > changes on disk, it doesn't actually load these changes into the object. So > it's really just a save(), not a sync=save+load. > > In suggestion 5, you mean adding non-virtual forwarding methods? But > these are protected virtual methods, so it would be pointless. IOW the only > API there is "what should I reimplement", which can only be one or the > other... > > > > Matthew Dawson wrote: > I choose sync for similarity, and to avoid porting work. In KConfig, > sync() doesn't re-read anything, so I agree that calling it save() makes more > sense. I'd even prefer to rename them. Unforunately, lxr makes it hard to > find a re-implementation of sync (since it is virtual). It probably wouldn't > be too bad to deprecate and change over to save() though. > > Oddly enough, KCoreConfigSkeleton does in fact do a read after the write. > I can't find out why, as I traced the code back to when it was committed, > and it was just part of the file with no rationale given. I want to > investigate that, as it seems pointless. > > I meant to rename the usr*Config functions. In the interest of > maintaining SC/BC, I was thinking to also keep the original methods. I'd be > just as happy to kick them to the curb, but they have some significant usage > according lxr.kde.org. Keeping them around would at least allow for porting > to be easier. > > David Faure wrote: > Replying in order: > > In all my years in KDE I have never seen someone reimplement > KConfig::sync() indeed. We could de-virtualize it. > > Read after write pointless? Well, the values have been updated on disk > and are available, we might as well use them? Not 100% sure. It's true that > in the cases where we care for showing changes by other processes, we ensure > there's some notification anyway, in which case we reload anyway > (independently from the saving code). > > One thing I realized when writing 116845: the name of the virtual method > usrReadConfig() is just fine. We should NOT rename that to usrLoad() since > it's called by read(), not only by load(). load() -> read() -> > usrReadConfig(). > > > Matthew Dawson wrote: > In order as well: > > Looking over how the classes behave, I believe it exists as a virtual so > that KConfig and KConfigGroup can both behave as KConfigBase, allowing for > software to take a KConfigBase, and store their settings in there, along with > request it to save. So renaming sync() -> save() should be safe, and a > compatibility save() can be kept in KConfigBase, keeping SC. > > Unless the values have changed on disk, then saving should mean > re-reading the values will produce the exact same result. The only values > that can change are other un-modified values. The api documentation also > doesn't mention this. I rather have a sync() function that writes and the > reads, as save() doesn't imply there will be a read. At least for now, > removing the extra read would be good. If users end up often doing a > save+load, a simple sync() function can be added for that case. > > Oops, I meant to rename it to usrRead(), not usrLoad().
OK for sync() -> save(), I'll make another RR (virtual method, so this is urgent). I don't see any need for save+load, as I said, because any app whose config might get changed externally (and who cares about it) already has to reload on changes, so this is handled before saving time. usrReadConfig() -> usrRead() seems like too big a SC (given that it's a virtual method meant to be reimplemented) to me. Apps don't use Q_DECL_OVERRIDE right now, so they'll get bitten. - David ----------------------------------------------------------- This is an automatically generated e-mail. To reply, visit: https://git.reviewboard.kde.org/r/116461/#review51374 ----------------------------------------------------------- On Feb. 27, 2014, 9:18 p.m., David Faure wrote: > > ----------------------------------------------------------- > This is an automatically generated e-mail. To reply, visit: > https://git.reviewboard.kde.org/r/116461/ > ----------------------------------------------------------- > > (Updated Feb. 27, 2014, 9:18 p.m.) > > > Review request for KDE Frameworks and Matthew Dawson. > > > Repository: kconfig > > > Description > ------- > > KConfigSkeleton: avoid calling reparseConfiguration() immediately after > creation. > > KConfig already parses the config files from disk in the constructor, > which is necessary for non-KConfigXT users. However when using KConfigXT > the first thing one has to do after creation is to call readConfig(), > which should therefore not call reparseConfiguration the first time. > > strace -e open kate |& grep -v NOENT | grep oxygenrc | wc -l > went from 4 to 1 --> bingo, goal reached! > (and when looking for kdeglobals, from 10 to 7) > > > Diffs > ----- > > src/core/kcoreconfigskeleton.cpp 9c5fb4a80d500e81b483b749a137ad5f2c99a55f > src/core/kcoreconfigskeleton_p.h 0b020ed3493186e699d872ddc7a9f9294d797a95 > > Diff: https://git.reviewboard.kde.org/r/116461/diff/ > > > Testing > ------- > > (see commit log) + unittests in kconfig still pass. > > > Thanks, > > David Faure > >
_______________________________________________ Kde-frameworks-devel mailing list Kde-frameworks-devel@kde.org https://mail.kde.org/mailman/listinfo/kde-frameworks-devel