Index: libs/libmythtv/tvosdmenuentry.h =================================================================== --- libs/libmythtv/tvosdmenuentry.h (revision 22742) +++ libs/libmythtv/tvosdmenuentry.h (working copy) @@ -63,7 +63,7 @@ TVOSDMenuEntry* FindEntry(QString category); private: - void GetEntriesFromDB(void); + int GetEntriesFromDB(void); void InitDefaultEntries(void); QList curMenuEntries; Index: libs/libmythtv/tv_play.cpp =================================================================== --- libs/libmythtv/tv_play.cpp (revision 22742) +++ libs/libmythtv/tv_play.cpp (working copy) @@ -839,45 +839,167 @@ pseudoChangeChanTimerId(0), speedChangeTimerId(0), errorRecoveryTimerId(0), exitPlayerTimerId(0) { - VERBOSE(VB_PLAYBACK, LOC + "ctor"); + VERBOSE(VB_PLAYBACK, LOC + "ctor -- begin"); setObjectName("TV"); keyRepeatTimer.start(); - db_idle_timeout = gContext->GetNumSetting("LiveTVIdleTimeout", 0); - db_idle_timeout *= 60 * 1000; // convert from minutes to ms. - db_udpnotify_port = gContext->GetNumSetting("UDPNotifyPort", 0); - db_browse_max_forward = gContext->GetNumSetting("BrowseMaxForward",240)*60; - db_playback_exit_prompt= gContext->GetNumSetting("PlaybackExitPrompt", 0); - db_autoexpire_default = gContext->GetNumSetting("AutoExpireDefault", 0); + QStringList keys; + QStringList values; + keys.push_back("LiveTVIdleTimeout"); + values.push_back("0"); + keys.push_back("UDPNotifyPort"); + values.push_back("0"); + keys.push_back("BrowseMaxForward"); + values.push_back("240"); + keys.push_back("PlaybackExitPrompt"); + values.push_back("0"); + keys.push_back("AutoExpireDefault"); + values.push_back("0"); + keys.push_back("AutomaticSetWatched"); + values.push_back("0"); + keys.push_back("EndOfRecordingExitPrompt"); + values.push_back("0"); + keys.push_back("JumpToProgramOSD"); + values.push_back("1"); + keys.push_back("GuiSizeForTV"); + values.push_back("0"); + keys.push_back("WatchTVGuide"); + values.push_back("0"); + keys.push_back("AltClearSavedPosition"); + values.push_back("1"); + keys.push_back("JobsRunOnRecordHost"); + values.push_back("0"); + keys.push_back("EnableDVDBookmark"); + values.push_back("0"); + keys.push_back("ContinueEmbeddedTVPlay"); + values.push_back("0"); + keys.push_back("UseFixedWindowSize"); + values.push_back("1"); + keys.push_back("PersistentBrowseMode"); + values.push_back("0"); + keys.push_back("BrowseAllTuners"); + values.push_back("0"); + keys.push_back("ChannelOrdering"); + values.push_back("channum"); - db_auto_set_watched = gContext->GetNumSetting("AutomaticSetWatched", 0); - db_end_of_rec_exit_prompt = - gContext->GetNumSetting("EndOfRecordingExitPrompt", 0); - db_jump_prefer_osd = gContext->GetNumSetting("JumpToProgramOSD", 1); - db_use_gui_size_for_tv = gContext->GetNumSetting("GuiSizeForTV", 0); - db_start_in_guide = gContext->GetNumSetting("WatchTVGuide", 0); - db_toggle_bookmark = gContext->GetNumSetting("AltClearSavedPosition",1); - db_run_jobs_on_remote = gContext->GetNumSetting("JobsRunOnRecordHost", 0); - db_use_dvd_bookmark = gContext->GetNumSetting("EnableDVDBookmark", 0); - db_continue_embedded = gContext->GetNumSetting( - "ContinueEmbeddedTVPlay", 0); - db_use_fixed_size = gContext->GetNumSettingOnHost( - "UseFixedWindowSize", gContext->GetHostName(), 1); - db_browse_always = gContext->GetNumSetting("PersistentBrowseMode", 0); - db_browse_all_tuners = gContext->GetNumSetting("BrowseAllTuners", 0); + keys.push_back("CustomFilters"); + values.push_back(""); + keys.push_back("ChannelFormat"); + values.push_back(" "); + keys.push_back("TimeFormat"); + values.push_back("h:mm AP"); + keys.push_back("ShortDateFormat"); + values.push_back("M/d"); + keys.push_back("SmartChannelChange"); + values.push_back("0"); + keys.push_back("IndividualMuteControl"); + values.push_back("0"); + keys.push_back("UseArrowAccels"); + values.push_back("1"); + keys.push_back("OSDGeneralTimeout"); + values.push_back("2"); + keys.push_back("OSDProgramInfoTimeout"); + values.push_back("3"); + keys.push_back("TryUnflaggedSkip"); + values.push_back("0"); + + keys.push_back("ChannelGroupDefault"); + values.push_back("-1"); + keys.push_back("BrowseChannelGroup"); + values.push_back("0"); + keys.push_back("SmartForward"); + values.push_back("0"); + keys.push_back("StickyKeys"); + values.push_back("0"); + keys.push_back("FFRewReposTime"); + values.push_back("100"); + keys.push_back("FFRewReverse"); + values.push_back("1"); + + keys.push_back("ChannelGroupDefault"); + values.push_back("-1"); + keys.push_back("BrowseChannelGroup"); + values.push_back("0"); + keys.push_back("VbiFormat"); + values.push_back(""); + keys.push_back("DecodeVBIFormat"); + values.push_back(""); + + int ff_rew_def[8] = { 3, 5, 10, 20, 30, 60, 120, 180 }; + for (uint i = 0; i < sizeof(ff_rew_def)/sizeof(ff_rew_def[0]); i++) + { + keys.push_back(QString("FFRewSpeed%1").arg(i)); + values.push_back(QString::number(ff_rew_def[i])); + } + + QMap map; + bool ok = MythDB::getMythDB()->GetSettings( + keys, values, MythDB::kCacheDefaults); + + if (ok) + { + for (uint i = 0; i < (uint) keys.size(); i++) + map[keys[i]] = values[i]; + } + + // convert from minutes to ms. + db_idle_timeout = map["LiveTVIdleTimeout"].toInt() * 60 * 1000; + db_udpnotify_port = map["UDPNotifyPort"].toInt(); + db_browse_max_forward = map["BrowseMaxForward"].toInt() * 60; + db_playback_exit_prompt= map["PlaybackExitPrompt"].toInt(); + db_autoexpire_default = map["AutoExpireDefault"].toInt(); + db_auto_set_watched = map["AutomaticSetWatched"].toInt(); + db_end_of_rec_exit_prompt = map["EndOfRecordingExitPrompt"].toInt(); + db_jump_prefer_osd = map["JumpToProgramOSD"].toInt(); + db_use_gui_size_for_tv = map["GuiSizeForTV"].toInt(); + db_start_in_guide = map["WatchTVGuide"].toInt(); + db_toggle_bookmark = map["AltClearSavedPosition"].toInt(); + db_run_jobs_on_remote = map["JobsRunOnRecordHost"].toInt(); + db_use_dvd_bookmark = map["EnableDVDBookmark"].toInt(); + db_continue_embedded = map["ContinueEmbeddedTVPlay"].toInt(); + db_use_fixed_size = map["UseFixedWindowSize"].toInt(); + db_browse_always = map["PersistentBrowseMode"].toInt(); + db_browse_all_tuners = map["BrowseAllTuners"].toInt(); + QString channelOrdering= map["ChannelOrdering"]; + baseFilters += map["CustomFilters"]; + db_channel_format = map["ChannelFormat"]; + db_time_format = map["TimeFormat"]; + db_short_date_format = map["ShortDateFormat"]; + smartChannelChange = map["SmartChannelChange"].toInt(); + MuteIndividualChannels = map["IndividualMuteControl"].toInt(); + arrowAccel = map["UseArrowAccels"].toInt(); + osd_general_timeout = map["OSDGeneralTimeout"].toInt(); + osd_prog_info_timeout = map["OSDProgramInfoTimeout"].toInt(); + tryUnflaggedSkip = map["TryUnflaggedSkip"].toInt(); + channel_group_id = map["ChannelGroupDefault"].toInt(); + browse_changrp = map["BrowseChannelGroup"].toInt(); + smartForward = map["SmartForward"].toInt(); + stickykeys = map["StickyKeys"].toInt(); + ff_rew_repos = map["FFRewReposTime"].toFloat() * 0.01f; + ff_rew_reverse = map["FFRewReverse"].toInt(); + channel_group_id = map["ChannelGroupDefault"].toInt(); + browse_changrp = map["BrowseChannelGroup"].toInt(); + QString beVBI = map["VbiFormat"]; + QString feVBI = map["DecodeVBIFormat"]; + + for (uint i = 0; i < sizeof(ff_rew_def)/sizeof(ff_rew_def[0]); i++) + ff_rew_speeds.push_back(map[QString("FFRewSpeed%1").arg(i)].toInt()); + + // process it.. + if (db_browse_all_tuners) { QMutexLocker locker(&browseLock); - QString channelOrdering = gContext->GetSetting( - "ChannelOrdering", "channum"); db_browse_all_channels = ChannelUtil::GetChannels( 0, true, "channum, callsign"); ChannelUtil::SortChannels( db_browse_all_channels, channelOrdering, true); } + vbimode = VBIMode::Parse(!feVBI.isEmpty() ? feVBI : beVBI); + sleep_times.push_back(SleepTimerInfo(QObject::tr("Off"), 0)); sleep_times.push_back(SleepTimerInfo(QObject::tr("30m"), 30*60)); sleep_times.push_back(SleepTimerInfo(QObject::tr("1h"), 60*60)); @@ -892,6 +1014,7 @@ player.push_back(new PlayerContext("player")); playerActive = 0; playerLock.unlock(); + VERBOSE(VB_PLAYBACK, LOC + "ctor -- end"); } /** \fn TV::Init(bool) @@ -902,6 +1025,8 @@ */ bool TV::Init(bool createWindow) { + VERBOSE(VB_PLAYBACK, LOC + "Init -- begin"); + MSqlQuery query(MSqlQuery::InitCon()); if (!query.isConnected()) { @@ -910,35 +1035,6 @@ return false; } - baseFilters += gContext->GetSetting("CustomFilters"); - db_channel_format =gContext->GetSetting("ChannelFormat"," "); - db_time_format = gContext->GetSetting("TimeFormat", "h:mm AP"); - db_short_date_format = gContext->GetSetting("ShortDateFormat", "M/d"); - smartChannelChange = gContext->GetNumSetting("SmartChannelChange", 0); - MuteIndividualChannels=gContext->GetNumSetting("IndividualMuteControl", 0); - arrowAccel = gContext->GetNumSetting("UseArrowAccels", 1); - osd_general_timeout = gContext->GetNumSetting("OSDGeneralTimeout", 2); - osd_prog_info_timeout= gContext->GetNumSetting("OSDProgramInfoTimeout", 3); - tryUnflaggedSkip = gContext->GetNumSetting("TryUnflaggedSkip", 0); - channel_group_id = gContext->GetNumSetting("ChannelGroupDefault", -1); - browse_changrp = gContext->GetNumSetting("BrowseChannelGroup", 0); - smartForward = gContext->GetNumSetting("SmartForward", 0); - stickykeys = gContext->GetNumSetting("StickyKeys"); - ff_rew_repos = gContext->GetNumSetting("FFRewReposTime", 100)/100.0; - ff_rew_reverse = gContext->GetNumSetting("FFRewReverse", 1); - int def[8] = { 3, 5, 10, 20, 30, 60, 120, 180 }; - for (uint i = 0; i < sizeof(def)/sizeof(def[0]); i++) - ff_rew_speeds.push_back( - gContext->GetNumSetting(QString("FFRewSpeed%1").arg(i), def[i])); - - vbimode = VBIMode::Parse(gContext->GetSetting("VbiFormat")); - QString feVBI = gContext->GetSetting("DecodeVBIFormat", ""); - if (!feVBI.isEmpty()) - vbimode = VBIMode::Parse(gContext->GetSetting(feVBI)); - - channel_group_id = gContext->GetNumSetting("ChannelGroupDefault", -1); - browse_changrp = gContext->GetNumSetting("BrowseChannelGroup", 0); - if (browse_changrp && (channel_group_id > -1)) { m_channellist = ChannelUtil::GetChannels(0, true, "channum, callsign", channel_group_id); @@ -947,6 +1043,8 @@ m_changrplist = ChannelGroup::GetChannelGroups(); + VERBOSE(VB_PLAYBACK, LOC + "Init -- end channel groups"); + if (createWindow) { bool fullscreen = !gContext->GetNumSetting("GuiSizeForTV", 0); @@ -1040,6 +1138,7 @@ mainLoopCondLock.unlock(); + VERBOSE(VB_PLAYBACK, LOC + "Init -- end"); return true; } Index: libs/libmythtv/tvosdmenuentry.cpp =================================================================== --- libs/libmythtv/tvosdmenuentry.cpp (revision 22742) +++ libs/libmythtv/tvosdmenuentry.cpp (working copy) @@ -238,54 +238,67 @@ curMenuEntries.append(new TVOSDMenuEntry( "SLEEP", 1, 1, 1, 1, "Sleep")); - GetEntriesFromDB(); - if (GetCount() != curMenuEntries.count()) + int preload_cnt = curMenuEntries.size(); + int loaded_cnt = GetEntriesFromDB(); + if (preload_cnt != loaded_cnt) UpdateDB(); } int TVOSDMenuEntryList::GetCount(void) { - MSqlQuery query(MSqlQuery::InitCon()); - if (!query.isConnected()) - return curMenuEntries.count(); + return (int) curMenuEntries.size(); +} - query.prepare ("SELECT COUNT(osdcategory) FROM tvosdmenu;"); - if (query.exec() && query.isActive() && query.size() > 0) +// Returns the number of entries intialized from database. +int TVOSDMenuEntryList::GetEntriesFromDB(void) +{ + typedef QList::iterator Lit; + QMap catmap; + Lit it = curMenuEntries.begin(); + QString catlist(""); + for (; it != curMenuEntries.end(); ++it) { - query.next(); - return query.value(0).toUInt(); + QString cat = (*it)->GetCategory(); + catlist += QString("'%1',").arg(cat); + catmap[cat] = it; } - MythDB::DBError(LOC + "GetCount()", query); - return 0; -} + if (catlist.isEmpty()) + return 0; -void TVOSDMenuEntryList::GetEntriesFromDB(void) -{ + catlist = catlist.left(catlist.length() - 1); MSqlQuery query(MSqlQuery::InitCon()); - if (!query.isConnected()) - return; + query.prepare( + QString( + "SELECT osdcategory, livetv, recorded, video, dvd " + "FROM tvosdmenu " + "WHERE osdcategory IN (%1)").arg(catlist)); - TVOSDMenuEntry *entry = NULL; - QString osdcategory; - QListIterator cm = GetIterator(); - while (cm.hasNext()) + if (!query.exec()) { - entry = cm.next(); - osdcategory = entry->GetCategory(); - query.prepare("SELECT livetv, recorded, video, dvd " - "FROM tvosdmenu WHERE osdcategory = :OSDCATEGORY;"); - query.bindValue(":OSDCATEGORY", osdcategory); + MythDB::DBError("TVOSDMenuEntryList::GetEntriesFromDB()", query); + return 0; + } - if (query.exec() && query.isActive() && query.size() == 1) + int cnt = 0; + while (query.next()) + { + QString cat = query.value(0).toString(); + QMap::iterator mit = catmap.find(cat); + if (mit == catmap.end()) { - query.next(); - entry->UpdateEntry(query.value(0).toInt(), - query.value(1).toInt(), - query.value(2).toInt(), - query.value(3).toInt()); + VERBOSE(VB_IMPORTANT, QString("TVOSDMenuEntryList, Error: ") + + QString("Unable to find category '%1'").arg(cat)); + continue; } + + (**mit)->UpdateEntry(query.value(1).toInt(), + query.value(2).toInt(), + query.value(3).toInt(), + query.value(4).toInt()); + cnt++; } + return cnt; } void TVOSDMenuEntryList::UpdateDB(void) Index: libs/libmythtv/videoout_xv.h =================================================================== --- libs/libmythtv/videoout_xv.h (revision 22742) +++ libs/libmythtv/videoout_xv.h (working copy) @@ -209,6 +209,7 @@ int non_xv_fps; PixelFormat non_xv_av_format; time_t non_xv_stop_time; + bool first_show; // Basic XvMC drawing info XvMCBufferSettings *xvmc_buf_attr; Index: libs/libmythtv/videoout_xv.cpp =================================================================== --- libs/libmythtv/videoout_xv.cpp (revision 22742) +++ libs/libmythtv/videoout_xv.cpp (working copy) @@ -114,6 +114,7 @@ XJ_non_xv_image(0), non_xv_frames_shown(0), non_xv_show_frame(1), non_xv_fps(0), non_xv_av_format(PIX_FMT_NB), non_xv_stop_time(0), + first_show(true), xvmc_buf_attr(new XvMCBufferSettings()), xvmc_chroma(XVMC_CHROMA_FORMAT_420), xvmc_ctx(NULL), @@ -2464,6 +2465,12 @@ // this is documented in videooutbase.cpp void VideoOutputXv::Show(FrameScanType scan) { + if (first_show) + { + VERBOSE(VB_IMPORTANT, LOC + "First Show"); + first_show = false; + } + if (IsErrored()) { VERBOSE(VB_IMPORTANT, LOC_ERR + "IsErrored() is true in Show()"); Index: libs/libmyth/mythcontext.h =================================================================== --- libs/libmyth/mythcontext.h (revision 22742) +++ libs/libmyth/mythcontext.h (working copy) @@ -114,7 +114,7 @@ QString GetHostName(void); - void ClearSettingsCache(QString myKey = "", QString newVal = ""); + void ClearSettingsCache(const QString &myKey = QString("")); void ActivateSettingsCache(bool activate = true); void OverrideSettingForSession(const QString &key, const QString &newValue); Index: libs/libmyth/mythcontext.cpp =================================================================== --- libs/libmyth/mythcontext.cpp (revision 22742) +++ libs/libmyth/mythcontext.cpp (working copy) @@ -1895,9 +1895,9 @@ return ret; } -void MythContext::ClearSettingsCache(QString myKey, QString newVal) +void MythContext::ClearSettingsCache(const QString &myKey) { - d->m_database->ClearSettingsCache(myKey, newVal); + d->m_database->ClearSettingsCache(myKey); } void MythContext::ActivateSettingsCache(bool activate) Index: libs/libmythdb/mythdb.cpp =================================================================== --- libs/libmythdb/mythdb.cpp (revision 22742) +++ libs/libmythdb/mythdb.cpp (working copy) @@ -1,4 +1,8 @@ +#include +using namespace std; + #include +#include #include #include @@ -44,6 +48,7 @@ MythDB::destroyMythDB(); } +typedef QHash SettingsMap; class MythDBPrivate { @@ -58,18 +63,21 @@ Settings *m_settings; bool ignoreDatabase; - bool useSettingsCache; bool suppressDBMessages; - QMutex settingsCacheLock; - QHash settingsCache; // permanent settings in the DB - QHash overriddenSettings; // overridden this session only + + QReadWriteLock settingsCacheLock; + volatile bool useSettingsCache; + /// Permanent settings in the DB and overridden settings + SettingsMap settingsCache; + /// Overridden this session only + SettingsMap overriddenSettings; }; static const int settings_reserve = 61; MythDBPrivate::MythDBPrivate() : m_settings(new Settings()), - ignoreDatabase(false), useSettingsCache(false), suppressDBMessages(true) + ignoreDatabase(false), suppressDBMessages(true), useSettingsCache(false) { m_localhostname.clear(); settingsCache.reserve(settings_reserve); @@ -174,15 +182,18 @@ d->m_DBparams = params; } -void MythDB::SetLocalHostname(QString name) +void MythDB::SetLocalHostname(const QString &name) { - d->m_localhostname = name; + if (d->m_localhostname != name.toLower()) + { + d->m_localhostname = name.toLower(); + ClearSettingsCache(); + } } QString MythDB::GetHostName(void) { - QString tmp = d->m_localhostname; - return tmp; + return d->m_localhostname; } void MythDB::IgnoreDatabase(bool bIgnore) @@ -218,16 +229,16 @@ } bool MythDB::SaveSettingOnHost(const QString &key, - const QString &newValue, - const QString &host) + const QString &newValue, + const QString &host) { QString LOC = QString("SaveSettingOnHost('%1') ").arg(key); bool success = false; if (d->ignoreDatabase) { - ClearSettingsCache(key, newValue); - ClearSettingsCache(host + ' ' + key, newValue); + if (host.toLower() == d->m_localhostname) + OverrideSettingForSession(key, newValue); return true; } @@ -284,86 +295,223 @@ VERBOSE(VB_IMPORTANT, LOC + "- database not open"); } - ClearSettingsCache(key, newValue); - ClearSettingsCache(host + ' ' + key, newValue); + ClearSettingsCache(host + ' ' + key); return success; } -QString MythDB::GetSetting(const QString &key, const QString &defaultval) +QString MythDB::GetSetting(const QString &_key, const QString &defaultval) { + QString key = _key.toLower(); QString value; - if (d->overriddenSettings.contains(key)) + d->settingsCacheLock.lockForRead(); + if (d->useSettingsCache) { - value = d->overriddenSettings[key]; + SettingsMap::const_iterator it = d->settingsCache.find(key); + if (it != d->settingsCache.end()) + { + value = *it; + d->settingsCacheLock.unlock(); + return value; + } + } + else + { + SettingsMap::const_iterator it = d->overriddenSettings.find(key); + if (it != d->overriddenSettings.end()) + { + value = *it; + d->settingsCacheLock.unlock(); + return value; + } + } + d->settingsCacheLock.unlock(); + + if (d->ignoreDatabase) return value; + + MSqlQuery query(MSqlQuery::InitCon()); + if (!query.isConnected()) + { + VERBOSE(VB_IMPORTANT, + QString("Database not open while trying to load setting: %1") + .arg(key)); + return d->m_settings->GetSetting(key, defaultval); } - d->settingsCacheLock.lock(); + query.prepare( + "SELECT data " + "FROM settings " + "WHERE value = :KEY AND hostname = :HOSTNAME"); + query.bindValue(":KEY", key); + query.bindValue(":HOSTNAME", d->m_localhostname); + + if (query.exec() && query.next()) + { + value = query.value(0).toString(); + } + else + { + query.prepare( + "SELECT data " + "FROM settings " + "WHERE value = :KEY AND hostname IS NULL"); + query.bindValue(":KEY", key); - value = d->settingsCache.value(key, "_cold_"); + if (query.exec() && query.next()) + { + value = query.value(0).toString(); + } + else + { + return d->m_settings->GetSetting(key, defaultval); + } + } - d->settingsCacheLock.unlock(); + if (d->useSettingsCache) + { + key.squeeze(); + value.squeeze(); + d->settingsCacheLock.lockForWrite(); + // another thread may have inserted a value into the cache + // while we did not have the lock, check first then save + if (d->settingsCache.find(key) == d->settingsCache.end()) + d->settingsCache[key] = value; + d->settingsCacheLock.unlock(); + } - if (value != "_cold_" && d->useSettingsCache) - return value; + return value; +} - bool found = false; - if (!d->ignoreDatabase) +bool MythDB::GetSettings( + const QStringList &_keys, QStringList &values, CacheBehavior cb) +{ + if (_keys.size() != values.size()) + return false; + + uint done_cnt = 0; + vector done; + done.resize(_keys.size(), false); + + QStringList keys; + for (uint i = 0; i < (uint) _keys.size(); ++i) + keys.push_back(_keys[i].toLower()); + + d->settingsCacheLock.lockForRead(); + if (d->useSettingsCache) { - MSqlQuery query(MSqlQuery::InitCon()); - if (query.isConnected()) + for (uint i = 0; i < (uint) keys.size(); ++i) { - query.prepare("SELECT data FROM settings WHERE value " - "= :KEY AND hostname = :HOSTNAME ;"); - query.bindValue(":KEY", key); - query.bindValue(":HOSTNAME", d->m_localhostname); - - if (query.exec() && query.next()) + SettingsMap::const_iterator it = d->settingsCache.find(keys[i]); + if (it != d->settingsCache.end()) { - found = true; - value = query.value(0).toString(); + values[i] = *it; + done[i] = true; + done_cnt++; } - else + } + } + else + { + for (uint i = 0; i < (uint) keys.size(); ++i) + { + SettingsMap::const_iterator it = d->overriddenSettings.find(keys[i]); + if (it != d->overriddenSettings.end()) { - query.prepare("SELECT data FROM settings " - "WHERE value = :KEY AND " - "hostname IS NULL;"); - query.bindValue(":KEY", key); - - if (query.exec() && query.next()) - { - found = true; - value = query.value(0).toString(); - } - else - { - value = defaultval; - } + values[i] = *it; + done[i] = true; + done_cnt++; } } + } + d->settingsCacheLock.unlock(); + + if (done.size() == done_cnt || d->ignoreDatabase) + return true; + + QString keylist(""); + QMap keymap; + for (uint i = 0; i < (uint) keys.size(); ++i) + { + if (done[i]) + continue; + + QString key = keys[i]; + if (!key.contains("'")) + { + keylist += QString("'%1',").arg(key); + keymap[key] = i; + } else + { // don't bother optimizing this case.. + values[i] = GetSetting(key, values[i]); + done[i] = true; + done_cnt++; + } + } + + if (keylist.isEmpty()) + return true; + + keylist = keylist.left(keylist.length() - 1); + + MSqlQuery query(MSqlQuery::InitCon()); + if (!query.exec( + QString( + "SELECT value, data, hostname " + "FROM settings " + "WHERE (hostname = '%1' OR hostname IS NULL) AND " + " value IN (%2) " + "ORDER BY hostname DESC") + .arg(d->m_localhostname).arg(keylist))) + { + DBError("GetSettings", query); + return false; + } + + while (query.next()) + { + QString key = query.value(0).toString().toLower(); + QMap::const_iterator it = keymap.find(key); + if (it != keymap.end() && !done[*it]) { - VERBOSE(VB_IMPORTANT, - QString("Database not open while trying to " - "load setting: %1") - .arg(key)); + values[*it] = query.value(1).toString(); + done[*it] = true; + done_cnt++; } } - if (!found) - return d->m_settings->GetSetting(key, defaultval); + if (d->useSettingsCache) + { + VERBOSE(VB_IMPORTANT, "d->useSettingsCache"); + d->settingsCacheLock.lockForWrite(); + QMap::const_iterator it = keymap.begin(); + for (; it != keymap.end(); ++it) + { + if ((kCacheDBOnly == cb) && !done[*it]) + { + VERBOSE(VB_IMPORTANT, QString("Not caching '%1'").arg(it.key())); + continue; + } - // Do not store default values since they may not have been specified. - if (!value.isNull() && d->useSettingsCache) - { - d->settingsCacheLock.lock(); - d->settingsCache[key] = value; + QString key = it.key(), value = values[*it]; + + // another thread may have inserted a value into the cache + // while we did not have the lock, check first then save + if (d->settingsCache.find(key) == d->settingsCache.end()) + { + key.squeeze(); + value.squeeze(); + d->settingsCache[key] = value; + } + } d->settingsCacheLock.unlock(); } + else + VERBOSE(VB_IMPORTANT, "!d->useSettingsCache"); - return value; + return true; } int MythDB::GetNumSetting(const QString &key, int defaultval) @@ -382,74 +530,71 @@ return retval.toDouble(); } -QString MythDB::GetSettingOnHost(const QString &key, const QString &host, - const QString &defaultval) +QString MythDB::GetSettingOnHost(const QString &_key, const QString &_host, + const QString &defaultval) { - bool found = false; + QString key = _key.toLower(); + QString host = _host.toLower(); QString value = defaultval; QString myKey = host + ' ' + key; - if (d->overriddenSettings.contains(myKey)) + d->settingsCacheLock.lockForRead(); + if (d->useSettingsCache) { - value = d->overriddenSettings[myKey]; - return value; + SettingsMap::const_iterator it = d->settingsCache.find(myKey); + if (it != d->settingsCache.end()) + { + value = *it; + d->settingsCacheLock.unlock(); + return value; + } } - - if ((host == d->m_localhostname) && - (d->overriddenSettings.contains(key))) + else { - - value = d->overriddenSettings[key]; - return value; - } - - if (d->useSettingsCache) - { - d->settingsCacheLock.lock(); - if (d->settingsCache.contains(myKey)) + SettingsMap::const_iterator it = d->overriddenSettings.find(myKey); + if (it != d->overriddenSettings.end()) { - value = d->settingsCache[myKey]; + value = *it; d->settingsCacheLock.unlock(); return value; } - d->settingsCacheLock.unlock(); } + d->settingsCacheLock.unlock(); - if (!d->ignoreDatabase) + if (d->ignoreDatabase) + return value; + + MSqlQuery query(MSqlQuery::InitCon()); + if (!query.isConnected()) { - MSqlQuery query(MSqlQuery::InitCon()); - if (query.isConnected()) - { - query.prepare("SELECT data FROM settings WHERE value = :VALUE " - "AND hostname = :HOSTNAME ;"); - query.bindValue(":VALUE", key); - query.bindValue(":HOSTNAME", host); + VERBOSE(VB_IMPORTANT, + QString("Database not open while trying to " + "load setting: %1").arg(key)); + return value; + } - if (query.exec() && query.isActive() && query.size() > 0) - { - if (query.next()) - { - value = query.value(0).toString(); - found = true; - } - } - } - else + query.prepare( + "SELECT data " + "FROM settings " + "WHERE value = :VALUE AND hostname = :HOSTNAME"); + query.bindValue(":VALUE", key); + query.bindValue(":HOSTNAME", host); + + if (query.exec() && query.next()) + { + value = query.value(0).toString(); + + if (d->useSettingsCache) { - VERBOSE(VB_IMPORTANT, - QString("Database not open while trying to " - "load setting: %1") - .arg(key)); + myKey.squeeze(); + value.squeeze(); + d->settingsCacheLock.lockForWrite(); + if (d->settingsCache.find(myKey) == d->settingsCache.end()) + d->settingsCache[myKey] = value; + d->settingsCacheLock.unlock(); } } - if (found && d->useSettingsCache) - { - d->settingsCacheLock.lock(); - d->settingsCache[host + ' ' + key] = value; - d->settingsCacheLock.unlock(); - } - return value; } @@ -474,7 +619,7 @@ void MythDB::SetSetting(const QString &key, const QString &newValue) { d->m_settings->SetSetting(key, newValue); - ClearSettingsCache(key, newValue); + ClearSettingsCache(key); } void MythDB::GetResolutionSetting(const QString &type, @@ -550,28 +695,75 @@ * This allows defining settings for the session only, without touching the * settings in the data base. */ -void MythDB::OverrideSettingForSession(const QString &key, - const QString &value) +void MythDB::OverrideSettingForSession( + const QString &key, const QString &value) { - d->overriddenSettings[key] = value; + QString mk = key.toLower(), mk2 = d->m_localhostname + ' ' + mk, mv = value; + mk.squeeze(); + mk2.squeeze(); + mv.squeeze(); + + d->settingsCacheLock.lockForWrite(); + d->overriddenSettings[mk] = mv; + d->settingsCache[mk] = mv; + d->settingsCache[mk2] = mv; + d->settingsCacheLock.unlock(); } -void MythDB::ClearSettingsCache(QString myKey, QString newVal) +static void clear( + SettingsMap &cache, SettingsMap &overrides, const QString &myKey) { - d->settingsCacheLock.lock(); - if (!myKey.isEmpty() && d->settingsCache.contains(myKey)) + // Do the actual clearing.. + SettingsMap::iterator it = cache.find(myKey); + if (it != cache.end()) { - VERBOSE(VB_DATABASE, QString("Clearing Settings Cache for '%1'.") - .arg(myKey)); - d->settingsCache.remove(myKey); - d->settingsCache[myKey] = newVal; + SettingsMap::const_iterator oit = overrides.find(myKey); + if (oit == overrides.end()) + { + VERBOSE(VB_DATABASE, + QString("Clearing Settings Cache for '%1'.").arg(myKey)); + cache.erase(it); + } + else + { + VERBOSE(VB_DATABASE, + QString("Clearing Cache of overridden '%1' ignored.") + .arg(myKey)); + } } - else +} + +void MythDB::ClearSettingsCache(const QString &_key) +{ + d->settingsCacheLock.lockForWrite(); + + if (_key.isEmpty()) { VERBOSE(VB_DATABASE, "Clearing Settings Cache."); d->settingsCache.clear(); d->settingsCache.reserve(settings_reserve); + + SettingsMap::const_iterator it = d->overriddenSettings.begin(); + for (; it != d->overriddenSettings.end(); ++it) + { + QString mk2 = d->m_localhostname + ' ' + it.key(); + mk2.squeeze(); + + d->settingsCache[it.key()] = *it; + d->settingsCache[mk2] = *it; + } } + else + { + QString myKey = _key.toLower(); + clear(d->settingsCache, d->overriddenSettings, myKey); + + // To be safe always clear any local[ized] version too + QStringList mkl = myKey.split(QChar(' ')); + if (mkl.size() == 2) + clear(d->settingsCache, d->overriddenSettings, mkl[1]); + } + d->settingsCacheLock.unlock(); } @@ -585,4 +777,3 @@ d->useSettingsCache = activate; ClearSettingsCache(); } - Index: libs/libmythdb/mythdb.h =================================================================== --- libs/libmythdb/mythdb.h (revision 22742) +++ libs/libmythdb/mythdb.h (working copy) @@ -13,6 +13,11 @@ class MPUBLIC MythDB { public: + typedef enum + { + kCacheDBOnly, + kCacheDefaults, + } CacheBehavior; MDBManager *GetDBManager(void); Settings *GetOldSettings(void); @@ -23,7 +28,7 @@ DatabaseParams GetDatabaseParams(void); void SetDatabaseParams(const DatabaseParams ¶ms); - void SetLocalHostname(QString name); + void SetLocalHostname(const QString &name); QString GetHostName(void); void IgnoreDatabase(bool bIgnore); @@ -32,7 +37,7 @@ void SetSuppressDBMessages(bool bUpgraded); bool SuppressDBMessages(void) const; - void ClearSettingsCache(QString myKey = "", QString newVal = ""); + void ClearSettingsCache(const QString &key = QString()); void ActivateSettingsCache(bool activate = true); void OverrideSettingForSession(const QString &key, const QString &newValue); @@ -42,6 +47,9 @@ const QString &host); QString GetSetting(const QString &key, const QString &defaultval = ""); + bool GetSettings(const QStringList &_keys, QStringList &values, + CacheBehavior cb = kCacheDBOnly); + int GetNumSetting(const QString &key, int defaultval = 0); double GetFloatSetting(const QString &key, double defaultval = 0.0); void GetResolutionSetting(const QString &type, int &width, int &height,