Ticket #1316: dir_watch_fam.cpp.patch
File dir_watch_fam.cpp.patch, 6.6 KB (added by , 12 years ago) |
---|
-
dir_watch_fam.cpp
28 28 #include "lib/sysdep/sysdep.h" 29 29 #include "lib/sysdep/dir_watch.h" 30 30 #include "ps/CLogger.h" 31 #include <sys/inotify.h> 31 32 32 #if !CONFIG2_FAM 33 // Stub implementations removed as inotify is part of the kernel. 33 34 34 // stub implementations35 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 #else47 48 #include <fam.h>49 50 // FAMEvent is large (~4KB), so define a smaller structure to store events51 35 struct NotificationEvent 52 36 { 53 37 std::string filename; 54 void *userdata;55 FAMCodes code;38 uint32_t code; 39 int wd; 56 40 }; 57 41 58 42 // 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. 60 44 // So we just spawn a thread to push events into this list, then swap it out 61 45 // when someone calls dir_watch_Poll. 62 46 // (We assume STL memory allocation is thread-safe.) … … 72 56 // FAM is broken or unavailable. 73 57 static int initialized = 0; 74 58 75 static FAMConnection fc; 59 // Inotify file descriptor 60 int inotifyfd; 76 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. 64 static std::vector<DirWatch> g_paths; 65 77 66 struct DirWatch 78 67 { 79 68 DirWatch() 80 69 : reqnum(-1) 81 70 { 82 71 } 83 72 84 73 ~DirWatch() 85 74 { 86 75 ENSURE(initialized > 0); 87 88 FAMRequest req;89 req.reqnum = reqnum;90 FAMCancelMonitor(&fc, &req);91 76 } 92 77 93 78 OsPath path; 94 int reqnum; 79 int reqnum; 95 80 }; 96 81 97 98 82 // for atexit 99 83 static void fam_deinit() 100 84 { 101 FAMClose(&fc);85 close(inotifyfd); 102 86 103 87 pthread_cancel(g_event_loop_thread); 104 88 // NOTE: POSIX threads are (by default) only cancellable inside particular … … 110 94 pthread_join(g_event_loop_thread, NULL); 111 95 } 112 96 97 113 98 static void fam_event_loop_process_events() 114 99 { 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) 116 119 { 117 FAMEvent e;118 if(FAMNextEvent(&fc, &e) < 0)119 {120 debug_printf(L"FAMNextEvent error");121 return;122 }123 124 120 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; 129 126 pthread_mutex_lock(&g_mutex); 130 127 g_notifications.push_back(ne); 131 128 pthread_mutex_unlock(&g_mutex); 129 130 buffer_i += event_size; 132 131 } 133 132 } 134 133 135 134 static void* fam_event_loop(void*) 136 135 { 137 int famfd = FAMCONNECTION_GETFD(&fc);138 139 136 while(true) 140 137 { 141 138 fd_set fdrset; 142 139 FD_ZERO(&fdrset); 143 FD_SET( famfd, &fdrset);140 FD_SET(inotifyfd, &fdrset); 144 141 145 142 // Block with select until there's events waiting 146 143 // (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) 148 145 { 149 146 if(errno == EINTR) 150 147 { 151 148 // interrupted - try again 152 149 FD_ZERO(&fdrset); 153 FD_SET( famfd, &fdrset);150 FD_SET(inotifyfd, &fdrset); 154 151 } 155 152 else if(errno == EBADF) 156 153 { 157 154 // 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"); 159 156 return NULL; 160 157 } 161 158 else … … 166 163 } 167 164 } 168 165 169 if(FD_ISSET( famfd, &fdrset))166 if(FD_ISSET(inotifyfd, &fdrset)) 170 167 fam_event_loop_process_events(); 171 168 } 172 169 } … … 179 176 180 177 if(!initialized) 181 178 { 182 if( FAMOpen2(&fc, "lib_res"))179 if((inotifyfd = inotify_init()) < 0) 183 180 { 181 // Check for error ? 184 182 initialized = -1; 185 LOGERROR(L"Error initializing FAM; hotloading will be disabled");186 return ERR::FAIL; // NOWARN183 LOGERROR(L"Error initializing inotify file descriptor; hotloading will be disabled"); 184 return ERR::FAIL; 187 185 } 188 186 189 187 if (pthread_create(&g_event_loop_thread, NULL, &fam_event_loop, NULL)) 190 188 { 191 189 initialized = -1; 192 LOGERROR(L"Error creating FAMevent loop thread; hotloading will be disabled");190 LOGERROR(L"Error creating inotify event loop thread; hotloading will be disabled"); 193 191 return ERR::FAIL; // NOWARN 194 192 } 195 193 … … 198 196 } 199 197 200 198 PDirWatch tmpDirWatch(new DirWatch); 199 int wd = -1; 201 200 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) 209 202 { 210 203 debug_warn(L"res_watch_dir failed!"); 211 WARN_RETURN(ERR::FAIL); 204 WARN_RETURN(ERR::FAIL); // no way of getting error code? 212 205 } 213 206 214 207 dirWatch.swap(tmpDirWatch); 215 208 dirWatch->path = path; 216 dirWatch->reqnum = req.reqnum; 217 209 dirWatch->reqnum = wd; 210 g_paths.push_back(*dirWatch); 211 218 212 return INFO::OK; 219 213 } 220 214 … … 238 232 DirWatchNotification::EType type; 239 233 switch(polled_notifications[i].code) 240 234 { 241 case FAMChanged:235 case IN_MODIFY: 242 236 type = DirWatchNotification::Changed; 243 237 break; 244 case FAMCreated:238 case IN_CREATE: 245 239 type = DirWatchNotification::Created; 246 240 break; 247 case FAMDeleted:241 case IN_DELETE: 248 242 type = DirWatchNotification::Deleted; 249 243 break; 250 244 default: 251 245 continue; 252 246 } 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)); 256 260 } 257 261 258 262 // nothing new; try again later 259 263 return INFO::OK; 260 264 } 261 265 262 #endif // CONFIG2_FAM