Changeset 945 for trunk/src


Ignore:
Timestamp:
Aug 5, 2011, 11:26:12 PM (14 years ago)
Author:
Dmitry A. Kuminov
Message:

OS/2: Make native QFileSystemWatcher recognize file changes.

The original notification mechanism does not actually implement
the file date/time/size change notification. To solve this,
we add another thread that only check for file attribute changes
at specified polling intervals (using a relatively fast native stat call)
and nothing more.

The file change notification is needed e.g. for Qt Creator which
uses it to detect external file notifications in the editor.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/corelib/io/qfilesystemwatcher_os2.cpp

    r944 r945  
    9494QT_BEGIN_NAMESPACE
    9595
     96// -----------------------------------------------------------------------------
     97
     98// we don't actually get RCNF_CHANGED from OS/2 when the file modification date
     99// or size changes (some really silly bug), so we need a poller thread that
     100// will query this information from time to time
     101class QOS2FileSysPollerThread : public QThread
     102{
     103private:
     104
     105    enum { PollingInterval = 1000 };
     106
     107    friend class QOS2FileSysWatcherThread;
     108
     109    QOS2FileSysPollerThread();
     110    ~QOS2FileSysPollerThread();
     111
     112    void addPaths(const QStringList &paths);
     113    void removePaths(const QStringList &paths);
     114
     115    void run();
     116
     117    void stop(QMutexLocker &locker);
     118    void stop() { QMutexLocker locker(&mutex); stop(locker); }
     119
     120    bool finish;
     121    HEV eventSem;
     122
     123    typedef QHash<QString, FILESTATUS3> PathMap;
     124    PathMap watchedPaths;
     125    QMutex mutex;
     126};
     127
    96128class QOS2FileSysWatcherThread : public QThread
    97129{
     
    113145    static void removePaths(QOS2FileSystemWatcherEngine *engine);
    114146
     147    static void reportChanges(const QStringList &paths);
     148
    115149    static bool isOk() { return instance->notifyPipe != NULLHANDLE; }
    116150
     
    121155
    122156    void run();
     157
     158    void stop(QMutexLocker &locker);
    123159
    124160    int refcnt;
     
    127163    HFILE notifyPipe;
    128164    HEV eventSem;
     165
     166    QOS2FileSysPollerThread poller;
    129167
    130168    enum Type { None = 0, Dir, File };
     
    143181};
    144182
     183// -----------------------------------------------------------------------------
     184
     185QOS2FileSysPollerThread::QOS2FileSysPollerThread()
     186    : finish(false), eventSem(NULLHANDLE)
     187{
     188    APIRET arc;
     189    arc = DosCreateEventSem(NULL, &eventSem,
     190                            DC_SEM_SHARED | DCE_AUTORESET | DCE_POSTONE,
     191                            FALSE);
     192    Q_ASSERT(arc == NO_ERROR);
     193    Q_UNUSED(arc);
     194}
     195
     196QOS2FileSysPollerThread::~QOS2FileSysPollerThread()
     197{
     198    Q_ASSERT(watchedPaths.isEmpty());
     199
     200    if (eventSem)
     201        DosCloseEventSem(eventSem);
     202}
     203
     204void QOS2FileSysPollerThread::addPaths(const QStringList &paths)
     205{
     206    APIRET arc;
     207    FILESTATUS3 newInfo;
     208
     209    QMutexLocker locker(&mutex);
     210
     211    foreach (const QString &path, paths) {
     212        arc = DosQueryPathInfo(QFile::encodeName(path), FIL_STANDARD,
     213                               &newInfo, sizeof(newInfo));
     214        if (arc == NO_ERROR)
     215            watchedPaths.insert(path, newInfo);
     216    }
     217
     218    if (!watchedPaths.isEmpty() && !isRunning()) {
     219        // (re)start the thread
     220        finish = false;
     221        start(IdlePriority);
     222    }
     223}
     224
     225void QOS2FileSysPollerThread::removePaths(const QStringList &paths)
     226{
     227    QMutexLocker locker(&mutex);
     228
     229    foreach (const QString &path, paths) {
     230        watchedPaths.remove(path);
     231    }
     232
     233    if (watchedPaths.isEmpty())
     234        stop(locker);
     235}
     236
     237void QOS2FileSysPollerThread::run()
     238{
     239    APIRET arc;
     240    QStringList changedPaths;
     241
     242    QMutexLocker locker(&mutex);
     243
     244    forever {
     245        locker.unlock();
     246
     247        if (!changedPaths.isEmpty()) {
     248            // important: must be done outside the lock to avoid the deadlock
     249            // with QOS2FileSysWatcherThread that may be calling us
     250            QOS2FileSysWatcherThread::reportChanges(changedPaths);
     251            changedPaths.clear();
     252        }
     253
     254        qDosNI(arc = DosWaitEventSem(eventSem, PollingInterval));
     255
     256        locker.relock();
     257
     258        for (PathMap::iterator it = watchedPaths.begin();
     259             it != watchedPaths.end(); ++it) {
     260            QString path = it.key();
     261            FILESTATUS3 &info = it.value();
     262
     263            FILESTATUS3 newInfo;
     264            arc = DosQueryPathInfo(QFile::encodeName(path), FIL_STANDARD,
     265                                   &newInfo, sizeof(newInfo));
     266            if (arc != NO_ERROR)
     267                continue;
     268
     269            if (memcmp(&newInfo.fdateLastWrite,
     270                       &info.fdateLastWrite, sizeof(FDATE)) != 0 ||
     271                newInfo.cbFile != info.cbFile ||
     272                newInfo.cbFileAlloc != info.cbFileAlloc ||
     273                newInfo.attrFile != info.attrFile) {
     274                // there was a change, memorize it and update the info
     275                changedPaths << path;
     276                info = newInfo;
     277            }
     278        }
     279
     280        if (finish)
     281            break;
     282    }
     283}
     284
     285void QOS2FileSysPollerThread::stop(QMutexLocker &locker)
     286{
     287    Q_ASSERT(locker.locked());
     288
     289    if (isRunning()) {
     290        finish = true;
     291        DosPostEventSem(eventSem);
     292        locker.unlock();
     293        wait();
     294    }
     295}
     296
     297// -----------------------------------------------------------------------------
     298
    145299// static
    146300QOS2FileSysWatcherThread *QOS2FileSysWatcherThread::instance = 0;
     
    170324        QOS2FileSysWatcherThread *instance = QOS2FileSysWatcherThread::instance;
    171325        QOS2FileSysWatcherThread::instance = 0;
    172         if (instance->isRunning()) {
    173             // stop the thread
    174             instance->finish = true;
    175             locker.unlock();
    176             DosPostEventSem(instance->eventSem);
    177             instance->wait();
    178         }
     326        instance->poller.stop();
     327        instance->stop(locker);
    179328        delete instance;
    180329    }
     
    228377{
    229378    Q_ASSERT(!refcnt);
    230     Q_ASSERT(!watchedPaths.size());
     379    Q_ASSERT(watchedPaths.isEmpty());
    231380
    232381    if (notifyPipe != NULLHANDLE) {
     
    236385}
    237386
     387// static
    238388QStringList QOS2FileSysWatcherThread::addPaths(QOS2FileSystemWatcherEngine *engine,
    239389                                               const QStringList &paths,
     
    243393    QMutexLocker locker(&mutex);
    244394
    245     QStringList p = paths;
     395    QStringList p = paths, pollerPaths;
    246396    QMutableListIterator<QString> it(p);
    247397    while (it.hasNext()) {
     
    264414        }
    265415
     416        if (!instance->watchedPaths.contains(normalPath))
     417            pollerPaths << normalPath;
     418
    266419        QList<PathInfo> &variants = instance->watchedPaths[normalPath];
    267         bool alreadyAdded = false;
     420        bool found = false, alreadyAdded = false;
    268421        for (QList<PathInfo>::iterator pathIt = variants.begin();
    269422             pathIt != variants.end(); ++pathIt) {
    270423            if (pathIt->path == path) {
    271                 if (pathIt->engines.contains(engine)) {
     424                found = true;
     425                if (pathIt->engines.contains(engine))
    272426                    alreadyAdded = true;
    273                     break;
    274                 }
    275                 pathIt->engines.append(engine);
     427                else
     428                    pathIt->engines.append(engine);
     429                break;
    276430            }
    277431        }
     
    279433            continue;
    280434
    281         variants << PathInfo(path, type, engine);
     435        if (!found)
     436            variants << PathInfo(path, type, engine);
    282437        it.remove();
    283438    }
     
    289444    }
    290445
     446    // add new normal paths to the poller
     447    if (!pollerPaths.isEmpty())
     448        instance->poller.addPaths(pollerPaths);
     449
    291450    return p;
    292451}
    293452
     453// static
    294454QStringList QOS2FileSysWatcherThread::removePaths(QOS2FileSystemWatcherEngine *engine,
    295455                                                  const QStringList &paths,
     
    299459    QMutexLocker locker(&mutex);
    300460
    301     QStringList p = paths;
     461    QStringList p = paths, pollerPaths;
    302462    QMutableListIterator<QString> it(p);
    303463    while (it.hasNext()) {
     
    322482            }
    323483
    324             if (variants.isEmpty())
     484            if (variants.isEmpty()) {
    325485                instance->watchedPaths.remove(normalPath);
    326         }
    327     }
    328 
    329     if (instance->watchedPaths.isEmpty()) {
    330         // stop the thread
    331         instance->finish = true;
    332         DosPostEventSem(instance->eventSem);
    333     }
     486                pollerPaths << normalPath;
     487            }
     488        }
     489    }
     490
     491    // remove unwatched normal paths from the poller
     492    if (!pollerPaths.isEmpty())
     493        instance->poller.removePaths(pollerPaths);
     494
     495    if (instance->watchedPaths.isEmpty())
     496        instance->stop(locker);
    334497
    335498    return p;
    336499}
    337500
     501// static
    338502void QOS2FileSysWatcherThread::removePaths(QOS2FileSystemWatcherEngine *engine)
    339503{
    340504    QMutexLocker locker(&mutex);
    341505
     506    QStringList pollerPaths;
    342507    foreach (const QString &normalPath, instance->watchedPaths.keys()) {
    343508        QList<PathInfo> &variants = instance->watchedPaths[normalPath];
     
    352517        }
    353518
    354         if (variants.isEmpty())
     519        if (variants.isEmpty()) {
    355520            instance->watchedPaths.remove(normalPath);
    356     }
    357 
    358     if (instance->watchedPaths.isEmpty()) {
    359         // stop the thread
    360         instance->finish = true;
    361         DosPostEventSem(instance->eventSem);
     521            pollerPaths << normalPath;
     522        }
     523    }
     524
     525    // remove unwatched normal paths from the poller
     526    if (!pollerPaths.isEmpty())
     527        instance->poller.removePaths(pollerPaths);
     528
     529    if (instance->watchedPaths.isEmpty() && instance->isRunning())
     530        instance->stop(locker);
     531}
     532
     533// static
     534void QOS2FileSysWatcherThread::reportChanges(const QStringList &paths)
     535{
     536    QMutexLocker locker(&mutex);
     537
     538    foreach (const QString &path, paths) {
     539        // signal the change
     540        if (instance->watchedPaths.contains(path)) {
     541            QList<PathInfo> &variants = instance->watchedPaths[path];
     542            foreach(const PathInfo &pi, variants) {
     543                foreach(QOS2FileSystemWatcherEngine *e, pi.engines) {
     544                    if (pi.type == File)
     545                        e->doFileChanged(pi.path, false);
     546                    else
     547                        e->doDirectoryChanged(pi.path, false);
     548                }
     549            }
     550        }
    362551    }
    363552}
     
    421610                        bool deleted = info->bAction != RCNF_CHANGED;
    422611                        if (deleted) {
     612                            // make a copy as we will need to iterate over it
    423613                            deletedVariants = variants;
    424614                            variants = deletedVariants;
     615                            // remove the deleted path from watching
    425616                            watchedPaths.remove(normalPath);
     617                            instance->poller.removePaths(QStringList(normalPath));
    426618                        }
    427619                        foreach(const PathInfo &pi, variants) {
     
    467659}
    468660
     661void QOS2FileSysWatcherThread::stop(QMutexLocker &locker)
     662{
     663    Q_ASSERT(locker.locked());
     664
     665    if (instance->isRunning()) {
     666        // stop the thread
     667        finish = true;
     668        DosPostEventSem(eventSem);
     669        locker.unlock();
     670        wait();
     671    }
     672}
     673
     674// -----------------------------------------------------------------------------
     675
    469676QOS2FileSystemWatcherEngine::QOS2FileSystemWatcherEngine()
    470677{
Note: See TracChangeset for help on using the changeset viewer.