Ticket #1316: dir_watch_fam.cpp.2.patch

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

2nd version of the dir_watch_fam.cpp patch

  • 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
     60static int inotifyfd;
     61 
     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::map<int, PDirWatch> g_paths;
    7665
    7766struct DirWatch
    7867{
     
    8473    ~DirWatch()
    8574    {
    8675        ENSURE(initialized > 0);
    87 
    88         FAMRequest req;
    89         req.reqnum = reqnum;
    90         FAMCancelMonitor(&fc, &req);
     76        inotify_rm_watch(inotifyfd, reqnum);
    9177    }
    9278
    9379    OsPath path;
    9480    int reqnum;
    9581};
    9682
    97 
    9883// for atexit
    9984static void fam_deinit()
    10085{
    101     FAMClose(&fc);
     86    close(inotifyfd);
    10287
    10388    pthread_cancel(g_event_loop_thread);
    10489    // NOTE: POSIX threads are (by default) only cancellable inside particular
     
    11297
    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 
    129         pthread_mutex_lock(&g_mutex);
    130         g_notifications.push_back(ne);
    131         pthread_mutex_unlock(&g_mutex);
     121        pevent = (struct inotify_event *) &buffer[buffer_i];
     122       
     123        event_size = offsetof(struct inotify_event, name) + pevent->len;
     124        ne.wd = pevent->wd;
     125        ne.filename = pevent->name;
     126        ne.code = pevent->mask;
     127       
     128        pthread_mutex_lock(&g_mutex);
     129        g_notifications.push_back(ne);
     130        pthread_mutex_unlock(&g_mutex);
     131       
     132        buffer_i += event_size;
    132133    }
    133134}
    134135
    135136static void* fam_event_loop(void*)
    136137{
    137     int famfd = FAMCONNECTION_GETFD(&fc);
    138 
    139138    while(true)
    140139    {
    141140        fd_set fdrset;
    142141        FD_ZERO(&fdrset);
    143         FD_SET(famfd, &fdrset);
    144 
     142        FD_SET(inotifyfd, &fdrset);
    145143        // Block with select until there's events waiting
    146144        // (Mustn't just block inside FAMNextEvent since fam will deadlock)
    147         while(select(famfd+1, &fdrset, NULL, NULL, NULL) < 0)
     145        while(select(inotifyfd+1, &fdrset, NULL, NULL, NULL) < 0)
    148146        {
    149147            if(errno == EINTR)
    150148            {
    151149                // interrupted - try again
    152150                FD_ZERO(&fdrset);
    153                 FD_SET(famfd, &fdrset);
     151                FD_SET(inotifyfd, &fdrset);
    154152            }
    155153            else if(errno == EBADF)
    156154            {
    157155                // probably just lost the connection to FAM - kill the thread
    158                 debug_printf(L"lost connection to FAM");
     156                debug_printf(L"Invalid file descriptor");
    159157                return NULL;
    160158            }
    161159            else
     
    165163                return NULL;
    166164            }
    167165        }
    168 
    169         if(FD_ISSET(famfd, &fdrset))
     166        if(FD_ISSET(inotifyfd, &fdrset))
     167        {
    170168            fam_event_loop_process_events();
     169            std::cout << "event !" << std::endl;
     170        }
    171171    }
    172172}
    173173
    174174Status dir_watch_Add(const OsPath& path, PDirWatch& dirWatch)
    175175{
    176     // init already failed; don't try again or complain
     176    char    resolved[PATH_MAX + 1];
     177   
     178    // init already failed; don't try again or complain
    177179    if(initialized == -1)
    178180        return ERR::FAIL;   // NOWARN
    179181
    180182    if(!initialized)
    181183    {
    182         if(FAMOpen2(&fc, "lib_res"))
     184        if((inotifyfd = inotify_init()) < 0)
    183185        {
     186            // Check for error ?
    184187            initialized = -1;
    185             LOGERROR(L"Error initializing FAM; hotloading will be disabled");
    186             return ERR::FAIL;   // NOWARN
     188            LOGERROR(L"Error initializing inotify file descriptor; hotloading will be disabled");
     189            return ERR::FAIL;
    187190        }
    188191       
    189192        if (pthread_create(&g_event_loop_thread, NULL, &fam_event_loop, NULL))
    190193        {
    191194            initialized = -1;
    192             LOGERROR(L"Error creating FAM event loop thread; hotloading will be disabled");
     195            LOGERROR(L"Error creating inotify event loop thread; hotloading will be disabled");
    193196            return ERR::FAIL;   // NOWARN
    194197        }
    195198
     
    198201    }
    199202
    200203    PDirWatch tmpDirWatch(new DirWatch);
     204    int wd = -1;
    201205
    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)
     206    if( (wd = inotify_add_watch(inotifyfd, realpath(OsString(path).c_str(), resolved), IN_CREATE | IN_DELETE | IN_CLOSE_WRITE)) < 0)
    209207    {
    210208        debug_warn(L"res_watch_dir failed!");
    211         WARN_RETURN(ERR::FAIL); // no way of getting error code?
     209        WARN_RETURN(ERR::FAIL); // no way of getting error code?
    212210    }
    213 
     211   
    214212    dirWatch.swap(tmpDirWatch);
    215213    dirWatch->path = path;
    216     dirWatch->reqnum = req.reqnum;
    217 
     214    dirWatch->reqnum = wd;
     215    g_paths.insert(std::make_pair(wd, dirWatch));
     216   
    218217    return INFO::OK;
    219218}
    220219
    221 
    222 
    223220Status dir_watch_Poll(DirWatchNotifications& notifications)
    224221{
    225222    if(initialized == -1)
     
    238235        DirWatchNotification::EType type;
    239236        switch(polled_notifications[i].code)
    240237        {
    241         case FAMChanged:
     238        case IN_CLOSE_WRITE:
    242239            type = DirWatchNotification::Changed;
    243240            break;
    244         case FAMCreated:
     241        case IN_CREATE:
    245242            type = DirWatchNotification::Created;
    246243            break;
    247         case FAMDeleted:
     244        case IN_DELETE:
    248245            type = DirWatchNotification::Deleted;
    249246            break;
    250247        default:
    251248            continue;
    252249        }
    253         DirWatch* dirWatch = (DirWatch*)polled_notifications[i].userdata;
    254         OsPath pathname = dirWatch->path / polled_notifications[i].filename;
    255         notifications.push_back(DirWatchNotification(pathname, type));
     250       
     251        OsPath filename;
     252        for(std::map<int, PDirWatch>::iterator it = g_paths.begin();
     253            it != g_paths.end();
     254            ++it)
     255        {
     256            if( it->first == polled_notifications[i].wd)
     257            {
     258                filename = Path(OsString(it->second->path).append(polled_notifications[i].filename));
     259                break;
     260            }
     261        }
     262        notifications.push_back(DirWatchNotification(filename, type));
    256263    }
    257264
    258265    // nothing new; try again later
    259266    return INFO::OK;
    260267}
    261268
    262 #endif  // CONFIG2_FAM