Ticket #1316: inotify2.patch
File inotify2.patch, 10.6 KB (added by , 12 years ago) |
---|
-
build/premake/premake4.lua
5 5 newoption { trigger = "gles", description = "Use non-working OpenGL ES 2.0 mode" } 6 6 newoption { trigger = "icc", description = "Use Intel C++ Compiler (Linux only; should use either \"--cc icc\" or --without-pch too, and then set CXX=icpc before calling make)" } 7 7 newoption { trigger = "outpath", description = "Location for generated project files" } 8 newoption { trigger = "without-fam", description = "Disable use of FAM API on Linux" }9 8 newoption { trigger = "without-audio", description = "Disable use of OpenAL/Ogg/Vorbis APIs" } 10 9 newoption { trigger = "minimal-flags", description = "Only set compiler/linker flags that are really needed. Has no effect on Windows builds" } 11 10 newoption { trigger = "without-nvtt", description = "Disable use of NVTT" } … … 156 155 defines { "CONFIG2_GLES=1" } 157 156 end 158 157 159 if _OPTIONS["without-fam"] then160 defines { "CONFIG2_FAM=0" }161 end162 163 158 if _OPTIONS["without-audio"] then 164 159 defines { "CONFIG2_AUDIO=0" } 165 160 end … … 780 775 781 776 elseif os.is("linux") or os.is("bsd") then 782 777 783 if not _OPTIONS["without-fam"] then784 links { "fam" }785 end786 787 778 if not _OPTIONS["android"] and not (os.getversion().description == "OpenBSD") then 788 779 links { "rt" } 789 780 end … … 1205 1196 1206 1197 elseif os.is("linux") or os.is("bsd") then 1207 1198 1208 if not _OPTIONS["without-fam"] then1209 links { "fam" }1210 end1211 1212 1199 if not _OPTIONS["android"] and not (os.getversion().description == "OpenBSD") then 1213 1200 links { "rt" } 1214 1201 end -
source/lib/sysdep/os/linux/dir_watch_fam.cpp
22 22 23 23 #include "precompiled.h" 24 24 25 #include <string> 26 27 #include "lib/config2.h" 25 #include "lib/sysdep/dir_watch.h" 28 26 #include "lib/sysdep/sysdep.h" 29 #include "lib/sysdep/dir_watch.h"30 27 #include "ps/CLogger.h" 31 28 32 #if !CONFIG2_FAM 29 #include <map> 30 #include <string> 31 #include <sys/inotify.h> 33 32 34 // stub implementations35 33 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 34 struct NotificationEvent 52 35 { 53 36 std::string filename; 54 void *userdata;55 FAMCodes code;37 uint32_t code; 38 int wd; 56 39 }; 57 40 58 41 // To avoid deadlocks and slow synchronous reads, it's necessary to use a 59 // separate thread for reading events from FAM.42 // separate thread for reading events from inotify. 60 43 // So we just spawn a thread to push events into this list, then swap it out 61 44 // when someone calls dir_watch_Poll. 62 45 // (We assume STL memory allocation is thread-safe.) … … 69 52 70 53 // trool; -1 = init failed and all operations will be aborted silently. 71 54 // this is so that each dir_* call doesn't complain if the system's 72 // FAMis broken or unavailable.55 // inotify is broken or unavailable. 73 56 static int initialized = 0; 74 57 75 static FAMConnection fc; 58 // Inotify file descriptor 59 static int inotifyfd; 60 61 // With inotify, using a map seems to be a good alternative to FAM's userdata 62 typedef std::map<int, PDirWatch> DirWatchMap; 63 static DirWatchMap g_paths; 76 64 77 65 struct DirWatch 78 66 { … … 84 72 ~DirWatch() 85 73 { 86 74 ENSURE(initialized > 0); 87 88 FAMRequest req; 89 req.reqnum = reqnum; 90 FAMCancelMonitor(&fc, &req); 75 inotify_rm_watch(inotifyfd, reqnum); 91 76 } 92 77 93 78 OsPath path; 94 79 int reqnum; 95 80 }; 96 81 97 98 82 // for atexit 99 static void fam_deinit()83 static void inotify_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 105 89 // functions (like 'select'), so this should safely terminate while it's 106 // in select/ FAMNextEvent/etc (and won't e.g. cancel while it's holding the90 // in select/etc (and won't e.g. cancel while it's holding the 107 91 // mutex) 108 92 109 93 // Wait for the thread to finish 110 94 pthread_join(g_event_loop_thread, NULL); 111 95 } 112 96 113 static void fam_event_loop_process_events()97 static void inotify_event_loop_process_events() 114 98 { 115 while(FAMPending(&fc) > 0) 99 // Buffer for reading the events. 100 // Need to be careful about overflow here. 101 char buffer[65535]; 102 103 // Event iterator 104 ssize_t buffer_i = 0; 105 106 // Total size of all the events 107 ssize_t r; 108 109 // Size & struct for the current event 110 size_t event_size; 111 struct inotify_event *pevent; 112 113 r = read(inotifyfd, buffer, 65535); 114 if(r <= 0) 115 return; 116 117 while(buffer_i < r) 116 118 { 117 FAMEvent e;118 if(FAMNextEvent(&fc, &e) < 0)119 {120 debug_printf(L"FAMNextEvent error");121 return;122 }123 124 119 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); 120 pevent = (struct inotify_event *) &buffer[buffer_i]; 121 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; 126 127 pthread_mutex_lock(&g_mutex); 128 g_notifications.push_back(ne); 129 pthread_mutex_unlock(&g_mutex); 130 131 buffer_i += event_size; 132 132 } 133 133 } 134 134 135 static void* fam_event_loop(void*)135 static void* inotify_event_loop(void*) 136 136 { 137 int famfd = FAMCONNECTION_GETFD(&fc);138 139 137 while(true) 140 138 { 141 139 fd_set fdrset; 142 140 FD_ZERO(&fdrset); 143 FD_SET( famfd, &fdrset);144 141 FD_SET(inotifyfd, &fdrset); 142 errno = 0; 145 143 // Block with select until there's events waiting 146 // (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 // probably just lost the connection to FAM- kill the thread158 debug_printf(L" lost connection to FAM");154 // probably just lost the connection to inotify - kill the thread 155 debug_printf(L"inotify_event_loop: Invalid file descriptor inotifyfd=%d\n", inotifyfd); 159 156 return NULL; 160 157 } 161 158 else 162 159 { 163 160 // oops 164 debug_printf(L" select error %d", errno);161 debug_printf(L"inotify_event_loop: select error errno=%d\n", errno); 165 162 return NULL; 166 163 } 164 errno = 0; 167 165 } 168 169 if(FD_ISSET(famfd, &fdrset)) 170 fam_event_loop_process_events(); 166 if(FD_ISSET(inotifyfd, &fdrset)) 167 inotify_event_loop_process_events(); 171 168 } 172 169 } 173 170 174 171 Status dir_watch_Add(const OsPath& path, PDirWatch& dirWatch) 175 172 { 176 // init already failed; don't try again or complain 173 char resolved[PATH_MAX + 1]; 174 175 // init already failed; don't try again or complain 177 176 if(initialized == -1) 178 177 return ERR::FAIL; // NOWARN 179 178 180 179 if(!initialized) 181 180 { 182 if(FAMOpen2(&fc, "lib_res")) 181 errno = 0; 182 if((inotifyfd = inotify_init()) < 0) 183 183 { 184 // Check for error ? 185 int err = errno; 184 186 initialized = -1; 185 LOGERROR(L"Error initializing FAM; hotloading will be disabled"); 186 return ERR::FAIL; // NOWARN 187 LOGERROR(L"Error initializing inotify file descriptor; hotloading will be disabled, errno=%d", err); 188 errno = err; 189 return StatusFromErrno(); // NOWARN 187 190 } 188 191 189 if (pthread_create(&g_event_loop_thread, NULL, &fam_event_loop, NULL)) 192 errno = 0; 193 int ret = pthread_create(&g_event_loop_thread, NULL, &inotify_event_loop, NULL); 194 if (ret != 0) 190 195 { 191 196 initialized = -1; 192 LOGERROR(L"Error creating FAM event loop thread; hotloading will be disabled"); 193 return ERR::FAIL; // NOWARN 197 LOGERROR(L"Error creating inotify event loop thread; hotloading will be disabled, err=%d", ret); 198 errno = ret; 199 return StatusFromErrno(); // NOWARN 194 200 } 195 201 196 202 initialized = 1; 197 atexit( fam_deinit);203 atexit(inotify_deinit); 198 204 } 199 205 200 206 PDirWatch tmpDirWatch(new DirWatch); 201 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) 209 { 210 debug_warn(L"res_watch_dir failed!"); 211 WARN_RETURN(ERR::FAIL); // no way of getting error code? 212 } 213 207 errno = 0; 208 int wd = inotify_add_watch(inotifyfd, realpath(OsString(path).c_str(), resolved), IN_CREATE | IN_DELETE | IN_CLOSE_WRITE); 209 if (wd < 0) 210 WARN_RETURN(StatusFromErrno()); 211 214 212 dirWatch.swap(tmpDirWatch); 215 213 dirWatch->path = path; 216 dirWatch->reqnum = req.reqnum; 217 214 dirWatch->reqnum = wd; 215 g_paths.insert(std::make_pair(wd, dirWatch)); 216 218 217 return INFO::OK; 219 218 } 220 219 221 222 223 220 Status dir_watch_Poll(DirWatchNotifications& notifications) 224 221 { 225 222 if(initialized == -1) … … 236 233 for(size_t i = 0; i < polled_notifications.size(); ++i) 237 234 { 238 235 DirWatchNotification::EType type; 236 // TODO: code is actually a bitmask, so this is slightly incorrect 239 237 switch(polled_notifications[i].code) 240 238 { 241 case FAMChanged:239 case IN_CLOSE_WRITE: 242 240 type = DirWatchNotification::Changed; 243 241 break; 244 case FAMCreated:242 case IN_CREATE: 245 243 type = DirWatchNotification::Created; 246 244 break; 247 case FAMDeleted:245 case IN_DELETE: 248 246 type = DirWatchNotification::Deleted; 249 247 break; 250 248 default: 251 249 continue; 252 250 } 253 DirWatch* dirWatch = (DirWatch*)polled_notifications[i].userdata; 254 OsPath pathname = dirWatch->path / polled_notifications[i].filename; 255 notifications.push_back(DirWatchNotification(pathname, type)); 251 252 DirWatchMap::iterator it = g_paths.find(polled_notifications[i].wd); 253 if(it != g_paths.end()) 254 { 255 OsPath filename = Path(OsString(it->second->path).append(polled_notifications[i].filename)); 256 notifications.push_back(DirWatchNotification(filename, type)); 257 } 258 else 259 { 260 debug_printf(L"dir_watch_Poll: Notification with invalid watch descriptor wd=%d\n", polled_notifications[i].wd); 261 } 256 262 } 257 263 258 264 // nothing new; try again later 259 265 return INFO::OK; 260 266 } 261 267 262 #endif // CONFIG2_FAM -
source/lib/config2.h
101 101 # define CONFIG2_GLES 0 102 102 #endif 103 103 104 // allow use of FAM API on Linux105 #ifndef CONFIG2_FAM106 # define CONFIG2_FAM 0107 #endif108 109 104 // allow use of OpenAL/Ogg/Vorbis APIs 110 105 #ifndef CONFIG2_AUDIO 111 106 # define CONFIG2_AUDIO 1