From 55f124725f9007002646b53c0bf33de5c9422338 Mon Sep 17 00:00:00 2001 From: Jean-Francois Dockes Date: Thu, 28 Apr 2011 10:58:33 +0200 Subject: [PATCH] Fix problems that occurred when multiple threads were trying to read/convert files at the same time (ie: indexing and previewing threads in the GUI calling internfile()). Either get rid of or lock-protect all shared data, eliminate misc initialization possible conflicts by using static initializers. Hopefuly closes issue #51 --- src/common/rclconfig.cpp | 7 ++- src/common/rclinit.cpp | 4 ++ src/common/textsplit.cpp | 72 ++++++++++++++---------------- src/index/fsindexer.cpp | 9 +++- src/index/fsindexer.h | 9 ++-- src/internfile/internfile.cpp | 61 ++++++++++++++----------- src/internfile/internfile.h | 27 ++++++++--- src/internfile/mh_mbox.cpp | 14 +++++- src/internfile/mimehandler.cpp | 29 +++--------- src/internfile/myhtmlparse.cpp | 35 ++++++++------- src/internfile/myhtmlparse.h | 1 - src/qtgui/advsearch_w.cpp | 8 ++-- src/qtgui/confgui/confgui.cpp | 4 +- src/qtgui/confgui/confguiindex.cpp | 5 ++- src/qtgui/idxthread.cpp | 11 +++-- src/qtgui/idxthread.h | 6 +-- src/qtgui/main.cpp | 35 ++++++++++----- src/qtgui/preview_w.cpp | 34 ++++++++------ src/qtgui/rclmain_w.cpp | 50 ++++++++++----------- src/qtgui/rclmain_w.h | 3 +- src/qtgui/recoll.h | 7 ++- src/qtgui/reslist.cpp | 6 +-- src/qtgui/restable.cpp | 8 ++-- src/qtgui/searchclause_w.cpp | 4 +- src/qtgui/spell_w.cpp | 2 +- src/qtgui/ssearch_w.cpp | 6 +-- src/qtgui/uiprefs_w.cpp | 2 +- src/qtgui/viewaction_w.cpp | 4 +- src/query/reslistpager.cpp | 2 +- src/rcldb/rcldb.cpp | 2 +- src/rcldb/stemdb.cpp | 2 +- src/utils/execmd.cpp | 28 +++--------- src/utils/ptmutex.h | 52 +++++++++++++++++++++ 33 files changed, 318 insertions(+), 231 deletions(-) create mode 100644 src/utils/ptmutex.h diff --git a/src/common/rclconfig.cpp b/src/common/rclconfig.cpp index f88ac237..87613f4c 100644 --- a/src/common/rclconfig.cpp +++ b/src/common/rclconfig.cpp @@ -320,7 +320,12 @@ list RclConfig::getTopdirs() // (only the locale). const string& RclConfig::getDefCharset(bool filename) { - // This can't change once computed inside a process. + // This can't change once computed inside a process. It would be + // nicer to move this to a static class initializer to avoid + // possible threading issues but this doesn't work (tried) as + // things would not be ready. In practise we make sure that this + // is called from the main thread at once, by calling + // getDefCharset from recollinit static string localecharset; if (localecharset.empty()) { const char *cp; diff --git a/src/common/rclinit.cpp b/src/common/rclinit.cpp index 35be93ea..3ea89d1a 100644 --- a/src/common/rclinit.cpp +++ b/src/common/rclinit.cpp @@ -92,6 +92,10 @@ RclConfig *recollinit(RclInitFlags flags, // to utf8 for indexing. setlocale(LC_CTYPE, ""); + // Make sure the locale charset is initialized (so that multiple + // threads don't try to do it at once). + config->getDefCharset(); + return config; } diff --git a/src/common/textsplit.cpp b/src/common/textsplit.cpp index 714d980d..637b263c 100644 --- a/src/common/textsplit.cpp +++ b/src/common/textsplit.cpp @@ -60,49 +60,47 @@ static int charclasses[charclasses_size]; static set unicign; static set visiblewhite; -// Set up character classes array and the additional unicode sets -static void setcharclasses() -{ - static int init = 0; - if (init) - return; - unsigned int i; +class CharClassInit { +public: + CharClassInit() + { + unsigned int i; - // Set default value for all: SPACE - for (i = 0 ; i < 256 ; i ++) - charclasses[i] = SPACE; + // Set default value for all: SPACE + for (i = 0 ; i < 256 ; i ++) + charclasses[i] = SPACE; - char digits[] = "0123456789"; - for (i = 0; i < strlen(digits); i++) - charclasses[int(digits[i])] = DIGIT; + char digits[] = "0123456789"; + for (i = 0; i < strlen(digits); i++) + charclasses[int(digits[i])] = DIGIT; - char upper[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - for (i = 0; i < strlen(upper); i++) - charclasses[int(upper[i])] = A_ULETTER; + char upper[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + for (i = 0; i < strlen(upper); i++) + charclasses[int(upper[i])] = A_ULETTER; - char lower[] = "abcdefghijklmnopqrstuvwxyz"; - for (i = 0; i < strlen(lower); i++) - charclasses[int(lower[i])] = A_LLETTER; + char lower[] = "abcdefghijklmnopqrstuvwxyz"; + for (i = 0; i < strlen(lower); i++) + charclasses[int(lower[i])] = A_LLETTER; - char wild[] = "*?[]"; - for (i = 0; i < strlen(wild); i++) - charclasses[int(wild[i])] = WILD; + char wild[] = "*?[]"; + for (i = 0; i < strlen(wild); i++) + charclasses[int(wild[i])] = WILD; - char special[] = ".@+-,#'_\n\r"; - for (i = 0; i < strlen(special); i++) - charclasses[int(special[i])] = special[i]; + char special[] = ".@+-,#'_\n\r"; + for (i = 0; i < strlen(special); i++) + charclasses[int(special[i])] = special[i]; - for (i = 0; i < sizeof(uniign) / sizeof(int); i++) { - unicign.insert(uniign[i]); + for (i = 0; i < sizeof(uniign) / sizeof(int); i++) { + unicign.insert(uniign[i]); + } + unicign.insert((unsigned int)-1); + + for (i = 0; i < sizeof(avsbwht) / sizeof(int); i++) { + visiblewhite.insert(avsbwht[i]); + } } - unicign.insert((unsigned int)-1); - - for (i = 0; i < sizeof(avsbwht) / sizeof(int); i++) { - visiblewhite.insert(avsbwht[i]); - } - - init = 1; -} +}; +static const CharClassInit charClassInitInstance; static inline int whatcc(unsigned int c) { @@ -280,8 +278,6 @@ bool TextSplit::text_to_words(const string &in) m_flags & TXTS_KEEPWILD ? " keepwild" : "", in.substr(0,50).c_str())); - setcharclasses(); - m_span.erase(); m_inNumber = false; m_wordStart = m_wordLen = m_prevpos = m_prevlen = m_wordpos = m_spanpos = 0; @@ -633,7 +629,6 @@ int TextSplit::countWords(const string& s, TextSplit::Flags flgs) bool TextSplit::hasVisibleWhite(const string &in) { - setcharclasses(); Utf8Iter it(in); for (; !it.eof(); it++) { unsigned int c = (unsigned char)*it; @@ -650,7 +645,6 @@ bool TextSplit::hasVisibleWhite(const string &in) template bool u8stringToStrings(const string &s, T &tokens) { - setcharclasses(); Utf8Iter it(s); string current; diff --git a/src/index/fsindexer.cpp b/src/index/fsindexer.cpp index b5e4afdd..85961257 100644 --- a/src/index/fsindexer.cpp +++ b/src/index/fsindexer.cpp @@ -63,7 +63,13 @@ using namespace std; #define deleteZ(X) {delete X;X = 0;} #endif +FsIndexer::FsIndexer(RclConfig *cnf, Rcl::Db *db, DbIxStatusUpdater *updfunc) + : m_config(cnf), m_db(db), m_updater(updfunc), m_missing(new FIMissingStore) +{ + m_havelocalfields = m_config->hasNameAnywhere("localfields"); +} FsIndexer::~FsIndexer() { + delete m_missing; } bool FsIndexer::init() @@ -121,7 +127,7 @@ bool FsIndexer::index() } string missing; - FileInterner::getMissingDescription(missing); + FileInterner::getMissingDescription(m_missing, missing); if (!missing.empty()) { LOGINFO(("FsIndexer::index missing helper program(s):\n%s\n", missing.c_str())); @@ -359,6 +365,7 @@ FsIndexer::processone(const std::string &fn, const struct stat *stp, // indexallfilenames is not set return FsTreeWalker::FtwOk; } + interner.setMissingStore(m_missing); // File name transcoded to utf8 for indexing. string charset = m_config->getDefCharset(true); diff --git a/src/index/fsindexer.h b/src/index/fsindexer.h index 33a9bb89..58a00eff 100644 --- a/src/index/fsindexer.h +++ b/src/index/fsindexer.h @@ -26,6 +26,7 @@ using std::list; #include "rcldb.h" class DbIxStatusUpdater; +class FIMissingStore; /** Index selected parts of the file system @@ -45,12 +46,7 @@ class FsIndexer : public FsTreeWalkerCB { * @param cnf Configuration data * @param updfunc Status updater callback */ - FsIndexer(RclConfig *cnf, Rcl::Db *db, DbIxStatusUpdater *updfunc = 0) - : m_config(cnf), m_db(db), m_updater(updfunc) - { - m_havelocalfields = m_config->hasNameAnywhere("localfields"); - } - + FsIndexer(RclConfig *cnf, Rcl::Db *db, DbIxStatusUpdater *updfunc = 0); virtual ~FsIndexer(); /** @@ -79,6 +75,7 @@ class FsIndexer : public FsTreeWalkerCB { string m_reason; DbIxStatusUpdater *m_updater; list m_tdl; + FIMissingStore *m_missing; // The configuration can set attribute fields to be inherited by // all files in a file system area. Ie: set "rclaptg = thunderbird" diff --git a/src/internfile/internfile.cpp b/src/internfile/internfile.cpp index 45a02717..6c8945b9 100644 --- a/src/internfile/internfile.cpp +++ b/src/internfile/internfile.cpp @@ -45,6 +45,7 @@ using namespace std; #include "beaglequeuecache.h" #include "cancelcheck.h" #include "copyfile.h" +#include "ptmutex.h" #ifdef RCL_USE_XATTR #include "pxattr.h" @@ -76,9 +77,6 @@ static string colon_restore(const string& in) return out; } -set FileInterner::o_missingExternal; -map > FileInterner::o_typesForMissing; - #ifdef RCL_USE_XATTR void FileInterner::reapXAttrs(const string& path) { @@ -192,7 +190,7 @@ void FileInterner::tmpcleanup() FileInterner::FileInterner(const string &f, const struct stat *stp, RclConfig *cnf, TempDir& td, int flags, const string *imime) - : m_tdir(td), m_ok(false) + : m_tdir(td), m_ok(false), m_missingdatap(0) { initcommon(cnf, flags); init(f, stp, cnf, flags, imime); @@ -304,7 +302,7 @@ void FileInterner::init(const string &f, const struct stat *stp, RclConfig *cnf, // Setup from memory data (ie: out of the web cache). imime needs to be set. FileInterner::FileInterner(const string &data, RclConfig *cnf, TempDir& td, int flags, const string& imime) - : m_tdir(td), m_ok(false) + : m_tdir(td), m_ok(false), m_missingdatap(0) { initcommon(cnf, flags); init(data, cnf, flags, imime); @@ -366,9 +364,13 @@ void FileInterner::initcommon(RclConfig *cnf, int flags) m_targetMType = stxtplain; } +// We used a single beagle cache object to access beagle data. We protect it +// against multiple thread access. +static PTMutexInit o_lock; + FileInterner::FileInterner(const Rcl::Doc& idoc, RclConfig *cnf, TempDir& td, int flags) - : m_tdir(td), m_ok(false) + : m_tdir(td), m_ok(false), m_missingdatap(0) { LOGDEB(("FileInterner::FileInterner(idoc)\n")); initcommon(cnf, flags); @@ -405,10 +407,6 @@ FileInterner::FileInterner(const Rcl::Doc& idoc, RclConfig *cnf, } init(fn, &st, cnf, flags, &idoc.mimetype); } else if (!backend.compare("BGL")) { - // Retrieve from our webcache (beagle data). The beagler - // object is created at the first call of this routine and - // deleted when the program exits. - static BeagleQueueCache beagler(cnf); string data; Rcl::Doc dotdoc; map::const_iterator it = @@ -418,11 +416,19 @@ FileInterner::FileInterner(const Rcl::Doc& idoc, RclConfig *cnf, return; } string udi = it->second; - if (!beagler.getFromCache(udi, dotdoc, data)) { - LOGINFO(("FileInterner:: failed fetch from Beagle cache for [%s]\n", - udi.c_str())); - return; - } + + { + PTMutexLocker locker(o_lock); + // Retrieve from our webcache (beagle data). The beagler + // object is created at the first call of this routine and + // deleted when the program exits. + static BeagleQueueCache beagler(cnf); + if (!beagler.getFromCache(udi, dotdoc, data)) { + LOGINFO(("FileInterner:: failed fetch from Beagle cache for [%s]\n", + udi.c_str())); + return; + } + } if (dotdoc.mimetype.compare(idoc.mimetype)) { LOGINFO(("FileInterner:: udi [%s], mimetp mismatch: in: [%s], bgl " "[%s]\n", idoc.mimetype.c_str(), dotdoc.mimetype.c_str())); @@ -485,7 +491,7 @@ bool FileInterner::dataToTempFile(const string& dt, const string& mt, // accumulate helper name if it is void FileInterner::checkExternalMissing(const string& msg, const string& mt) { - if (msg.find("RECFILTERROR") == 0) { + if (m_missingdatap && msg.find("RECFILTERROR") == 0) { list lerr; stringToStrings(msg, lerr); if (lerr.size() > 2) { @@ -495,28 +501,33 @@ void FileInterner::checkExternalMissing(const string& msg, const string& mt) lerr.erase(it++); string s; stringsToString(lerr, s); - o_missingExternal.insert(s); - o_typesForMissing[s].insert(mt); + m_missingdatap->m_missingExternal.insert(s); + m_missingdatap->m_typesForMissing[s].insert(mt); } } } } -void FileInterner::getMissingExternal(string& out) +void FileInterner::getMissingExternal(FIMissingStore *st, string& out) { - stringsToString(o_missingExternal, out); + if (st) + stringsToString(st->m_missingExternal, out); } -void FileInterner::getMissingDescription(string& out) +void FileInterner::getMissingDescription(FIMissingStore *st, string& out) { + if (st == 0) + return; + out.erase(); - for (set::const_iterator it = o_missingExternal.begin(); - it != o_missingExternal.end(); it++) { + for (set::const_iterator it = + st->m_missingExternal.begin(); + it != st->m_missingExternal.end(); it++) { out += *it; map >::const_iterator it2; - it2 = o_typesForMissing.find(*it); - if (it2 != o_typesForMissing.end()) { + it2 = st->m_typesForMissing.find(*it); + if (it2 != st->m_typesForMissing.end()) { out += " ("; set::const_iterator it3; for (it3 = it2->second.begin(); diff --git a/src/internfile/internfile.h b/src/internfile/internfile.h index 7d2ac821..ae29d9a9 100644 --- a/src/internfile/internfile.h +++ b/src/internfile/internfile.h @@ -39,6 +39,19 @@ class Doc; struct stat; +/** Storage for missing helper program info. We want to keep this out of the + * FileInterner class, because the data will typically be accumulated by several + * FileInterner objects. Can't use static member either (because there + * may be several separate usages of the class which shouldn't mix + * their data). + */ +class FIMissingStore { +public: + // Missing external programs + set m_missingExternal; + map > m_typesForMissing; +}; + /** * A class to convert data from a datastore (file-system, firefox * history, etc.) into possibly one or severaldocuments in internal @@ -110,6 +123,11 @@ class FileInterner { ~FileInterner(); + void setMissingStore(FIMissingStore *st) + { + m_missingdatap = st; + } + /** * Turn file or file part into Recoll document. * @@ -169,11 +187,10 @@ class FileInterner { RclConfig *cnf, const Rcl::Doc& doc); const string& getReason() const {return m_reason;} - static void getMissingExternal(string& missing); - static void getMissingDescription(string& desc); + static void getMissingExternal(FIMissingStore *st, string& missing); + static void getMissingDescription(FIMissingStore *st, string& desc); bool ok() {return m_ok;} - private: static const unsigned int MAXHANDLERS = 20; RclConfig *m_cfg; @@ -203,9 +220,7 @@ class FileInterner { vector m_tempfiles; // Error data if any string m_reason; - // Missing external programs - static set o_missingExternal; - static map > o_typesForMissing; + FIMissingStore *m_missingdatap; // Pseudo-constructors void init(const string &fn, const struct stat *stp, diff --git a/src/internfile/mh_mbox.cpp b/src/internfile/mh_mbox.cpp index 23988e4b..76fe56b0 100644 --- a/src/internfile/mh_mbox.cpp +++ b/src/internfile/mh_mbox.cpp @@ -37,6 +37,7 @@ #include "rclconfig.h" #include "md5.h" #include "conftree.h" +#include "ptmutex.h" using namespace std; class FpKeeper { @@ -52,6 +53,7 @@ public: private: FILE **m_fpp; }; +static PTMutexInit o_mutex; /** * Handles a cache for message numbers to offset translations. Permits direct @@ -78,6 +80,7 @@ public: ~MboxCache() {} mbhoff_type get_offset(RclConfig *config, const string& udi, int msgnum) { + PTMutexLocker locker(o_mutex); LOGDEB0(("MboxCache::get_offsets: udi [%s] msgnum %d\n", udi.c_str(), msgnum)); if (!ok(config)) { @@ -125,6 +128,7 @@ public: void put_offsets(RclConfig *config, const string& udi, mbhoff_type fsize, vector& offs) { + PTMutexLocker locker(o_mutex); LOGDEB0(("MboxCache::put_offsets: %u offsets\n", offs.size())); if (!ok(config) || !maybemakedir()) return; @@ -159,6 +163,7 @@ public: // Check state, possibly initialize bool ok(RclConfig *config) { + PTMutexLocker locker(o_mutex); if (m_minfsize == -1) return false; if (!m_ok) { @@ -218,7 +223,9 @@ private: }; const size_t MboxCache::o_b1size = 1024; + static class MboxCache mcache; + static const string keyquirks("mhmboxquirks"); MimeHandlerMbox::~MimeHandlerMbox() @@ -389,6 +396,7 @@ bool MimeHandlerMbox::next_document() // avoid rereading the whole thing in this case. But I'm not sure // we're ever used in this way (multiple retrieves on same // object). So: + bool storeoffsets = true; if (mtarg > 0) { mbhoff_type off; line_type line; @@ -404,6 +412,7 @@ bool MimeHandlerMbox::next_document() LOGDEB0(("MimeHandlerMbox: Cache: From_ Ok\n")); fseeko(fp, (off_t)off, SEEK_SET); m_msgnum = mtarg -1; + storeoffsets = false; } else { fseek(fp, 0, SEEK_SET); m_msgnum = 0; @@ -444,7 +453,8 @@ bool MimeHandlerMbox::next_document() !regexec(&minifromregex, line, 0, 0, 0)) ) { LOGDEB1(("MimeHandlerMbox: msgnum %d, " "From_ at line %d: [%s]\n", m_msgnum, m_lineno, line)); - m_offsets.push_back(message_end); + if (storeoffsets) + m_offsets.push_back(message_end); m_msgnum++; if ((mtarg <= 0 && m_msgnum > 1) || (mtarg > 0 && m_msgnum > mtarg)) { @@ -477,7 +487,7 @@ bool MimeHandlerMbox::next_document() if (iseof) { LOGDEB2(("MimeHandlerMbox::next: eof hit\n")); m_havedoc = false; - if (!m_udi.empty()) { + if (!m_udi.empty() && storeoffsets) { mcache.put_offsets(m_config, m_udi, m_fsize, m_offsets); } } diff --git a/src/internfile/mimehandler.cpp b/src/internfile/mimehandler.cpp index e024c406..b405dda0 100644 --- a/src/internfile/mimehandler.cpp +++ b/src/internfile/mimehandler.cpp @@ -28,7 +28,6 @@ using namespace std; #include "debuglog.h" #include "rclconfig.h" #include "smallut.h" -#include "pthread.h" #include "mh_exec.h" #include "mh_execm.h" @@ -37,6 +36,7 @@ using namespace std; #include "mh_mbox.h" #include "mh_text.h" #include "mh_unknown.h" +#include "ptmutex.h" // Performance help: we use a pool of already known and created // handlers. There can be several instances for a given mime type @@ -48,26 +48,7 @@ using namespace std; // simple lock should be enough as handlers are removed from the cache // while in use and multiple copies are allowed static multimap o_handlers; -pthread_mutex_t o_handlers_mutex; -class HandlersLocker { -public: - HandlersLocker() - { - pthread_mutex_lock(&o_handlers_mutex); - } - ~HandlersLocker() - { - pthread_mutex_unlock(&o_handlers_mutex); - } -}; -class HandlersLockerInit { -public: - HandlersLockerInit() - { - pthread_mutex_init(&o_handlers_mutex, 0); - } -}; -static HandlersLockerInit o_hli; +static PTMutexInit o_handlers_mutex; /** For mime types set as "internal" in mimeconf: * create appropriate handler object. */ @@ -163,7 +144,7 @@ void returnMimeHandler(Dijon::Filter *handler) typedef multimap::value_type value_type; if (handler) { handler->clear(); - HandlersLocker locker; + PTMutexLocker locker(o_handlers_mutex); o_handlers.insert(value_type(handler->get_mime_type(), handler)); } } @@ -172,7 +153,7 @@ void clearMimeHandlerCache() { typedef multimap::value_type value_type; map::iterator it; - HandlersLocker locker; + PTMutexLocker locker(o_handlers_mutex); for (it = o_handlers.begin(); it != o_handlers.end(); it++) { delete it->second; } @@ -187,7 +168,7 @@ Dijon::Filter *getMimeHandler(const string &mtype, RclConfig *cfg, LOGDEB2(("getMimeHandler: mtype [%s] filtertypes %d\n", mtype.c_str(), filtertypes)); Dijon::Filter *h = 0; - HandlersLocker locker; + PTMutexLocker locker(o_handlers_mutex); // Get handler definition for mime type. We do this even if an // appropriate handler object may be in the cache (indexed by mime diff --git a/src/internfile/myhtmlparse.cpp b/src/internfile/myhtmlparse.cpp index f5729b55..d000627a 100644 --- a/src/internfile/myhtmlparse.cpp +++ b/src/internfile/myhtmlparse.cpp @@ -35,8 +35,6 @@ #include "debuglog.h" #include "transcode.h" -map MyHtmlParser::my_named_ents; - inline static bool p_notdigit(char c) { @@ -151,20 +149,11 @@ static const char *epairs[] = { "rsaquo", "\xe2\x80\xba", "euro", "\xe2\x82\xac", NULL, NULL }; - -MyHtmlParser::MyHtmlParser() - : in_script_tag(false), - in_style_tag(false), - in_body_tag(false), - in_pre_tag(false), - pending_space(false), - indexing_allowed(true) -{ - // The default html document charset is iso-8859-1. We'll update - // this value from the encoding tag if found. - charset = "iso-8859-1"; - - if (my_named_ents.empty()) { +map my_named_ents; +class NamedEntsInitializer { +public: + NamedEntsInitializer() + { for (int i = 0;;) { const char *ent; const char *val; @@ -177,6 +166,20 @@ MyHtmlParser::MyHtmlParser() my_named_ents[string(ent)] = val; } } +}; +static NamedEntsInitializer namedEntsInitializerInstance; + +MyHtmlParser::MyHtmlParser() + : in_script_tag(false), + in_style_tag(false), + in_body_tag(false), + in_pre_tag(false), + pending_space(false), + indexing_allowed(true) +{ + // The default html document charset is iso-8859-1. We'll update + // this value from the encoding tag if found. + charset = "iso-8859-1"; } void MyHtmlParser::decode_entities(string &s) diff --git a/src/internfile/myhtmlparse.h b/src/internfile/myhtmlparse.h index 141a2cd1..82830eca 100644 --- a/src/internfile/myhtmlparse.h +++ b/src/internfile/myhtmlparse.h @@ -40,7 +40,6 @@ class MyHtmlParser : public HtmlParser { bool in_pre_tag; bool pending_space; map meta; - static map my_named_ents; string dump, dmtime; // This is the charset our caller thinks the doc used (initially // comes from the environment/configuration, used as source for diff --git a/src/qtgui/advsearch_w.cpp b/src/qtgui/advsearch_w.cpp index daaa9ddd..b6a6f912 100644 --- a/src/qtgui/advsearch_w.cpp +++ b/src/qtgui/advsearch_w.cpp @@ -48,8 +48,6 @@ using std::unique; #include "guiutils.h" #include "rclhelp.h" -extern RclConfig *rclconfig; - static const int initclausetypes[] = {1, 3, 0, 2, 5}; static const unsigned int iclausescnt = sizeof(initclausetypes) / sizeof(int); static map cat_translations; @@ -268,7 +266,7 @@ void AdvSearch::fillFileTypes() QStringList ql; if (m_ignByCats == false) { - list types = rclconfig->getAllMimeTypes(); + list types = theconfig->getAllMimeTypes(); for (list::iterator it = types.begin(); it != types.end(); it++) { QString qs = QString::fromUtf8(it->c_str()); @@ -277,7 +275,7 @@ void AdvSearch::fillFileTypes() } } else { list cats; - rclconfig->getMimeCategories(cats); + theconfig->getMimeCategories(cats); for (list::const_iterator it = cats.begin(); it != cats.end(); it++) { map::const_iterator it1; @@ -334,7 +332,7 @@ void AdvSearch::runSearch() cat = (const char *)qcat.toUtf8(); } list types; - rclconfig->getMimeCatTypes(cat, types); + theconfig->getMimeCatTypes(cat, types); for (list::const_iterator it = types.begin(); it != types.end(); it++) { sdata->addFiletype(*it); diff --git a/src/qtgui/confgui/confgui.cpp b/src/qtgui/confgui/confgui.cpp index 7fc0f5c4..20fefbaf 100644 --- a/src/qtgui/confgui/confgui.cpp +++ b/src/qtgui/confgui/confgui.cpp @@ -49,8 +49,8 @@ using std::list; namespace confgui { -const static int spacing = 2; -const static int margin = 2; +static const int spacing = 2; +static const int margin = 2; void ConfParamW::setValue(const QString& value) { diff --git a/src/qtgui/confgui/confguiindex.cpp b/src/qtgui/confgui/confguiindex.cpp index 8676d4a3..a517a738 100644 --- a/src/qtgui/confgui/confguiindex.cpp +++ b/src/qtgui/confgui/confguiindex.cpp @@ -44,8 +44,8 @@ using std::list; #include "rclconfig.h" namespace confgui { -const static int spacing = 3; -const static int margin = 3; +static const int spacing = 3; +static const int margin = 3; ConfIndexW::ConfIndexW(QWidget *parent, RclConfig *config) : QDialog(parent), m_rclconf(config) @@ -84,6 +84,7 @@ void ConfIndexW::acceptChanges() delete m_conf; m_conf = 0; m_rclconf->updateMainConfig(); + snapshotConfig(); if (startIndexingAfterConfig) { startIndexingAfterConfig = 0; diff --git a/src/qtgui/idxthread.cpp b/src/qtgui/idxthread.cpp index 99f00981..8d20cb0c 100644 --- a/src/qtgui/idxthread.cpp +++ b/src/qtgui/idxthread.cpp @@ -29,6 +29,7 @@ #include "smallut.h" #include "rclinit.h" #include "pathut.h" +#include "recoll.h" static int stopindexing; static int startindexing; @@ -60,7 +61,6 @@ class IdxThread : public QThread , public DbIxStatusUpdater { // Maintain a copy/snapshot of idx status DbIxStatus m_statusSnap; bool m_interrupted; - const RclConfig *cnf; }; void IdxThread::run() @@ -76,7 +76,11 @@ void IdxThread::run() indexingstatus = IDXTS_NULL; // We make a private snapshot of the config: setKeydir changes // it during indexing and it may be updated by the main thread. - RclConfig *myconf = new RclConfig(*cnf); + RclConfig *myconf; + { + PTMutexLocker locker(thestableconfiglock); + myconf = new RclConfig(*thestableconfig); + } int loglevel; myconf->setKeyDir(""); myconf->getConfParam("loglevel", &loglevel); @@ -117,9 +121,8 @@ static IdxThread idxthread; // Functions called by the main thread -void start_idxthread(const RclConfig& cnf) +void start_idxthread() { - idxthread.cnf = &cnf; idxthread.start(); } diff --git a/src/qtgui/idxthread.h b/src/qtgui/idxthread.h index bdf0241a..abb387b6 100644 --- a/src/qtgui/idxthread.h +++ b/src/qtgui/idxthread.h @@ -19,11 +19,11 @@ #include #include "indexer.h" -class RclConfig; - // These two deal with starting / stopping the thread itself, not // indexing sessions. -extern void start_idxthread(const RclConfig& cnf); +// cnf will be cloned each time we start an indexing pass. The pointer must +// stay valid for the whole program duration. +extern void start_idxthread(); extern void stop_idxthread(); // Use these to to request action from thread diff --git a/src/qtgui/main.cpp b/src/qtgui/main.cpp index 618a633f..3cd5ae8d 100644 --- a/src/qtgui/main.cpp +++ b/src/qtgui/main.cpp @@ -47,7 +47,16 @@ #include "smallut.h" #include "recollq.h" -RclConfig *rclconfig; +RclConfig *theconfig; +RclConfig *thestableconfig; +PTMutexInit thestableconfiglock; + +void snapshotConfig() +{ + PTMutexLocker locker(thestableconfiglock); + thestableconfig = new RclConfig(*theconfig); +} + Rcl::Db *rcldb; #ifdef RCL_USE_ASPELL Aspell *aspell; @@ -80,7 +89,7 @@ bool maybeOpenDb(string &reason, bool force) } if (!rcldb->isopen() && !rcldb->open(Rcl::Db::DbRO)) { reason = "Could not open database in " + - rclconfig->getDbDir() + " wait for indexing to complete?"; + theconfig->getDbDir() + " wait for indexing to complete?"; return false; } rcldb->setAbstractParams(-1, prefs.syntAbsLen, prefs.syntAbsCtx); @@ -104,7 +113,8 @@ static void recollCleanup() rwSettings(true); LOGDEB2(("recollCleanup: closing database\n")); deleteZ(rcldb); - deleteZ(rclconfig); + deleteZ(theconfig); + deleteZ(thestableconfig); #ifdef RCL_USE_ASPELL deleteZ(aspell); #endif @@ -165,7 +175,7 @@ int main(int argc, char **argv) { for (int i = 0; i < argc; i++) { if (!strcmp(argv[i], "-t")) { - exit(recollq(&rclconfig, argc, argv)); + exit(recollq(&theconfig, argc, argv)); } } @@ -218,17 +228,18 @@ int main(int argc, char **argv) app.installTranslator( &qt ); string reason; - rclconfig = recollinit(recollCleanup, sigcleanup, reason, &a_config); - if (!rclconfig || !rclconfig->ok()) { + theconfig = recollinit(recollCleanup, sigcleanup, reason, &a_config); + if (!theconfig || !theconfig->ok()) { QString msg = "Configuration problem: "; msg += QString::fromUtf8(reason.c_str()); QMessageBox::critical(0, "Recoll", msg); exit(1); } + snapshotConfig(); // fprintf(stderr, "recollinit done\n"); // Translations for Recoll - string translatdir = path_cat(rclconfig->getDatadir(), "translations"); + string translatdir = path_cat(theconfig->getDatadir(), "translations"); QTranslator translator(0); translator.load( QString("recoll_") + slang, translatdir.c_str() ); app.installTranslator( &translator ); @@ -236,7 +247,7 @@ int main(int argc, char **argv) // fprintf(stderr, "Translations installed\n"); #ifdef RCL_USE_ASPELL - aspell = new Aspell(rclconfig); + aspell = new Aspell(theconfig); aspell->init(reason); if (!aspell || !aspell->ok()) { LOGDEB(("Aspell speller creation failed %s\n", reason.c_str())); @@ -244,7 +255,7 @@ int main(int argc, char **argv) } #endif - string historyfile = path_cat(rclconfig->getConfDir(), "history"); + string historyfile = path_cat(theconfig->getConfDir(), "history"); g_dynconf = new RclDynConf(historyfile); if (!g_dynconf || !g_dynconf->ok()) { QString msg = app.translate("Main", "Configuration problem (dynconf"); @@ -266,7 +277,7 @@ int main(int argc, char **argv) mainWindow->resize(s); } - string dbdir = rclconfig->getDbDir(); + string dbdir = theconfig->getDbDir(); if (dbdir.empty()) { QMessageBox::critical(0, "Recoll", app.translate("Main", @@ -274,7 +285,7 @@ int main(int argc, char **argv) exit(1); } - rcldb = new Rcl::Db(rclconfig); + rcldb = new Rcl::Db(theconfig); mainWindow->show(); QTimer::singleShot(0, mainWindow, SLOT(initDbOpen())); @@ -286,7 +297,7 @@ int main(int argc, char **argv) // Start the indexing thread. It will immediately go to sleep waiting for // something to do. - start_idxthread(*rclconfig); + start_idxthread(); mainWindow->sSearch->searchTypCMB->setCurrentIndex(prefs.ssearchTyp); mainWindow->sSearch->searchTypeChanged(prefs.ssearchTyp); diff --git a/src/qtgui/preview_w.cpp b/src/qtgui/preview_w.cpp index 467de79f..71236267 100644 --- a/src/qtgui/preview_w.cpp +++ b/src/qtgui/preview_w.cpp @@ -518,7 +518,7 @@ void Preview::setCurTabProps(const Rcl::Doc &doc, int docnum) } LOGDEB(("Doc.url: [%s]\n", doc.url.c_str())); string url; - printableUrl(rclconfig->getDefCharset(), doc.url, url); + printableUrl(theconfig->getDefCharset(), doc.url, url); string tiptxt = url + string("\n"); tiptxt += doc.mimetype + " " + string(datebuf) + "\n"; if (meta_it != doc.meta.end() && !meta_it->second.empty()) @@ -538,6 +538,11 @@ bool Preview::makeDocCurrent(const Rcl::Doc& doc, int docnum, bool sametab) { LOGDEB(("Preview::makeDocCurrent: %s\n", doc.url.c_str())); + if (m_loading) { + LOGERR(("Already loading\n")); + return false; + } + /* Check if we already have this page */ for (int i = 0; i < pvTab->count(); i++) { QWidget *tw = pvTab->widget(i); @@ -621,9 +626,10 @@ class LoadThread : public QThread { // QMessageBox::critical(0, "Recoll", Preview::tr("File does not exist")); - FileInterner interner(idoc, rclconfig, tmpdir, + FileInterner interner(idoc, theconfig, tmpdir, FileInterner::FIF_forPreview); - + FIMissingStore mst; + interner.setMissingStore(&mst); // We don't set the interner's target mtype to html because we // do want the html filter to do its work: we won't use the // text, but we need the conversion to utf-8 @@ -647,7 +653,7 @@ class LoadThread : public QThread { } } else { out.mimetype = interner.getMimetype(); - interner.getMissingExternal(missing); + interner.getMissingExternal(&mst, missing); *statusp = -1; } } catch (CancelExcept) { @@ -706,10 +712,6 @@ public: bool Preview::loadDocInCurrentTab(const Rcl::Doc &idoc, int docnum) { LOGDEB1(("PreviewTextEdit::loadDocInCurrentTab()\n")); - if (m_loading) { - LOGERR(("ALready loading\n")); - return false; - } LoadGuard guard(&m_loading); CancelCheck::instance().setCancel(false); @@ -757,14 +759,20 @@ bool Preview::loadDocInCurrentTab(const Rcl::Doc &idoc, int docnum) if (CancelCheck::instance().cancelState()) return false; if (status != 0) { - QMessageBox::warning(0, "Recoll", - tr("Can't turn doc into internal " - "representation for ") + - fdoc.mimetype.c_str()); + QString explain; + if (!lthr.missing.empty()) { + explain = QString::fromAscii("
") + + tr("Missing helper program: ") + + QString::fromLocal8Bit(lthr.missing.c_str()); + } + QMessageBox::warning(0, "Recoll", + tr("Can't turn doc into internal " + "representation for ") + + fdoc.mimetype.c_str() + explain); return false; } // Reset config just in case. - rclconfig->setKeyDir(""); + theconfig->setKeyDir(""); // Create preview text: highlight search terms // We don't do the highlighting for very big texts: too long. We diff --git a/src/qtgui/rclmain_w.cpp b/src/qtgui/rclmain_w.cpp index 9e693708..0e98892c 100644 --- a/src/qtgui/rclmain_w.cpp +++ b/src/qtgui/rclmain_w.cpp @@ -97,6 +97,7 @@ void RclMain::init() spellform = 0; m_idxStatusAck = false; periodictimer = new QTimer(this); + m_periodicToggle = 0; // At least some versions of qt4 don't display the status bar if // it's not created here. @@ -124,7 +125,7 @@ void RclMain::init() // instead string slangs; list langs; - if (rclconfig->getConfParam("indexstemminglanguages", slangs)) { + if (theconfig->getConfParam("indexstemminglanguages", slangs)) { stringToStrings(slangs, langs); } else { QMessageBox::warning(0, "Recoll", @@ -166,7 +167,7 @@ void RclMain::init() connect(bgrp, SIGNAL(buttonClicked(int)), this, SLOT(catgFilter(int))); allRDB->setChecked(true); list cats; - rclconfig->getMimeCategories(cats); + theconfig->getMimeCategories(cats); // Text for button 0 is not used. Next statement just avoids unused // variable compiler warning for catg_strings m_catgbutvec.push_back(catg_strings[0]); @@ -350,7 +351,7 @@ void RclMain::initDbOpen() question (this, "Recoll", qApp->translate("Main", "Could not open database in ") + - QString::fromLocal8Bit(rclconfig->getDbDir().c_str()) + + QString::fromLocal8Bit(theconfig->getDbDir().c_str()) + qApp->translate("Main", ".\n" "Click Cancel if you want to edit the configuration file before indexing starts, or Ok to let it proceed."), @@ -492,7 +493,6 @@ void RclMain::fileExit() // indexing thread and a possible need to exit void RclMain::periodic100() { - static int toggle = 0; // Check if indexing thread done if (idxthread_getStatus() != IDXTS_NULL) { // Indexing is stopped @@ -527,7 +527,7 @@ void RclMain::periodic100() fileToggleIndexingAction->setEnabled(TRUE); periodictimer->setInterval(100); // The toggle thing is for the status to flash - if (toggle < 9) { + if (m_periodicToggle < 9) { QString msg = tr("Indexing in progress: "); DbIxStatus status = idxthread_idxStatus(); QString phs; @@ -548,17 +548,17 @@ void RclMain::periodic100() msg += QString::fromAscii(cnts) + " "; } string mf;int ecnt = 0; - string fcharset = rclconfig->getDefCharset(true); + string fcharset = theconfig->getDefCharset(true); if (!transcode(status.fn, mf, fcharset, "UTF-8", &ecnt) || ecnt) { mf = url_encode(status.fn, 0); } msg += QString::fromUtf8(mf.c_str()); statusBar()->showMessage(msg, 4000); - } else if (toggle == 9) { + } else if (m_periodicToggle == 9) { statusBar()->showMessage(""); } - if (++toggle >= 10) - toggle = 0; + if (++m_periodicToggle >= 10) + m_periodicToggle = 0; } if (recollNeedsExit) fileExit(); @@ -602,7 +602,7 @@ void RclMain::startSearch(RefCntr sdata) string stemLang = (const char *)prefs.queryStemLang.toAscii(); if (stemLang == "ALL") { - rclconfig->getConfParam("indexstemminglanguages", stemLang); + theconfig->getConfParam("indexstemminglanguages", stemLang); } sdata->setStemlang(stemLang); @@ -692,7 +692,7 @@ void RclMain::showIndexConfig() { LOGDEB(("showIndexConfig()\n")); if (indexConfig == 0) { - indexConfig = new ConfIndexW(0, rclconfig); + indexConfig = new ConfIndexW(0, theconfig); connect(new QShortcut(quitKeySeq, indexConfig), SIGNAL (activated()), this, SLOT (fileExit())); } else { @@ -744,7 +744,7 @@ void RclMain::showAboutDialog() void RclMain::showMissingHelpers() { - string miss = rclconfig->getMissingHelperDesc(); + string miss = theconfig->getMissingHelperDesc(); QString msg = tr("External applications/commands needed and not found " "for indexing your file types:\n\n"); if (!miss.empty()) { @@ -984,7 +984,7 @@ void RclMain::saveDocToFile(Rcl::Doc doc) ); string tofile((const char *)s.toLocal8Bit()); TempFile temp; // not used - if (!FileInterner::idocToFile(temp, tofile, rclconfig, doc)) { + if (!FileInterner::idocToFile(temp, tofile, theconfig, doc)) { QMessageBox::warning(0, "Recoll", tr("Cannot extract document or create " "temporary file")); @@ -1034,13 +1034,13 @@ void RclMain::startNativeViewer(Rcl::Doc doc) // Look for appropriate viewer string cmdplusattr; if (prefs.useDesktopOpen) { - cmdplusattr = rclconfig->getMimeViewerDef("application/x-all", ""); + cmdplusattr = theconfig->getMimeViewerDef("application/x-all", ""); } else { string apptag; map::const_iterator it; if ((it = doc.meta.find(Rcl::Doc::keyapptg)) != doc.meta.end()) apptag = it->second; - cmdplusattr = rclconfig->getMimeViewerDef(doc.mimetype, apptag); + cmdplusattr = theconfig->getMimeViewerDef(doc.mimetype, apptag); } if (cmdplusattr.empty()) { @@ -1053,7 +1053,7 @@ void RclMain::startNativeViewer(Rcl::Doc doc) // Extract possible viewer attributes ConfSimple attrs; string cmd; - rclconfig->valueSplitAttributes(cmdplusattr, cmd, attrs); + theconfig->valueSplitAttributes(cmdplusattr, cmd, attrs); bool ignoreipath = false; if (attrs.get("ignoreipath", cmdplusattr)) ignoreipath = stringToBool(cmdplusattr); @@ -1073,7 +1073,7 @@ void RclMain::startNativeViewer(Rcl::Doc doc) // directory string cmdpath; if (!ExecCmd::which(lcmd.front(), cmdpath)) { - cmdpath = rclconfig->findFilter(lcmd.front()); + cmdpath = theconfig->findFilter(lcmd.front()); // findFilter returns its input param if the filter is not in // the normal places. As we already looked in the path, we // have no use for a simple command name here (as opposed to @@ -1128,10 +1128,10 @@ void RclMain::startNativeViewer(Rcl::Doc doc) // If the command wants a file but this is not a file url, or // there is an ipath that it won't understand, we need a temp file: - rclconfig->setKeyDir(path_getfather(fn)); + theconfig->setKeyDir(path_getfather(fn)); if ((wantsfile && fn.empty()) || (!wantsipath && !doc.ipath.empty())) { TempFile temp; - if (!FileInterner::idocToFile(temp, string(), rclconfig, doc)) { + if (!FileInterner::idocToFile(temp, string(), theconfig, doc)) { QMessageBox::warning(0, "Recoll", tr("Cannot extract document or create " "temporary file")); @@ -1145,7 +1145,7 @@ void RclMain::startNativeViewer(Rcl::Doc doc) // If using an actual file, check that it exists, and if it is // compressed, we may need an uncompressed version - if (!fn.empty() && rclconfig->mimeViewerNeedsUncomp(doc.mimetype)) { + if (!fn.empty() && theconfig->mimeViewerNeedsUncomp(doc.mimetype)) { if (access(fn.c_str(), R_OK) != 0) { QMessageBox::warning(0, "Recoll", tr("Can't access file: ") + @@ -1153,8 +1153,8 @@ void RclMain::startNativeViewer(Rcl::Doc doc) return; } TempFile temp; - if (FileInterner::isCompressed(fn, rclconfig)) { - if (!FileInterner::maybeUncompressToTemp(temp, fn, rclconfig, + if (FileInterner::isCompressed(fn, theconfig)) { + if (!FileInterner::maybeUncompressToTemp(temp, fn, theconfig, doc)) { QMessageBox::warning(0, "Recoll", tr("Can't uncompress file: ") + @@ -1208,7 +1208,7 @@ void RclMain::startNativeViewer(Rcl::Doc doc) ncmd += " &"; QStatusBar *stb = statusBar(); if (stb) { - string fcharset = rclconfig->getDefCharset(true); + string fcharset = theconfig->getDefCharset(true); string prcmd; transcode(ncmd, prcmd, fcharset, "UTF-8"); QString msg = tr("Executing: [") + @@ -1234,7 +1234,7 @@ void RclMain::startManual(const string& index) { Rcl::Doc doc; doc.url = "file://"; - doc.url = path_cat(doc.url, rclconfig->getDatadir()); + doc.url = path_cat(doc.url, theconfig->getDatadir()); doc.url = path_cat(doc.url, "doc"); doc.url = path_cat(doc.url, "usermanual.html"); LOGDEB(("RclMain::startManual: help index is %s\n", @@ -1382,7 +1382,7 @@ void RclMain::catgFilter(int id) if (id != 0) { string catg = m_catgbutvec[id]; list tps; - rclconfig->getMimeCatTypes(catg, tps); + theconfig->getMimeCatTypes(catg, tps); for (list::const_iterator it = tps.begin(); it != tps.end(); it++) m_filtspec.orCrit(DocSeqFiltSpec::DSFS_MIMETYPE, *it); diff --git a/src/qtgui/rclmain_w.h b/src/qtgui/rclmain_w.h index 67f7e996..6e5fd69c 100644 --- a/src/qtgui/rclmain_w.h +++ b/src/qtgui/rclmain_w.h @@ -133,7 +133,8 @@ private: bool m_sortspecnochange; DocSeqSortSpec m_sortspec; RefCntr m_source; - + int m_periodicToggle; + virtual void init(); virtual void previewPrevOrNextInTab(Preview *, int sid, int docnum, bool next); diff --git a/src/qtgui/recoll.h b/src/qtgui/recoll.h index 5b85ca3c..c786a5f8 100644 --- a/src/qtgui/recoll.h +++ b/src/qtgui/recoll.h @@ -21,6 +21,7 @@ #include "rclconfig.h" #include "rcldb.h" #include "idxthread.h" +#include "ptmutex.h" // Misc declarations in need of sharing between the UI files @@ -30,7 +31,11 @@ extern bool maybeOpenDb(std::string &reason, bool force = true); /** Retrieve configured stemming languages */ bool getStemLangs(list& langs); -extern RclConfig *rclconfig; +extern RclConfig *theconfig; +extern RclConfig *thestableconfig; +extern PTMutexInit thestableconfiglock; +extern void snapshotConfig(); + extern Rcl::Db *rcldb; extern int recollNeedsExit; extern int startIndexingAfterConfig; // 1st startup diff --git a/src/qtgui/reslist.cpp b/src/qtgui/reslist.cpp index 21036b53..8110d534 100644 --- a/src/qtgui/reslist.cpp +++ b/src/qtgui/reslist.cpp @@ -158,7 +158,7 @@ string QtGuiResListPager::pageTop() string QtGuiResListPager::iconPath(const string& mtype) { string iconpath; - rclconfig->getMimeIconName(mtype, &iconpath); + theconfig->getMimeIconName(mtype, &iconpath); return iconpath; } @@ -167,7 +167,7 @@ void QtGuiResListPager::suggest(const vectoruterms, vector&sugg) sugg.clear(); #ifdef RCL_USE_ASPELL bool noaspell = false; - rclconfig->getConfParam("noaspell", &noaspell); + theconfig->getConfParam("noaspell", &noaspell); if (noaspell) return; if (!aspell) { @@ -524,7 +524,7 @@ void ResList::displayPage() { m_pageParaToReldocnums.clear(); clear(); - m_pager->displayPage(rclconfig); + m_pager->displayPage(theconfig); LOGDEB0(("ResList::resultPageNext: hasNext %d hasPrev %d\n", m_pager->hasPrev(), m_pager->hasNext())); emit prevPageAvailable(m_pager->hasPrev()); diff --git a/src/qtgui/restable.cpp b/src/qtgui/restable.cpp index 02710c5a..3f20aaea 100644 --- a/src/qtgui/restable.cpp +++ b/src/qtgui/restable.cpp @@ -102,7 +102,7 @@ const string& ResTablePager::parFormat() string ResTablePager::iconPath(const string& mtype) { string iconpath; - rclconfig->getMimeIconName(mtype, &iconpath); + theconfig->getMimeIconName(mtype, &iconpath); return iconpath; } @@ -229,8 +229,8 @@ RecollModel::RecollModel(const QStringList fields, QObject *parent) // Add dynamic "stored" fields to the full column list. This // could be protected to be done only once, but it's no real // problem - if (rclconfig) { - const set& stored = rclconfig->getStoredFields(); + if (theconfig) { + const set& stored = theconfig->getStoredFields(); for (set::const_iterator it = stored.begin(); it != stored.end(); it++) { if (o_displayableFields.find(*it) == o_displayableFields.end()) { @@ -535,7 +535,7 @@ void ResTable::onTableView_currentChanged(const QModelIndex& index) m_detail->clear(); m_detaildocnum = index.row(); m_detaildoc = doc; - m_pager->displayDoc(rclconfig, index.row(), doc, m_model->m_hdata); + m_pager->displayDoc(theconfig, index.row(), doc, m_model->m_hdata); } else { m_detaildocnum = -1; } diff --git a/src/qtgui/searchclause_w.cpp b/src/qtgui/searchclause_w.cpp index b9a501ff..85a3219a 100644 --- a/src/qtgui/searchclause_w.cpp +++ b/src/qtgui/searchclause_w.cpp @@ -82,8 +82,8 @@ void SearchClauseW::languageChange() // sTpCMB->insertItem(tr("Complex clause"));//6 fldCMB->addItem(tr("In field")); - if (rclconfig) { - set fields = rclconfig->getIndexedFields(); + if (theconfig) { + set fields = theconfig->getIndexedFields(); for (set::const_iterator it = fields.begin(); it != fields.end(); it++) { // Some fields don't make sense here diff --git a/src/qtgui/spell_w.cpp b/src/qtgui/spell_w.cpp index b8d5b848..b8aeb85e 100644 --- a/src/qtgui/spell_w.cpp +++ b/src/qtgui/spell_w.cpp @@ -50,7 +50,7 @@ void SpellW::init() /*2*/expTypeCMB->addItem(tr("Stem expansion")); #ifdef RCL_USE_ASPELL bool noaspell = false; - rclconfig->getConfParam("noaspell", &noaspell); + theconfig->getConfParam("noaspell", &noaspell); if (!noaspell) /*3*/expTypeCMB->addItem(tr("Spelling/Phonetic")); #endif diff --git a/src/qtgui/ssearch_w.cpp b/src/qtgui/ssearch_w.cpp index b6bea470..33ba5c06 100644 --- a/src/qtgui/ssearch_w.cpp +++ b/src/qtgui/ssearch_w.cpp @@ -132,9 +132,9 @@ void SSearch::startSimpleSearch() if (tp == SST_LANG) { string reason; if (prefs.autoSuffsEnable) - sdata = wasaStringToRcl(rclconfig, u8, reason, (const char *)prefs.autoSuffs.toUtf8()); + sdata = wasaStringToRcl(theconfig, u8, reason, (const char *)prefs.autoSuffs.toUtf8()); else - sdata = wasaStringToRcl(rclconfig, u8, reason); + sdata = wasaStringToRcl(theconfig, u8, reason); if (sdata == 0) { QMessageBox::warning(0, "Recoll", tr("Bad query string") + ": " + QString::fromAscii(reason.c_str())); @@ -280,7 +280,7 @@ void SSearch::completion() Rcl::TermMatchResult tmres; string stemLang = (const char *)prefs.queryStemLang.toAscii(); if (stemLang == "ALL") { - rclconfig->getConfParam("indexstemminglanguages", stemLang); + theconfig->getConfParam("indexstemminglanguages", stemLang); } if (!rcldb->termMatch(Rcl::Db::ET_WILD, stemLang, s, tmres, max) || tmres.entries.size() == 0) { diff --git a/src/qtgui/uiprefs_w.cpp b/src/qtgui/uiprefs_w.cpp index 2ba088db..c736bed1 100644 --- a/src/qtgui/uiprefs_w.cpp +++ b/src/qtgui/uiprefs_w.cpp @@ -372,7 +372,7 @@ void UIPrefsDialog::addExtraDbPB_clicked() } struct stat st1, st2; stat(dbdir.c_str(), &st1); - string rcldbdir = rclconfig->getDbDir(); + string rcldbdir = theconfig->getDbDir(); stat(rcldbdir.c_str(), &st2); path_catslash(rcldbdir); diff --git a/src/qtgui/viewaction_w.cpp b/src/qtgui/viewaction_w.cpp index 94eb1ea4..85b9c4a6 100644 --- a/src/qtgui/viewaction_w.cpp +++ b/src/qtgui/viewaction_w.cpp @@ -51,7 +51,7 @@ void ViewAction::fillLists() actionsLV->clear(); actionsLV->verticalHeader()->setDefaultSectionSize(20); vector > defs; - rclconfig->getMimeViewerDefs(defs); + theconfig->getMimeViewerDefs(defs); actionsLV->setRowCount(defs.size()); int row = 0; for (vector >::const_iterator it = defs.begin(); @@ -128,7 +128,7 @@ void ViewAction::editActions() string sact = (const char *)newaction.toLocal8Bit(); for (list::const_iterator it = mtypes.begin(); it != mtypes.end(); it++) { - rclconfig->setMimeViewerDef(*it, sact); + theconfig->setMimeViewerDef(*it, sact); } fillLists(); } diff --git a/src/query/reslistpager.cpp b/src/query/reslistpager.cpp index 2a64ed2b..5a69a0dd 100644 --- a/src/query/reslistpager.cpp +++ b/src/query/reslistpager.cpp @@ -372,7 +372,7 @@ string ResListPager::detailsLink() const string &ResListPager::parFormat() { - const static string format("" + static const string format("" "%R %S %L   %T
" "%M %D   %U
" "%A %K"); diff --git a/src/rcldb/rcldb.cpp b/src/rcldb/rcldb.cpp index f60eac1b..ba77b0bf 100644 --- a/src/rcldb/rcldb.cpp +++ b/src/rcldb/rcldb.cpp @@ -80,7 +80,7 @@ string version_string(){ // Synthetic abstract marker (to discriminate from abstract actually // found in document) -const static string rclSyntAbs("?!#@"); +static const string rclSyntAbs("?!#@"); // Compute the unique term used to link documents to their origin. // "Q" + external udi diff --git a/src/rcldb/stemdb.cpp b/src/rcldb/stemdb.cpp index e7f7957a..e5275c79 100644 --- a/src/rcldb/stemdb.cpp +++ b/src/rcldb/stemdb.cpp @@ -38,7 +38,7 @@ namespace Rcl { namespace StemDb { -const static string stemdirstem = "stem_"; +static const string stemdirstem = "stem_"; /// Compute name of stem db for given base database and language static string stemdbname(const string& dbdir, const string& lang) diff --git a/src/utils/execmd.cpp b/src/utils/execmd.cpp index c08e81de..ce9a4f5d 100644 --- a/src/utils/execmd.cpp +++ b/src/utils/execmd.cpp @@ -19,7 +19,6 @@ #include #include -#include #include #include #include @@ -44,6 +43,7 @@ #include "smallut.h" #include "netcon.h" #include "closefrom.h" +#include "ptmutex.h" #ifndef NO_NAMESPACES using namespace std; @@ -316,38 +316,20 @@ private: // The netcon selectloop that doexec() uses for reading/writing would // be complicated to render thread-safe. Use locking to ensure only // one thread in there -class ExecLocking { -public: - pthread_mutex_t m_mutex; - ExecLocking() - { - pthread_mutex_init(&m_mutex, 0); - } -}; -ExecLocking o_lock; -class ExecLocker { -public: - ExecLocker() - { - pthread_mutex_lock(&o_lock.m_mutex); - } - ~ExecLocker() - { - pthread_mutex_unlock(&o_lock.m_mutex); - } -}; +static PTMutexInit o_lock; int ExecCmd::doexec(const string &cmd, const list& args, const string *input, string *output) { + // Only one thread allowed in here... + PTMutexLocker locker(o_lock); + if (startExec(cmd, args, input != 0, output != 0) < 0) { return -1; } // Cleanup in case we return early ExecCmdRsrc e(this); - // Only one thread allowed in here... - ExecLocker locker; int ret = 0; if (input || output) { diff --git a/src/utils/ptmutex.h b/src/utils/ptmutex.h new file mode 100644 index 00000000..67cdb2e3 --- /dev/null +++ b/src/utils/ptmutex.h @@ -0,0 +1,52 @@ +/* Copyright (C) 2011 J.F.Dockes + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#ifndef _PTMUTEX_H_INCLUDED_ +#define _PTMUTEX_H_INCLUDED_ + +#include + +/// A trivial wrapper/helper for pthread mutex locks + + +/// Init lock. Used as a single PTMutexInit static object. +class PTMutexInit { +public: + pthread_mutex_t m_mutex; + PTMutexInit() + { + pthread_mutex_init(&m_mutex, 0); + } +}; + +/// Take the lock when constructed, release when deleted +class PTMutexLocker { +public: + PTMutexLocker(PTMutexInit& l) : m_lock(l) + { + m_status = pthread_mutex_lock(&m_lock.m_mutex); + } + ~PTMutexLocker() + { + pthread_mutex_unlock(&m_lock.m_mutex); + } + int ok() {return m_status == 0;} +private: + PTMutexInit& m_lock; + int m_status; +}; + +#endif /* _PTMUTEX_H_INCLUDED_ */