1 /* 2 * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 #include <stdio.h> 27 #include <stdlib.h> 28 #include <signal.h> 29 #include <pthread.h> 30 #include <sys/types.h> 31 #include <sys/socket.h> 32 #include <sys/time.h> 33 #include <sys/resource.h> 34 #include <sys/uio.h> 35 #include <unistd.h> 36 #include <errno.h> 37 38 #include <sys/poll.h> 39 40 /* 41 * Stack allocated by thread when doing blocking operation 42 */ 43 typedef struct threadEntry { 44 pthread_t thr; /* this thread */ 45 struct threadEntry *next; /* next thread */ 46 int intr; /* interrupted */ 47 } threadEntry_t; 48 49 /* 50 * Heap allocated during initialized - one entry per fd 51 */ 52 typedef struct { 53 pthread_mutex_t lock; /* fd lock */ 54 threadEntry_t *threads; /* threads blocked on fd */ 55 } fdEntry_t; 56 57 /* 58 * Signal to unblock thread 59 */ 60 static int sigWakeup = (__SIGRTMAX - 2); 61 62 /* 63 * The fd table and the number of file descriptors 64 */ 65 static fdEntry_t *fdTable; 66 static int fdCount; 67 68 /* 69 * Null signal handler 70 */ 71 static void sig_wakeup(int sig) { 72 } 73 74 /* 75 * Initialization routine (executed when library is loaded) 76 * Allocate fd tables and sets up signal handler. 77 */ 78 static void __attribute((constructor)) init() { 79 struct rlimit nbr_files; 80 sigset_t sigset; 81 struct sigaction sa; 82 83 /* 84 * Allocate table based on the maximum number of 85 * file descriptors. 86 */ 87 getrlimit(RLIMIT_NOFILE, &nbr_files); 88 fdCount = nbr_files.rlim_max; 89 fdTable = (fdEntry_t *)calloc(fdCount, sizeof(fdEntry_t)); 90 if (fdTable == NULL) { 91 fprintf(stderr, "library initialization failed - " 92 "unable to allocate file descriptor table - out of memory"); 93 abort(); 94 } 95 96 /* 97 * Setup the signal handler 98 */ 99 sa.sa_handler = sig_wakeup; 100 sa.sa_flags = 0; 101 sigemptyset(&sa.sa_mask); 102 sigaction(sigWakeup, &sa, NULL); 103 104 sigemptyset(&sigset); 105 sigaddset(&sigset, sigWakeup); 106 sigprocmask(SIG_UNBLOCK, &sigset, NULL); 107 } 108 109 /* 110 * Return the fd table for this fd or NULL is fd out 111 * of range. 112 */ 113 static inline fdEntry_t *getFdEntry(int fd) 114 { 115 if (fd < 0 || fd >= fdCount) { 116 return NULL; 117 } 118 return &fdTable[fd]; 119 } 120 121 /* 122 * Start a blocking operation :- 123 * Insert thread onto thread list for the fd. 124 */ 125 static inline void startOp(fdEntry_t *fdEntry, threadEntry_t *self) 126 { 127 self->thr = pthread_self(); 128 self->intr = 0; 129 130 pthread_mutex_lock(&(fdEntry->lock)); 131 { 132 self->next = fdEntry->threads; 133 fdEntry->threads = self; 134 } 135 pthread_mutex_unlock(&(fdEntry->lock)); 136 } 137 138 /* 139 * End a blocking operation :- 140 * Remove thread from thread list for the fd 141 * If fd has been interrupted then set errno to EBADF 142 */ 143 static inline void endOp 144 (fdEntry_t *fdEntry, threadEntry_t *self) 145 { 146 int orig_errno = errno; 147 pthread_mutex_lock(&(fdEntry->lock)); 148 { 149 threadEntry_t *curr, *prev=NULL; 150 curr = fdEntry->threads; 151 while (curr != NULL) { 152 if (curr == self) { 153 if (curr->intr) { 154 orig_errno = EBADF; 155 } 156 if (prev == NULL) { 157 fdEntry->threads = curr->next; 158 } else { 159 prev->next = curr->next; 160 } 161 break; 162 } 163 prev = curr; 164 curr = curr->next; 165 } 166 } 167 pthread_mutex_unlock(&(fdEntry->lock)); 168 errno = orig_errno; 169 } 170 171 /* 172 * Close or dup2 a file descriptor ensuring that all threads blocked on 173 * the file descriptor are notified via a wakeup signal. 174 * 175 * fd1 < 0 => close(fd2) 176 * fd1 >= 0 => dup2(fd1, fd2) 177 * 178 * Returns -1 with errno set if operation fails. 179 */ 180 static int closefd(int fd1, int fd2) { 181 int rv, orig_errno; 182 fdEntry_t *fdEntry = getFdEntry(fd2); 183 if (fdEntry == NULL) { 184 errno = EBADF; 185 return -1; 186 } 187 188 /* 189 * Lock the fd to hold-off additional I/O on this fd. 190 */ 191 pthread_mutex_lock(&(fdEntry->lock)); 192 193 { 194 /* 195 * And close/dup the file descriptor 196 * (restart if interrupted by signal) 197 */ 198 do { 199 if (fd1 < 0) { 200 rv = close(fd2); 201 } else { 202 rv = dup2(fd1, fd2); 203 } 204 } while (rv == -1 && errno == EINTR); 205 206 /* 207 * Send a wakeup signal to all threads blocked on this 208 * file descriptor. 209 */ 210 threadEntry_t *curr = fdEntry->threads; 211 while (curr != NULL) { 212 curr->intr = 1; 213 pthread_kill( curr->thr, sigWakeup ); 214 curr = curr->next; 215 } 216 } 217 218 /* 219 * Unlock without destroying errno 220 */ 221 orig_errno = errno; 222 pthread_mutex_unlock(&(fdEntry->lock)); 223 errno = orig_errno; 224 225 return rv; 226 } 227 228 /* 229 * Wrapper for dup2 - same semantics as dup2 system call except 230 * that any threads blocked in an I/O system call on fd2 will be 231 * preempted and return -1/EBADF; 232 */ 233 int NET_Dup2(int fd, int fd2) { 234 if (fd < 0) { 235 errno = EBADF; 236 return -1; 237 } 238 return closefd(fd, fd2); 239 } 240 241 /* 242 * Wrapper for close - same semantics as close system call 243 * except that any threads blocked in an I/O on fd will be 244 * preempted and the I/O system call will return -1/EBADF. 245 */ 246 int NET_SocketClose(int fd) { 247 return closefd(-1, fd); 248 } 249 250 /************** Basic I/O operations here ***************/ 251 252 /* 253 * Macro to perform a blocking IO operation. Restarts 254 * automatically if interrupted by signal (other than 255 * our wakeup signal) 256 */ 257 #define BLOCKING_IO_RETURN_INT(FD, FUNC) { \ 258 int ret; \ 259 threadEntry_t self; \ 260 fdEntry_t *fdEntry = getFdEntry(FD); \ 261 if (fdEntry == NULL) { \ 262 errno = EBADF; \ 263 return -1; \ 264 } \ 265 do { \ 266 startOp(fdEntry, &self); \ 267 ret = FUNC; \ 268 endOp(fdEntry, &self); \ 269 } while (ret == -1 && errno == EINTR); \ 270 return ret; \ 271 } 272 273 int NET_Read(int s, void* buf, size_t len) { 274 BLOCKING_IO_RETURN_INT( s, recv(s, buf, len, 0) ); 275 } 276 277 int NET_ReadV(int s, const struct iovec * vector, int count) { 278 BLOCKING_IO_RETURN_INT( s, readv(s, vector, count) ); 279 } 280 281 int NET_RecvFrom(int s, void *buf, int len, unsigned int flags, 282 struct sockaddr *from, socklen_t *fromlen) { 283 BLOCKING_IO_RETURN_INT( s, recvfrom(s, buf, len, flags, from, fromlen) ); 284 } 285 286 int NET_Send(int s, void *msg, int len, unsigned int flags) { 287 BLOCKING_IO_RETURN_INT( s, send(s, msg, len, flags) ); 288 } 289 290 int NET_WriteV(int s, const struct iovec * vector, int count) { 291 BLOCKING_IO_RETURN_INT( s, writev(s, vector, count) ); 292 } 293 294 int NET_SendTo(int s, const void *msg, int len, unsigned int 295 flags, const struct sockaddr *to, int tolen) { 296 BLOCKING_IO_RETURN_INT( s, sendto(s, msg, len, flags, to, tolen) ); 297 } 298 299 int NET_Accept(int s, struct sockaddr *addr, socklen_t *addrlen) { 300 BLOCKING_IO_RETURN_INT( s, accept(s, addr, addrlen) ); 301 } 302 303 int NET_Connect(int s, struct sockaddr *addr, int addrlen) { 304 BLOCKING_IO_RETURN_INT( s, connect(s, addr, addrlen) ); 305 } 306 307 #ifndef USE_SELECT 308 int NET_Poll(struct pollfd *ufds, unsigned int nfds, int timeout) { 309 BLOCKING_IO_RETURN_INT( ufds[0].fd, poll(ufds, nfds, timeout) ); 310 } 311 #else 312 int NET_Select(int s, fd_set *readfds, fd_set *writefds, 313 fd_set *exceptfds, struct timeval *timeout) { 314 BLOCKING_IO_RETURN_INT( s-1, 315 select(s, readfds, writefds, exceptfds, timeout) ); 316 } 317 #endif 318 319 /* 320 * Wrapper for poll(s, timeout). 321 * Auto restarts with adjusted timeout if interrupted by 322 * signal other than our wakeup signal. 323 */ 324 int NET_Timeout(int s, long timeout) { 325 long prevtime = 0, newtime; 326 struct timeval t; 327 fdEntry_t *fdEntry = getFdEntry(s); 328 329 /* 330 * Check that fd hasn't been closed. 331 */ 332 if (fdEntry == NULL) { 333 errno = EBADF; 334 return -1; 335 } 336 337 /* 338 * Pick up current time as may need to adjust timeout 339 */ 340 if (timeout > 0) { 341 gettimeofday(&t, NULL); 342 prevtime = t.tv_sec * 1000 + t.tv_usec / 1000; 343 } 344 345 for(;;) { 346 struct pollfd pfd; 347 int rv; 348 threadEntry_t self; 349 350 /* 351 * Poll the fd. If interrupted by our wakeup signal 352 * errno will be set to EBADF. 353 */ 354 pfd.fd = s; 355 pfd.events = POLLIN | POLLERR; 356 357 startOp(fdEntry, &self); 358 rv = poll(&pfd, 1, timeout); 359 endOp(fdEntry, &self); 360 361 /* 362 * If interrupted then adjust timeout. If timeout 363 * has expired return 0 (indicating timeout expired). 364 */ 365 if (rv < 0 && errno == EINTR) { 366 if (timeout > 0) { 367 gettimeofday(&t, NULL); 368 newtime = t.tv_sec * 1000 + t.tv_usec / 1000; 369 timeout -= newtime - prevtime; 370 if (timeout <= 0) { 371 return 0; 372 } 373 prevtime = newtime; 374 } 375 } else { 376 return rv; 377 } 378 379 } 380 } --- EOF ---