1 |
/* gnu_java_nio_VMSelector.c - Native methods for SelectorImpl class |
/* gnu_java_nio_VMSelector.c - Native methods for SelectorImpl class |
2 |
Copyright (C) 2004 Free Software Foundation, Inc. |
Copyright (C) 2004, 2005 Free Software Foundation, Inc. |
3 |
|
|
4 |
This file is part of GNU Classpath. |
This file is part of GNU Classpath. |
5 |
|
|
35 |
obligated to do so. If you do not wish to do so, delete this |
obligated to do so. If you do not wish to do so, delete this |
36 |
exception statement from your version. */ |
exception statement from your version. */ |
37 |
|
|
38 |
|
#include <sys/select.h> |
39 |
|
#include <sys/time.h> |
40 |
|
|
41 |
|
#include <string.h> |
42 |
|
|
43 |
#include <config.h> |
#include <config.h> |
44 |
#include <errno.h> |
#include <errno.h> |
45 |
|
|
48 |
|
|
49 |
#include "gnu_java_nio_VMSelector.h" |
#include "gnu_java_nio_VMSelector.h" |
50 |
|
|
51 |
#define IO_EXCEPTION "java/io/IOException" |
/* Amount of characters in the error message buffer for strerror_r. */ |
52 |
|
#define BUF_SIZE 250 |
53 |
|
|
54 |
|
void |
55 |
|
helper_put_filedescriptors(JNIEnv *, jintArray, fd_set *, int *); |
56 |
|
|
57 |
|
void |
58 |
|
helper_get_filedescriptors (JNIEnv *, jintArray*, fd_set *); |
59 |
|
|
60 |
|
void |
61 |
|
helper_reset (JNIEnv *, jintArray*); |
62 |
|
|
63 |
|
int |
64 |
|
helper_select (JNIEnv *, jclass, jmethodID, |
65 |
|
int, fd_set *, fd_set *, fd_set *, |
66 |
|
struct timeval *); |
67 |
|
|
68 |
|
void |
69 |
|
helper_put_filedescriptors(JNIEnv *env, jintArray fdArray, fd_set *fds, int *max_fd) |
70 |
|
{ |
71 |
|
jint *tmpFDArray = (*env)->GetIntArrayElements(env, fdArray, 0); |
72 |
|
int size = (*env)->GetArrayLength(env, fdArray); |
73 |
|
int index, fd; |
74 |
|
|
75 |
|
for( index = 0; index < size; index++) |
76 |
|
{ |
77 |
|
fd = tmpFDArray [index]; |
78 |
|
|
79 |
|
if (fd > 0) |
80 |
|
{ |
81 |
|
FD_SET (tmpFDArray [index], fds); |
82 |
|
|
83 |
|
if (tmpFDArray [index] > (*max_fd)) |
84 |
|
(*max_fd) = tmpFDArray [index]; |
85 |
|
} |
86 |
|
} |
87 |
|
} |
88 |
|
|
89 |
|
void |
90 |
|
helper_get_filedescriptors (JNIEnv *env, jintArray* fdArray, fd_set *fds) |
91 |
|
{ |
92 |
|
jint *tmpFDArray = (*env)->GetIntArrayElements(env, fdArray, 0); |
93 |
|
int size = (*env)->GetArrayLength(env, fdArray); |
94 |
|
int index, fd; |
95 |
|
|
96 |
|
for (index = 0; index < size; index++) |
97 |
|
{ |
98 |
|
fd = tmpFDArray [index]; |
99 |
|
if (fd < 0 || !FD_ISSET (fd, fds)) |
100 |
|
tmpFDArray [index] = 0; |
101 |
|
} |
102 |
|
} |
103 |
|
|
104 |
|
void |
105 |
|
helper_reset (JNIEnv *env, jintArray* fdArray) |
106 |
|
{ |
107 |
|
jint* tmpFDArray = (*env)->GetIntArrayElements(env, fdArray, 0); |
108 |
|
int size = (*env)->GetArrayLength(env, fdArray); |
109 |
|
int index; |
110 |
|
|
111 |
|
for (index = 0; index < size; index++) |
112 |
|
tmpFDArray [index] = 0; |
113 |
|
} |
114 |
|
|
115 |
|
/* A wrapper for select() which ignores EINTR. |
116 |
|
* Taken from gclib's posix.cc |
117 |
|
*/ |
118 |
|
int |
119 |
|
helper_select (JNIEnv *env, jclass thread_class, jmethodID thread_interrupted, |
120 |
|
int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, |
121 |
|
struct timeval *timeout) |
122 |
|
{ |
123 |
|
#ifdef HAVE_SYS_SELECT_H |
124 |
|
/* If we have a timeout, compute the absolute ending time. */ |
125 |
|
struct timeval end, delay, after; |
126 |
|
int r; |
127 |
|
|
128 |
|
if (timeout) |
129 |
|
{ |
130 |
|
gettimeofday (&end, NULL); |
131 |
|
|
132 |
|
end.tv_usec += timeout->tv_usec; |
133 |
|
|
134 |
|
if (end.tv_usec >= 1000000) |
135 |
|
{ |
136 |
|
++end.tv_sec; |
137 |
|
end.tv_usec -= 1000000; |
138 |
|
} |
139 |
|
|
140 |
|
end.tv_sec += timeout->tv_sec; |
141 |
|
delay = *timeout; |
142 |
|
} |
143 |
|
else |
144 |
|
{ |
145 |
|
/* Placate compiler. */ |
146 |
|
delay.tv_sec = delay.tv_usec = 0; |
147 |
|
} |
148 |
|
|
149 |
|
while (1) |
150 |
|
{ |
151 |
|
r = select (n, readfds, writefds, exceptfds, |
152 |
|
timeout ? &delay : NULL); |
153 |
|
|
154 |
|
if (r != -1 || errno != EINTR) |
155 |
|
return r; |
156 |
|
|
157 |
|
/* Here we know we got EINTR. */ |
158 |
|
if ( (*env)->CallStaticBooleanMethod(env, thread_class, thread_interrupted) ) |
159 |
|
{ |
160 |
|
return EINTR; |
161 |
|
} |
162 |
|
|
163 |
|
if (timeout) |
164 |
|
{ |
165 |
|
gettimeofday (&after, NULL); |
166 |
|
|
167 |
|
/* Now compute new timeout argument. */ |
168 |
|
delay.tv_usec = end.tv_usec - after.tv_usec; |
169 |
|
delay.tv_sec = end.tv_sec - after.tv_sec; |
170 |
|
|
171 |
|
if (delay.tv_usec < 0) |
172 |
|
{ |
173 |
|
--delay.tv_sec; |
174 |
|
delay.tv_usec += 1000000; |
175 |
|
} |
176 |
|
|
177 |
|
if (delay.tv_sec < 0) |
178 |
|
{ |
179 |
|
/* We assume that the user wants a valid select() call |
180 |
|
* more than precise timing. So if we get a series of |
181 |
|
* EINTR we just keep trying with delay 0 until we get a |
182 |
|
* valid result. |
183 |
|
*/ |
184 |
|
delay.tv_sec = 0; |
185 |
|
} |
186 |
|
} |
187 |
|
} |
188 |
|
#else /* HAVE_SYS_SELECT_H */ |
189 |
|
return 0; |
190 |
|
#endif |
191 |
|
|
192 |
|
} |
193 |
|
|
194 |
JNIEXPORT jint JNICALL |
JNIEXPORT jint JNICALL |
195 |
Java_gnu_java_nio_VMSelector_select (JNIEnv *env, |
Java_gnu_java_nio_VMSelector_select (JNIEnv *env, |
196 |
jclass obj __attribute__ ((__unused__)), |
jclass obj __attribute__ ((__unused__)), |
197 |
jintArray read |
jintArray read, |
198 |
__attribute__ ((__unused__)), |
jintArray write, |
199 |
jintArray write |
jintArray except, |
200 |
__attribute__ ((__unused__)), |
jlong timeout) |
|
jintArray except |
|
|
__attribute__ ((__unused__)), |
|
|
jlong timeout |
|
|
__attribute__ ((__unused__))) |
|
201 |
{ |
{ |
202 |
JCL_ThrowException (env, IO_EXCEPTION, "gnu.java.nio.VMSelector.select(): not implemented"); |
jint result; |
203 |
return 0; |
jclass thread_class = (*env)->FindClass(env, "java/lang/Thread"); |
204 |
|
jmethodID thread_current_thread = (*env)->GetStaticMethodID(env, thread_class, "currentThread", "()Ljava/lang/Thread;"); |
205 |
|
jmethodID thread_interrupt = (*env)->GetMethodID(env, thread_class, "interrupt", "()V"); |
206 |
|
jmethodID thread_interrupted = (*env)->GetMethodID(env, thread_class, "interrupted", "()Z"); |
207 |
|
jobject current_thread; |
208 |
|
int max_fd = 0; |
209 |
|
fd_set read_fds; |
210 |
|
fd_set write_fds; |
211 |
|
fd_set except_fds; |
212 |
|
struct timeval real_time_data; |
213 |
|
struct timeval *time_data = NULL; |
214 |
|
char message_buf[BUF_SIZE+1]; |
215 |
|
|
216 |
|
/* If a legal timeout value isn't given, use NULL. |
217 |
|
* This means an infinite timeout. The specification |
218 |
|
* also says that a zero timeout should be treated |
219 |
|
* as infinite. Otherwise (if the timeout value is legal), |
220 |
|
* fill our timeval struct and use it for the select. |
221 |
|
*/ |
222 |
|
if (timeout > 0) |
223 |
|
{ |
224 |
|
real_time_data.tv_sec = timeout / 1000; |
225 |
|
real_time_data.tv_usec = (timeout % 1000) * 1000; |
226 |
|
time_data = &real_time_data; |
227 |
|
} |
228 |
|
|
229 |
|
/* Reset all fd_set structures */ |
230 |
|
FD_ZERO (&read_fds); |
231 |
|
FD_ZERO (&write_fds); |
232 |
|
FD_ZERO (&except_fds); |
233 |
|
|
234 |
|
/* Fill the fd_set data structures for the _Jv_select() call. */ |
235 |
|
helper_put_filedescriptors (env, read, &read_fds, &max_fd); |
236 |
|
helper_put_filedescriptors (env, write, &write_fds, &max_fd); |
237 |
|
helper_put_filedescriptors (env, except, &except_fds, &max_fd); |
238 |
|
|
239 |
|
/* Actually do the select */ |
240 |
|
result = helper_select (env, thread_class, thread_interrupted, max_fd + 1, &read_fds, &write_fds, |
241 |
|
&except_fds, time_data); |
242 |
|
|
243 |
|
if( result == EINTR ) { |
244 |
|
/* The behavior of JRE 1.4.1 is that no exception is thrown |
245 |
|
* when the thread is interrupted, but the thread's interrupt |
246 |
|
* status is set. Clear all of our select sets and return 0, |
247 |
|
* indicating that nothing was selected. |
248 |
|
*/ |
249 |
|
current_thread = (*env)->CallStaticObjectMethod(env, thread_class, thread_current_thread); |
250 |
|
(*env)->CallVoidMethod(env, current_thread, thread_interrupt); |
251 |
|
|
252 |
|
helper_reset (env, read); |
253 |
|
helper_reset (env, write); |
254 |
|
helper_reset (env, except); |
255 |
|
|
256 |
|
return 0; |
257 |
|
} |
258 |
|
|
259 |
|
if (result < 0) |
260 |
|
{ |
261 |
|
if( strerror_r(errno, message_buf, BUF_SIZE) ) |
262 |
|
{ |
263 |
|
/* This would mean that message_buf was to small |
264 |
|
* to hold the error message. |
265 |
|
*/ |
266 |
|
JCL_ThrowException(env, "java/lang/InternalError", |
267 |
|
"Not enough space in message buffer."); |
268 |
|
return 0; |
269 |
|
} |
270 |
|
|
271 |
|
JCL_ThrowException (env, "java/io/IOException", message_buf); |
272 |
|
return 0; |
273 |
|
} |
274 |
|
|
275 |
|
/* Set the file descriptors according to the values returned from select(). */ |
276 |
|
helper_get_filedescriptors (env, read, &read_fds); |
277 |
|
helper_get_filedescriptors (env, write, &write_fds); |
278 |
|
helper_get_filedescriptors (env, except, &except_fds); |
279 |
|
|
280 |
|
return result; |
281 |
} |
} |
282 |
|
|