📄 jthread.c
字号:
/* take off condvar queue */ tid = *cv; *cv = tid->nextQ; /* put on lock queue */ tid->nextQ = lock->waiting; lock->waiting = tid; } intsRestore();}voidjcondvar_broadcast(jcondvar *cv, jmutex *lock){ intsDisable(); if (*cv != NULL) { /* splice the lists `*cv' and `lock->waiting' */ jthread** tidp; /* advance to last element in cv list */ for (tidp = cv; *tidp != 0; tidp = &(*tidp)->nextQ) ; (*tidp) = lock->waiting; lock->waiting = *cv; *cv = NULL; } intsRestore();}voidjcondvar_destroy(jcondvar* cv){ assert(*cv == NULL);}/*============================================================================ * * I/O routines that are exported to the user * *//* * Create a threaded file descriptor. * * We try various fcntl and ioctl to put the file descriptor in non-blocking * mode and to enable asynchronous notifications. */static intjthreadedFileDescriptor(int fd){ int r;#if (defined(FIOSSAIOSTAT) && !(defined(hpux) && defined(FIOASYNC))) || \ (defined(FIOASYNC) && !defined(linux)) int on = 1;#endif#if (defined(FIOSSAIOOWN) && !(defined(hpux) && defined(F_SETOWN))) || \ defined(F_SETOWN) /* cache pid to accommodate antique C libraries */ static int pid = -1; if (pid == -1) pid = getpid();#endif if (fd == -1) return (fd);#if defined(F_SETFD) /* set close-on-exec flag for this file descriptor */ if ((r = fcntl(fd, F_SETFD, 1)) < 0) { perror("F_SETFD"); return (r); }#endif /* Make non-blocking */ if ((r = fcntl(fd, F_GETFL, 0)) < 0) { perror("F_GETFL"); return (r); } /* * Apparently, this can fail, for instance when we stdout is * redirected to /dev/null. (On FreeBSD) */ fcntl(fd, F_SETFL, r | O_NONBLOCK #if defined(O_ASYNC) | O_ASYNC#elif defined(FASYNC) | FASYNC#endif );#if defined(FIOSSAIOSTAT) && !(defined(hpux) && defined(FIOASYNC)) r = ioctl(fd, FIOSSAIOSTAT, &on); if (r < 0 && errno != ENOTTY) { /* Defines ENOTTY to be an acceptable error */ perror("FIOSSAIOSTAT"); return (r); }#elif defined(FIOASYNC) && !defined(linux) /* Don't do this on Linux because Linux version < 2.2.5 doesn't * know what FIOASYNC means. It thinks FIOASYNC == O_SYNC. I kid you * not. You can imagine what that means. ;-) * Never mind, FASYNC work as expected and is already set :) */ /* * This ioctl fails for so many systems on so many occasions. * Reasons include ENXIO, ENOTTY, EINVAL(?) */ r = ioctl(fd, FIOASYNC, &on); if (r < 0) { DBG(JTHREAD, perror("FIOASYNC"); ) }#endif#if !(defined(O_ASYNC) || defined(FIOASYNC) || \ defined(FASYNC) || defined(FIOSSAIOSTAT))#error Could not put socket in async mode#endif /* Allow socket to signal this process when new data is available */#if defined(FIOSSAIOOWN) && !(defined(hpux) && defined(F_SETOWN)) r = ioctl(fd, FIOSSAIOOWN, &pid); if (r == -1 && errno != ENOTTY) { perror("Error doing FIOSSAIOWN"); } #elif defined(F_SETOWN) /* On some systems, this will flag an error if fd is not a socket */ r = fcntl(fd, F_SETOWN, pid); if (r < 0) { DBG(JTHREAD, perror("F_SETOWN"); ) }#endif return (fd);}/* * clear non-blocking flag for a file descriptor */voidjthreadRestoreFD(int fd){ /* clear nonblocking flag */ fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) & ~O_NONBLOCK);}/* * In SVR4 systems (notably AIX and HPUX 9.x), putting a file descriptor * in non-blocking mode affects the actual terminal file. * Thus, the shell we see the fd in * non-blocking mode when we exit and log the user off. * * Under Solaris, this happens if you use FIONBIO to get into non-blocking * mode. (as opposed to O_NONBLOCK) */static voidrestore_fds(void){ int i; /* clear non-blocking flag for file descriptor stdin, stdout, stderr */ for (i = 0; i < 3; i++) { jthreadRestoreFD(i); }}static voidrestore_fds_and_exit(){ restore_fds(); /* technically, we should restore the original handler and rethrow * the signal. */ EXIT(-1); /* XXX */}/* * Threaded socket create. */intjthreadedSocket(int af, int type, int proto, int *out){ int r; intsDisable(); r = socket(af, type, proto); if (r == -1) { r = errno; } else { *out = jthreadedFileDescriptor(r); r = 0; } intsRestore(); return (r);}/* * Threaded file open. */intjthreadedOpen(const char* path, int flags, int mode, int *out){ int r; intsDisable(); /* Cygnus WinNT requires this */ r = open(path, flags|O_BINARY, mode); if (r == -1) { r = errno; } else { *out = jthreadedFileDescriptor(r); r = 0; } intsRestore(); return (r);}/* * various building blocks for timeout system call functions */#define SET_DEADLINE(deadline, timeout) \ if (timeout != NOTIMEOUT) { \ jlong ct = currentTime(); \ deadline = timeout + ct; \ if( deadline < ct ) { \ deadline = 0; \ timeout = NOTIMEOUT; \ } \ }#define BREAK_IF_LATE(deadline, timeout) \ if (timeout != NOTIMEOUT) { \ if (currentTime() >= deadline) { \ errno = EINTR; \ break; \ } \ }#define IGNORE_EINTR(r) \ if (r == -1 && errno == EINTR) { \ continue; \ }#define SET_RETURN(r) \ if (r == -1) { \ r = errno; \ } #define SET_RETURN_OUT(r, out, ret) \ if (r == -1) { \ r = errno; \ } else { \ *out = ret; \ r = 0; \ }#define CALL_BLOCK_ON_FILE(A, B, C) \ if (blockOnFile(A, B, C)) { \ /* interrupted via jthread_interrupt() */ \ errno = EINTR; \ break; \ }/* * Threaded socket connect. */intjthreadedConnect(int fd, struct sockaddr* addr, int len, int timeout){ int r; jlong deadline = 0; int inProgress = 0; intsDisable(); SET_DEADLINE(deadline, timeout) for (;;) { r = connect(fd, addr, len); if (r == 0 || !(errno == EINPROGRESS || errno == EINTR || errno == EISCONN)) { break; /* success or real error */ } if (r == -1 && errno == EISCONN) { /* On Solaris 2.5, after getting EINPROGRESS from a non-blocking connect request, we won't ever get success. When we're waken up, we'll either get EISCONN, which should be taken as success, or a real failure. However, we can't map EISCONN to success inconditionally, because attempting to connect the same socket again should raise an exception. Mapping EISCONN to success might lead to false positives if connect fails and another thread succeeds to connect this socket before this one is waken up. Let's just hope it doesn't happen for now. */ if (inProgress) { r = 0; } break; } else if (r == -1 && errno == EINPROGRESS) { inProgress = 1; } IGNORE_EINTR(r) CALL_BLOCK_ON_FILE(fd, TH_CONNECT, timeout) BREAK_IF_LATE(deadline, timeout) } SET_RETURN(r) intsRestore(); return (r);}/* * Threaded socket accept. */intjthreadedAccept(int fd, struct sockaddr* addr, int* len, int timeout, int* out){ /* absolute time at which time out is reached */ jlong deadline = 0; int r; intsDisable(); SET_DEADLINE(deadline, timeout) for (;;) { r = accept(fd, addr, len); if (r >= 0 || !(errno == EWOULDBLOCK || errno == EINTR || errno == EAGAIN)) { break; /* success or real error */ } IGNORE_EINTR(r) CALL_BLOCK_ON_FILE(fd, TH_ACCEPT, timeout) BREAK_IF_LATE(deadline, timeout) } SET_RETURN_OUT(r, out, jthreadedFileDescriptor(r)) intsRestore(); return (r);}/* * Threaded read with timeout */intjthreadedTimedRead(int fd, void* buf, size_t len, int timeout, ssize_t *out){ ssize_t r = -1; /* absolute time at which timeout is reached */ jlong deadline = 0; assert(timeout >= 0); intsDisable(); SET_DEADLINE(deadline, timeout) for (;;) { r = read(fd, buf, len); if (r >= 0 || !(errno == EWOULDBLOCK || errno == EINTR || errno == EAGAIN)) { break; /* real error or success */ } IGNORE_EINTR(r) CALL_BLOCK_ON_FILE(fd, TH_READ, timeout) BREAK_IF_LATE(deadline, timeout) } SET_RETURN_OUT(r, out, r) intsRestore(); return (r);}/* * Threaded read with no time out */intjthreadedRead(int fd, void* buf, size_t len, ssize_t *out){ return (jthreadedTimedRead(fd, buf, len, NOTIMEOUT, out));}/* * Threaded write */intjthreadedWrite(int fd, const void* buf, size_t len, ssize_t *out){ ssize_t r = 1; const void* ptr; ptr = buf; intsDisable(); while (len > 0 && r > 0) { r = (ssize_t)write(fd, ptr, len); if (r >= 0) { ptr += r; len -= r; r = ptr - buf; continue; } if (errno == EINTR) { /* ignore */ r = 1; continue; } if (!(errno == EWOULDBLOCK || errno == EAGAIN)) { /* real error */ break; } /* must be EWOULDBLOCK or EAGAIN */ if (blockOnFile(fd, TH_WRITE, NOTIMEOUT)) { /* interrupted by jthread_interrupt() */ errno = EINTR; *out = ptr - buf; break; } r = 1; } SET_RETURN_OUT(r, out, r) intsRestore(); return (r); }/* * Threaded recvfrom */int jthreadedRecvfrom(int fd, void* buf, size_t len, int flags, struct sockaddr* from, int* fromlen, int timeout, ssize_t *out){ int r; jlong deadline = 0; SET_DEADLINE(deadline, timeout) intsDisable(); for (;;) { r = recvfrom(fd, buf, len, flags, from, fromlen); if (r >= 0 || !(errno == EWOULDBLOCK || errno == EINTR || errno == EAGAIN)) { break; } IGNORE_EINTR(r) /* else EWOULDBLOCK or EAGAIN */ CALL_BLOCK_ON_FILE(fd, TH_READ, timeout) BREAK_IF_LATE(deadline, timeout) } SET_RETURN_OUT(r, out, r) intsRestore(); return (r);}/*============================================================================ * * Routines dealing with Process::waitFor. * *//* * Child process has died. */staticvoidchildDeath(void){ if (waitForList) { resumeThread(waitForList); }}/* * Wait for a child process. */intjthreadedWaitpid(int wpid, int* status, int options, int *outpid){#if defined(HAVE_WAITPID) int npid; int ret = 0;DBG(JTHREAD, dprintf("waitpid %d current=%p\n", wpid, currentJThread); ) intsDisable(); for (;;) { int procStatus; wouldlosewakeup = 1; npid = waitpid(wpid, &procStatus, options|WNOHANG); if (npid > 0) { *outpid = npid; if (WIFEXITED(procStatus)) *status = WEXITSTATUS(procStatus); else if (WIFSIGNALED(procStatus)) *status=128+WTERMSIG(procStatus); else *status = -1; /* shouldn't happen */ break; } if ((npid == -1) && (errno == ECHILD)) { /* child does not exist */ ret = -1; break; } BLOCKED_ON_EXTERNAL(currentJThread); suspendOnQThread(currentJThread, &waitForList, NOTIMEOUT); } intsRestore(); return (ret);#else return (EOPNOTSUPPORT);#endif}/* helper function for forkexec, close fd[0..n-1] */static voidclose_fds(int fd[], int n){ int i = 0; while (i < n) close(fd[i++]);}int jthreadedForkExec(char **argv, char **arge, int ioes[4], int *outpid, const char *dir){/* these defines are indices in ioes */#define IN_IN 0#define IN_OUT 1#define OUT_IN 2#define OUT_OUT 3#define ERR_IN 4#define ERR_OUT 5#define SYNC_IN 6#define SYNC_OUT 7 int fds[8]; int nfd; /* number of fds in `fds' that are valid */ sigset_t nsig; char b[1]; int pid, i, err; /* * we need execve() and fork() for this to work. Don't bother if * we don't have them. */#if !defined(HAVE_EXECVE) && !defined(HAVE_EXECVP) unimp("neither execve() nor execvp() not provided");#endif#if !defined(HAVE_FORK) unimp("fork() not provided");#endifDBG(JTHREAD, { char **d = argv; dprintf("argv = [`%s ", *d++); while (*d) dprintf(", `%s'", *d++); dprintf("]\n"); } ) /* Create the pipes to communicate with the child */ /* Make sure fds get closed if we can't create all pipes */ for (nfd = 0; nfd < 8; nfd += 2) { int e; err = pipe(fds + nfd); e = errno; if (err == -1) { close_fds(fds, nfd); return (e); } } /* * We must avoid that the child dies because of SIGVTALRM or * other signals. We disable interrupts before forking and then * reenable signals in the child after we cleaned up. */ sigfillset(&nsig); sigprocmask(SIG_BLOCK, &nsig, 0); pid = fork(); switch (pid) { case 0: /* Child */ /* turn timers off */ deactivate_time_slicing(); MALARM(0); /* set all signals back to their default state */ for (i = 0; i < NSIG; i++) { clearSignal(i); } /* now reenable interrupts */ sigprocmask(SIG_UNBLOCK, &nsig, 0); /* set stdin, stdout, and stderr up from the pipes */ dup2(fds[IN_IN], 0); dup2(fds[OUT_OUT], 1); dup2(fds[ERR_OUT], 2); /* What is sync about anyhow? Well my current guess is that * the parent writes a single byte to it when it's ready to * proceed. So here I wait until I get it before doing * anything. */ /* note that this is a blocking read */ read(fds[SYNC_IN], b, sizeof(b)); /* now close all pipe fds */ close_fds(fds, 8); /* change working directory */#if defined(HAVE_CHDIR) (void)chdir(dir);#endif /* * If no environment was given and we have execvp, we use it. * If an environment was given, we use execve. * This is roughly was the linux jdk seems to do. */ /* execute program */#if defined(HAVE_EXECVP) if (arge == NULL) execvp(argv[0], argv); else#endif execve(argv[0], argv, arge); break; case -1: /* Error */ err = errno; /* Close all pipe fds */ close_fds(fds, 8); sigprocmask(SIG_UNBLOCK, &nsig, 0); return (err); default: /* Parent */ /* close the fds we won't need */ close(fds[IN_IN]); close(fds[OUT_OUT]); close(fds[ERR_OUT]); close(fds[SYNC_IN]); /* copy and fix up the fds we do need */ ioes[0] = jthreadedFileDescriptor(fds[IN_OUT]); ioes[1] = jthreadedFileDescriptor(fds[OUT_IN]); ioes[2] = jthreadedFileDescriptor(fds[ERR_IN]); ioes[3] = jthreadedFileDescriptor(fds[SYNC_OUT]); sigprocmask(SIG_UNBLOCK, &nsig, 0); *outpid = pid; return (0); } exit(-1); /* NEVER REACHED */ }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -