Ticket #1316: dir_watch_fam.cpp.patch

File dir_watch_fam.cpp.patch, 6.6 KB (added by noKid, 12 years ago)

Gamin / FAM are not needed anymore. Most of the work was to rewrite event processing.

  • dir_watch_fam.cpp

     
    2828#include "lib/sysdep/sysdep.h"
    2929#include "lib/sysdep/dir_watch.h"
    3030#include "ps/CLogger.h"
     31#include <sys/inotify.h>
    3132
    32 #if !CONFIG2_FAM
     33// Stub implementations removed as inotify is part of the kernel.
    3334
    34 // stub implementations
    35 
    36 Status dir_watch_Add(const OsPath& UNUSED(path), PDirWatch& UNUSED(dirWatch))
    37 {
    38     return INFO::OK;
    39 }
    40 
    41 Status dir_watch_Poll(DirWatchNotifications& UNUSED(notifications))
    42 {
    43     return INFO::OK;
    44 }
    45 
    46 #else
    47 
    48 #include <fam.h>
    49 
    50 // FAMEvent is large (~4KB), so define a smaller structure to store events
    5135struct NotificationEvent
    5236{
    5337    std::string filename;
    54     void *userdata;
    55     FAMCodes code;
     38    uint32_t code;
     39    int wd;
    5640};
    5741
    5842// To avoid deadlocks and slow synchronous reads, it's necessary to use a
    59 // separate thread for reading events from FAM.
     43// separate thread for reading events from inotify.
    6044// So we just spawn a thread to push events into this list, then swap it out
    6145// when someone calls dir_watch_Poll.
    6246// (We assume STL memory allocation is thread-safe.)
     
    7256// FAM is broken or unavailable.
    7357static int initialized = 0;
    7458
    75 static FAMConnection fc;
     59// Inotify file descriptor
     60int inotifyfd;
    7661
     62// FAM was able to give the whole path of the event thanks to *userdata
     63// With inotify, using a Vector seems to be a good alternative.
     64static std::vector<DirWatch> g_paths;
     65
    7766struct DirWatch
    7867{
    7968    DirWatch()
    80         : reqnum(-1)
     69        : reqnum(-1)
    8170    {
    8271    }
    83 
     72   
    8473    ~DirWatch()
    8574    {
    8675        ENSURE(initialized > 0);
    87 
    88         FAMRequest req;
    89         req.reqnum = reqnum;
    90         FAMCancelMonitor(&fc, &req);
    9176    }
    92 
     77   
    9378    OsPath path;
    94     int reqnum;
     79    int reqnum;     
    9580};
    9681
    97 
    9882// for atexit
    9983static void fam_deinit()
    10084{
    101     FAMClose(&fc);
     85    close(inotifyfd);
    10286
    10387    pthread_cancel(g_event_loop_thread);
    10488    // NOTE: POSIX threads are (by default) only cancellable inside particular
     
    11094    pthread_join(g_event_loop_thread, NULL);
    11195}
    11296
     97
    11398static void fam_event_loop_process_events()
    11499{
    115     while(FAMPending(&fc) > 0)
     100    // Buffer for reading the events.
     101    // Need to be careful about overflow here.
     102    char buffer[65535];
     103   
     104    // Event iterator
     105    size_t buffer_i = 0;
     106
     107    // Total size of all the events
     108    ssize_t r;
     109   
     110    // Size & struct for the current event
     111    size_t event_size;
     112    struct inotify_event *pevent;
     113       
     114    r = read(inotifyfd, buffer, 65535);
     115    if(r <= 0)
     116        return;
     117   
     118    while(buffer_i < r)
    116119    {
    117         FAMEvent e;
    118         if(FAMNextEvent(&fc, &e) < 0)
    119         {
    120             debug_printf(L"FAMNextEvent error");
    121             return;
    122         }
    123 
    124120        NotificationEvent ne;
    125         ne.filename = e.filename;
    126         ne.userdata = e.userdata;
    127         ne.code = e.code;
    128 
     121        pevent = (struct inotify_event *) &buffer[buffer_i];
     122        event_size = offsetof(struct inotify_event, name) + pevent->len;
     123        ne.wd = pevent->wd;
     124        ne.filename = pevent->name;
     125        ne.code = pevent->mask;     
    129126        pthread_mutex_lock(&g_mutex);
    130127        g_notifications.push_back(ne);
    131128        pthread_mutex_unlock(&g_mutex);
     129       
     130        buffer_i += event_size;
    132131    }
    133132}
    134133
    135134static void* fam_event_loop(void*)
    136135{
    137     int famfd = FAMCONNECTION_GETFD(&fc);
    138 
    139136    while(true)
    140137    {
    141138        fd_set fdrset;
    142139        FD_ZERO(&fdrset);
    143         FD_SET(famfd, &fdrset);
     140        FD_SET(inotifyfd, &fdrset);
    144141
    145142        // Block with select until there's events waiting
    146143        // (Mustn't just block inside FAMNextEvent since fam will deadlock)
    147         while(select(famfd+1, &fdrset, NULL, NULL, NULL) < 0)
     144        while(select(inotifyfd+1, &fdrset, NULL, NULL, NULL) < 0)
    148145        {
    149146            if(errno == EINTR)
    150147            {
    151148                // interrupted - try again
    152149                FD_ZERO(&fdrset);
    153                 FD_SET(famfd, &fdrset);
     150                FD_SET(inotifyfd, &fdrset);
    154151            }
    155152            else if(errno == EBADF)
    156153            {
    157154                // probably just lost the connection to FAM - kill the thread
    158                 debug_printf(L"lost connection to FAM");
     155                debug_printf(L"Invalid file descriptor");
    159156                return NULL;
    160157            }
    161158            else
     
    166163            }
    167164        }
    168165
    169         if(FD_ISSET(famfd, &fdrset))
     166        if(FD_ISSET(inotifyfd, &fdrset))
    170167            fam_event_loop_process_events();
    171168    }
    172169}
     
    179176
    180177    if(!initialized)
    181178    {
    182         if(FAMOpen2(&fc, "lib_res"))
     179        if((inotifyfd = inotify_init()) < 0)
    183180        {
     181            // Check for error ?
    184182            initialized = -1;
    185             LOGERROR(L"Error initializing FAM; hotloading will be disabled");
    186             return ERR::FAIL;   // NOWARN
     183            LOGERROR(L"Error initializing inotify file descriptor; hotloading will be disabled");
     184            return ERR::FAIL;
    187185        }
    188186       
    189187        if (pthread_create(&g_event_loop_thread, NULL, &fam_event_loop, NULL))
    190188        {
    191189            initialized = -1;
    192             LOGERROR(L"Error creating FAM event loop thread; hotloading will be disabled");
     190            LOGERROR(L"Error creating inotify event loop thread; hotloading will be disabled");
    193191            return ERR::FAIL;   // NOWARN
    194192        }
    195193
     
    198196    }
    199197
    200198    PDirWatch tmpDirWatch(new DirWatch);
     199    int wd = -1;
    201200
    202     // NOTE: It would be possible to use FAMNoExists iff we're building with Gamin
    203     // (not FAM), to avoid a load of boring notifications when we add a directory,
    204     // but it would only save tens of milliseconds of CPU time, so it's probably
    205     // not worthwhile
    206 
    207     FAMRequest req;
    208     if(FAMMonitorDirectory(&fc, OsString(path).c_str(), &req, tmpDirWatch.get()) < 0)
     201    if( (wd = inotify_add_watch(inotifyfd, OsString(path).c_str(), IN_CREATE | IN_DELETE | IN_MODIFY)) < 0)
    209202    {
    210203        debug_warn(L"res_watch_dir failed!");
    211         WARN_RETURN(ERR::FAIL); // no way of getting error code?
     204        WARN_RETURN(ERR::FAIL); // no way of getting error code?
    212205    }
    213 
     206   
    214207    dirWatch.swap(tmpDirWatch);
    215208    dirWatch->path = path;
    216     dirWatch->reqnum = req.reqnum;
    217 
     209    dirWatch->reqnum = wd;
     210    g_paths.push_back(*dirWatch);
     211   
    218212    return INFO::OK;
    219213}
    220214
     
    238232        DirWatchNotification::EType type;
    239233        switch(polled_notifications[i].code)
    240234        {
    241         case FAMChanged:
     235        case IN_MODIFY:
    242236            type = DirWatchNotification::Changed;
    243237            break;
    244         case FAMCreated:
     238        case IN_CREATE:
    245239            type = DirWatchNotification::Created;
    246240            break;
    247         case FAMDeleted:
     241        case IN_DELETE:
    248242            type = DirWatchNotification::Deleted;
    249243            break;
    250244        default:
    251245            continue;
    252246        }
    253         DirWatch* dirWatch = (DirWatch*)polled_notifications[i].userdata;
    254         OsPath pathname = dirWatch->path / polled_notifications[i].filename;
    255         notifications.push_back(DirWatchNotification(pathname, type));
     247       
     248        OsPath filename;
     249        for(std::vector<DirWatch>::iterator it = g_paths.begin();
     250            it != g_paths.end();
     251            ++it)
     252        {
     253            if( (*it).reqnum == polled_notifications[i].wd)
     254            {
     255                filename = Path(OsString((*it).path).append(polled_notifications[i].filename));
     256                break;
     257            }
     258        }
     259        notifications.push_back(DirWatchNotification(filename, type));
    256260    }
    257261
    258262    // nothing new; try again later
    259263    return INFO::OK;
    260264}
    261265
    262 #endif  // CONFIG2_FAM