Lines 42-51
Link Here
|
42 |
import com.sun.jna.Library; |
42 |
import com.sun.jna.Library; |
43 |
import com.sun.jna.Native; |
43 |
import com.sun.jna.Native; |
44 |
import com.sun.jna.NativeLibrary; |
44 |
import com.sun.jna.NativeLibrary; |
|
|
45 |
import com.sun.jna.NativeLong; |
46 |
import com.sun.jna.Structure; |
45 |
import java.io.IOException; |
47 |
import java.io.IOException; |
46 |
import java.nio.ByteBuffer; |
48 |
import java.nio.ByteBuffer; |
47 |
import java.nio.ByteOrder; |
49 |
import java.nio.ByteOrder; |
|
|
50 |
import java.util.ArrayList; |
51 |
import java.util.Arrays; |
48 |
import java.util.HashMap; |
52 |
import java.util.HashMap; |
|
|
53 |
import java.util.List; |
49 |
import java.util.Map; |
54 |
import java.util.Map; |
50 |
import java.util.logging.Level; |
55 |
import java.util.logging.Level; |
51 |
import java.util.logging.Logger; |
56 |
import java.util.logging.Logger; |
Lines 57-70
Link Here
|
57 |
*/ |
62 |
*/ |
58 |
final class LinuxNotifier extends Notifier<LinuxNotifier.LKey> { |
63 |
final class LinuxNotifier extends Notifier<LinuxNotifier.LKey> { |
59 |
private static final Logger LOG = Logger.getLogger(LinuxNotifier.class.getName()); |
64 |
private static final Logger LOG = Logger.getLogger(LinuxNotifier.class.getName()); |
|
|
65 |
|
66 |
public static class PollFd extends Structure { |
67 |
public int fd; |
68 |
public short events; |
69 |
public short revents; |
70 |
|
71 |
public static final short POLLIN = 0x01; // something to read |
72 |
|
73 |
public PollFd(int fd) { |
74 |
this.fd = fd; |
75 |
events = POLLIN; |
76 |
} |
77 |
|
78 |
public boolean hasData() { |
79 |
return (revents & POLLIN) != 0; |
80 |
} |
81 |
|
82 |
@Override |
83 |
public String toString() { |
84 |
return "PollFd[" + fd + "]"; |
85 |
} |
86 |
|
87 |
} |
88 |
public static class TimeVal extends Structure { |
89 |
public NativeLong seconds; |
90 |
public NativeLong nano; |
91 |
} |
60 |
|
92 |
|
61 |
private static interface InotifyImpl extends Library { |
93 |
private static interface InotifyImpl extends Library { |
62 |
public int inotify_init(); |
94 |
public int inotify_init(); |
63 |
public int inotify_init1(int flags); |
95 |
public int inotify_init1(int flags); |
64 |
public int close(int fd); |
96 |
public int close(int fd); |
65 |
public int read(int fd, ByteBuffer buff, int count); |
97 |
public int poll(PollFd[] fds, NativeLong numberOfFDs, int timeout); |
66 |
public int inotify_add_watch(int fd, String pathname, int mask); |
98 |
public int read(int fd, ByteBuffer buff, int count); |
67 |
public int inotify_rm_watch(int fd, int wd); |
99 |
public int inotify_add_watch(int fd, String pathname, int mask); |
|
|
100 |
public int inotify_rm_watch(int fd, int wd); |
101 |
|
68 |
|
102 |
|
69 |
public static final int O_CLOEXEC = 02000000; //0x80000 |
103 |
public static final int O_CLOEXEC = 02000000; //0x80000 |
70 |
|
104 |
|
Lines 90-96
Link Here
|
90 |
} |
124 |
} |
91 |
|
125 |
|
92 |
final InotifyImpl IMPL; |
126 |
final InotifyImpl IMPL; |
93 |
int fd; |
127 |
private PollFd[] polls; |
94 |
private ByteBuffer buff = ByteBuffer.allocateDirect(4096); |
128 |
private ByteBuffer buff = ByteBuffer.allocateDirect(4096); |
95 |
|
129 |
|
96 |
// An array would serve nearly as well |
130 |
// An array would serve nearly as well |
Lines 100-117
Link Here
|
100 |
IMPL = (InotifyImpl) Native.loadLibrary("c", InotifyImpl.class); |
134 |
IMPL = (InotifyImpl) Native.loadLibrary("c", InotifyImpl.class); |
101 |
buff.position(buff.capacity()); // make the buffer empty |
135 |
buff.position(buff.capacity()); // make the buffer empty |
102 |
buff.order(ByteOrder.nativeOrder()); |
136 |
buff.order(ByteOrder.nativeOrder()); |
103 |
fd = IMPL.inotify_init1(InotifyImpl.O_CLOEXEC); |
137 |
polls = new PollFd[] { new PollFd(allocFD()) }; |
104 |
if (fd < 0) { |
138 |
} |
105 |
LOG.log( |
139 |
|
106 |
Level.INFO, "Linux kernel {0} returned {1} from inotify_init1", |
140 |
private int allocFD() throws IllegalStateException { |
107 |
new Object[] { System.getProperty("os.version"), fd } |
141 |
int fd = IMPL.inotify_init1(InotifyImpl.O_CLOEXEC); |
108 |
); |
142 |
if (fd < 0) { |
109 |
fd = IMPL.inotify_init(); |
143 |
LOG.log( |
110 |
LOG.log(Level.INFO, "Trying inotify_init: {0}", fd); |
144 |
Level.INFO, "Linux kernel {0} returned {1} from inotify_init1", |
111 |
} |
145 |
new Object[] { System.getProperty("os.version"), fd } |
112 |
if (fd < 0) { |
146 |
); |
113 |
throw new IllegalStateException("inotify_init failed: " + fd); |
147 |
fd = IMPL.inotify_init(); |
114 |
} |
148 |
LOG.log(Level.INFO, "Trying inotify_init: {0}", fd); |
|
|
149 |
} |
150 |
if (fd < 0) { |
151 |
throw new IllegalStateException("inotify_init failed: " + fd); |
152 |
} |
153 |
return fd; |
115 |
} |
154 |
} |
116 |
|
155 |
|
117 |
private String getString(int maxLen) { |
156 |
private String getString(int maxLen) { |
Lines 133-140
Link Here
|
133 |
*/ |
172 |
*/ |
134 |
while (buff.remaining() < 16 || buff.remaining() < 16 + buff.getInt(buff.position() + 12)) { |
173 |
while (buff.remaining() < 16 || buff.remaining() < 16 + buff.getInt(buff.position() + 12)) { |
135 |
buff.compact(); |
174 |
buff.compact(); |
136 |
int len = IMPL.read(fd, buff, buff.remaining()); |
175 |
LOG.log(Level.FINEST, "before select from {0}", polls.length); |
137 |
|
176 |
IMPL.poll(polls, new NativeLong(1), -1); |
|
|
177 |
LOG.log(Level.FINEST, "before select from {0}", polls.length); |
178 |
int len = -1; |
179 |
for (PollFd fd : polls) { |
180 |
LOG.log(Level.FINEST, "{0} has data {1}", new Object[]{fd, fd.hasData()}); |
181 |
if (fd.hasData()) { |
182 |
len = IMPL.read(fd.fd, buff, buff.remaining()); |
183 |
break; |
184 |
} |
185 |
} |
138 |
if (len <= 0) { |
186 |
if (len <= 0) { |
139 |
// lazily get a thread local errno |
187 |
// lazily get a thread local errno |
140 |
int errno = NativeLibrary.getInstance("c").getFunction("errno").getInt(0); |
188 |
int errno = NativeLibrary.getInstance("c").getFunction("errno").getInt(0); |
Lines 166-177
Link Here
|
166 |
|
214 |
|
167 |
|
215 |
|
168 |
static class LKey { |
216 |
static class LKey { |
169 |
int id; |
217 |
final int id; |
170 |
String path; |
218 |
final String path; |
|
|
219 |
final PollFd from; |
171 |
|
220 |
|
172 |
public LKey(int id, String path) { |
221 |
public LKey(int id, String path, PollFd from) { |
173 |
this.id = id; |
222 |
this.id = id; |
174 |
this.path = path; |
223 |
this.path = path; |
|
|
224 |
this.from = from; |
175 |
} |
225 |
} |
176 |
|
226 |
|
177 |
@Override |
227 |
@Override |
Lines 181-202
Link Here
|
181 |
} |
231 |
} |
182 |
|
232 |
|
183 |
@Override public LKey addWatch(String path) throws IOException { |
233 |
@Override public LKey addWatch(String path) throws IOException { |
184 |
// what if the file doesn't exist? |
234 |
int id = -1; |
185 |
int id = IMPL.inotify_add_watch(fd, path, |
235 |
PollFd from = null; |
186 |
InotifyImpl.IN_CREATE | InotifyImpl.IN_MOVED_TO | |
236 |
List<PollFd> arr = Arrays.asList(polls); |
187 |
InotifyImpl.IN_DELETE | InotifyImpl.IN_MOVED_FROM | |
237 |
OK: for (int twice = 0; twice < 2; twice++) { |
188 |
InotifyImpl.IN_MODIFY | InotifyImpl.IN_ATTRIB); |
238 |
// what if the file doesn't exist? |
189 |
//XXX handle error return value (-1) |
239 |
for (PollFd fd : polls) { |
190 |
LOG.log(Level.FINEST, "addWatch{0} res: {1}", new Object[]{path, id}); |
240 |
id = IMPL.inotify_add_watch(fd.fd, path, |
191 |
if (id <= 0) { |
241 |
InotifyImpl.IN_CREATE | InotifyImpl.IN_MOVED_TO | |
192 |
// 28 == EINOSPC |
242 |
InotifyImpl.IN_DELETE | InotifyImpl.IN_MOVED_FROM | |
193 |
int errno = NativeLibrary.getInstance("c").getFunction("errno").getInt(0); // NOI18N |
243 |
InotifyImpl.IN_MODIFY | InotifyImpl.IN_ATTRIB); |
194 |
throw new IOException("addWatch on " + path + " errno: " + errno); // NOI18N |
244 |
//XXX handle error return value (-1) |
|
|
245 |
if (id <= 0) { |
246 |
int errno = NativeLibrary.getInstance("c").getFunction("errno").getInt(0); // NOI18N |
247 |
LOG.log(Level.FINE, "addWatch error {3}/{1} at {0} on {2}", new Object[]{path, id, fd, errno}); |
248 |
if (errno == 28) { |
249 |
// 28 == EINOSPC |
250 |
continue; |
251 |
} |
252 |
throw new IOException("addWatch on " + path + " errno: " + errno); // NOI18N |
253 |
} |
254 |
LOG.log(Level.FINEST, "addWatch{0} res: {1} on {2}", new Object[]{path, id, fd}); |
255 |
from = fd; |
256 |
break OK; |
257 |
} |
258 |
LOG.log(Level.WARNING, "Expanding list of iNotify instances from {0}", polls.length); |
259 |
synchronized (this) { |
260 |
arr = new ArrayList<PollFd>(arr); |
261 |
final PollFd pollFd = new PollFd(allocFD()); |
262 |
arr.add(pollFd); |
263 |
polls = arr.toArray(new PollFd[0]); |
264 |
LOG.log(Level.FINE, "Current polls: {0}", arr); |
265 |
} |
195 |
} |
266 |
} |
196 |
|
267 |
|
197 |
LKey newKey = map.get(id); |
268 |
LKey newKey = map.get(id); |
198 |
if (newKey == null) { |
269 |
if (newKey == null) { |
199 |
newKey = new LKey(id, path); |
270 |
newKey = new LKey(id, path, from); |
200 |
map.put(id, newKey); |
271 |
map.put(id, newKey); |
201 |
} |
272 |
} |
202 |
return newKey; |
273 |
return newKey; |
Lines 204-209
Link Here
|
204 |
|
275 |
|
205 |
@Override public void removeWatch(LKey lkey) { |
276 |
@Override public void removeWatch(LKey lkey) { |
206 |
map.remove(lkey.id); |
277 |
map.remove(lkey.id); |
207 |
IMPL.inotify_rm_watch(fd, lkey.id); |
278 |
IMPL.inotify_rm_watch(lkey.from.fd, lkey.id); |
208 |
} |
279 |
} |
209 |
} |
280 |
} |