📄 pth_high.c
字号:
/*** GNU Pth - The GNU Portable Threads** Copyright (c) 1999-2004 Ralf S. Engelschall <rse@engelschall.com>**** This file is part of GNU Pth, a non-preemptive thread scheduling** library which can be found at http://www.gnu.org/software/pth/.**** This library is free software; you can redistribute it and/or** modify it under the terms of the GNU Lesser General Public** License as published by the Free Software Foundation; either** version 2.1 of the License, or (at your option) any later version.**** This library is distributed in the hope that it will be useful,** but WITHOUT ANY WARRANTY; without even the implied warranty of** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU** Lesser General Public License for more details.**** You should have received a copy of the GNU Lesser General Public** License along with this library; if not, write to the Free Software** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307** USA, or contact Ralf S. Engelschall <rse@engelschall.com>.**** pth_high.c: Pth high-level replacement functions*/ /* ``The difference between a computer industry job and open-source software hacking is about 30 hours a week.'' -- Ralf S. Engelschall *//* * These functions used by the applications instead of the * regular Unix/POSIX functions. When the regular functions would * block, these variants let only the thread sleep. */#include "pth_p.h"/* Pth variant of nanosleep(2) */int pth_nanosleep(const struct timespec *rqtp, struct timespec *rmtp){ pth_time_t until; pth_time_t offset; pth_time_t now; pth_event_t ev; static pth_key_t ev_key = PTH_KEY_INIT; /* consistency checks for POSIX conformance */ if (rqtp == NULL) return pth_error(-1, EFAULT); if (rqtp->tv_nsec < 0 || rqtp->tv_nsec > (1000*1000000)) return pth_error(-1, EINVAL); /* short-circuit */ if (rqtp->tv_sec == 0 && rqtp->tv_nsec == 0) return 0; /* calculate asleep time */ offset = pth_time((long)(rqtp->tv_sec), (long)(rqtp->tv_nsec) / 1000); pth_time_set(&until, PTH_TIME_NOW); pth_time_add(&until, &offset); /* and let thread sleep until this time is elapsed */ if ((ev = pth_event(PTH_EVENT_TIME|PTH_MODE_STATIC, &ev_key, until)) == NULL) return pth_error(-1, errno); pth_wait(ev); /* optionally provide amount of slept time */ if (rmtp != NULL) { pth_time_set(&now, PTH_TIME_NOW); pth_time_sub(&until, &now); rmtp->tv_sec = until.tv_sec; rmtp->tv_nsec = until.tv_usec * 1000; } return 0;}/* Pth variant of usleep(3) */int pth_usleep(unsigned int usec){ pth_time_t until; pth_time_t offset; pth_event_t ev; static pth_key_t ev_key = PTH_KEY_INIT; /* short-circuit */ if (usec == 0) return 0; /* calculate asleep time */ offset = pth_time((long)(usec / 1000000), (long)(usec % 1000000)); pth_time_set(&until, PTH_TIME_NOW); pth_time_add(&until, &offset); /* and let thread sleep until this time is elapsed */ if ((ev = pth_event(PTH_EVENT_TIME|PTH_MODE_STATIC, &ev_key, until)) == NULL) return pth_error(-1, errno); pth_wait(ev); return 0;}/* Pth variant of sleep(3) */unsigned int pth_sleep(unsigned int sec){ pth_time_t until; pth_time_t offset; pth_event_t ev; static pth_key_t ev_key = PTH_KEY_INIT; /* consistency check */ if (sec == 0) return 0; /* calculate asleep time */ offset = pth_time(sec, 0); pth_time_set(&until, PTH_TIME_NOW); pth_time_add(&until, &offset); /* and let thread sleep until this time is elapsed */ if ((ev = pth_event(PTH_EVENT_TIME|PTH_MODE_STATIC, &ev_key, until)) == NULL) return sec; pth_wait(ev); return 0;}/* Pth variant of POSIX pthread_sigmask(3) */int pth_sigmask(int how, const sigset_t *set, sigset_t *oset){ int rv; /* change the explicitly remembered signal mask copy for the scheduler */ if (set != NULL) pth_sc(sigprocmask)(how, &(pth_current->mctx.sigs), NULL); /* change the real (per-thread saved/restored) signal mask */ rv = pth_sc(sigprocmask)(how, set, oset); return rv;}/* Pth variant of POSIX sigwait(3) */int pth_sigwait(const sigset_t *set, int *sigp){ return pth_sigwait_ev(set, sigp, NULL);}/* Pth variant of POSIX sigwait(3) with extra events */int pth_sigwait_ev(const sigset_t *set, int *sigp, pth_event_t ev_extra){ pth_event_t ev; static pth_key_t ev_key = PTH_KEY_INIT; sigset_t pending; int sig; if (set == NULL || sigp == NULL) return pth_error(EINVAL, EINVAL); /* check whether signal is already pending */ if (sigpending(&pending) < 0) sigemptyset(&pending); for (sig = 1; sig < PTH_NSIG; sig++) { if (sigismember(set, sig) && sigismember(&pending, sig)) { pth_util_sigdelete(sig); *sigp = sig; return 0; } } /* create event and wait on it */ if ((ev = pth_event(PTH_EVENT_SIGS|PTH_MODE_STATIC, &ev_key, set, sigp)) == NULL) return pth_error(errno, errno); if (ev_extra != NULL) pth_event_concat(ev, ev_extra, NULL); pth_wait(ev); if (ev_extra != NULL) { pth_event_isolate(ev); if (pth_event_status(ev) != PTH_STATUS_OCCURRED) return pth_error(EINTR, EINTR); } /* nothing to do, scheduler has already set *sigp for us */ return 0;}/* Pth variant of waitpid(2) */pid_t pth_waitpid(pid_t wpid, int *status, int options){ pth_event_t ev; static pth_key_t ev_key = PTH_KEY_INIT; pid_t pid; pth_debug2("pth_waitpid: called from thread \"%s\"", pth_current->name); for (;;) { /* do a non-blocking poll for the pid */ while ( (pid = pth_sc(waitpid)(wpid, status, options|WNOHANG)) < 0 && errno == EINTR) ; /* if pid was found or caller requested a polling return immediately */ if (pid == -1 || pid > 0 || (pid == 0 && (options & WNOHANG))) break; /* else wait a little bit */ ev = pth_event(PTH_EVENT_TIME|PTH_MODE_STATIC, &ev_key, pth_timeout(0,250000)); pth_wait(ev); } pth_debug2("pth_waitpid: leave to thread \"%s\"", pth_current->name); return pid;}/* Pth variant of system(3) */int pth_system(const char *cmd){ struct sigaction sa_ign, sa_int, sa_quit; sigset_t ss_block, ss_old; struct stat sb; pid_t pid; int pstat; /* POSIX calling convention: determine whether the Bourne Shell ("sh") is available on this platform */ if (cmd == NULL) { if (stat(PTH_PATH_BINSH, &sb) == -1) return 0; return 1; } /* temporarily ignore SIGINT and SIGQUIT actions */ sa_ign.sa_handler = SIG_IGN; sigemptyset(&sa_ign.sa_mask); sa_ign.sa_flags = 0; sigaction(SIGINT, &sa_ign, &sa_int); sigaction(SIGQUIT, &sa_ign, &sa_quit); /* block SIGCHLD signal */ sigemptyset(&ss_block); sigaddset(&ss_block, SIGCHLD); pth_sc(sigprocmask)(SIG_BLOCK, &ss_block, &ss_old); /* fork the current process */ pstat = -1; switch (pid = pth_fork()) { case -1: /* error */ break; case 0: /* child */ /* restore original signal dispositions and execute the command */ sigaction(SIGINT, &sa_int, NULL); sigaction(SIGQUIT, &sa_quit, NULL); pth_sc(sigprocmask)(SIG_SETMASK, &ss_old, NULL); /* stop the Pth scheduling */ pth_scheduler_kill(); /* execute the command through Bourne Shell */ execl(PTH_PATH_BINSH, "sh", "-c", cmd, (char *)NULL); /* POSIX compliant return in case execution failed */ exit(127); default: /* parent */ /* wait until child process terminates */ pid = pth_waitpid(pid, &pstat, 0); break; } /* restore original signal dispositions and execute the command */ sigaction(SIGINT, &sa_int, NULL); sigaction(SIGQUIT, &sa_quit, NULL); pth_sc(sigprocmask)(SIG_SETMASK, &ss_old, NULL); /* return error or child process result code */ return (pid == -1 ? -1 : pstat);}/* Pth variant of select(2) */int pth_select(int nfds, fd_set *rfds, fd_set *wfds, fd_set *efds, struct timeval *timeout){ return pth_select_ev(nfds, rfds, wfds, efds, timeout, NULL);}/* Pth variant of select(2) with extra events */int pth_select_ev(int nfd, fd_set *rfds, fd_set *wfds, fd_set *efds, struct timeval *timeout, pth_event_t ev_extra){ struct timeval delay; pth_event_t ev; pth_event_t ev_select; pth_event_t ev_timeout; static pth_key_t ev_key_select = PTH_KEY_INIT; static pth_key_t ev_key_timeout = PTH_KEY_INIT; fd_set rspare, wspare, espare; fd_set *rtmp, *wtmp, *etmp; int selected; int rc; pth_implicit_init(); pth_debug2("pth_select_ev: called from thread \"%s\"", pth_current->name); /* POSIX.1-2001/SUSv3 compliance */ if (nfd < 0 || nfd > FD_SETSIZE) return pth_error(-1, EINVAL); if (timeout != NULL) { if ( timeout->tv_sec < 0 || timeout->tv_usec < 0 || timeout->tv_usec >= 1000000 /* a full second */) return pth_error(-1, EINVAL); if (timeout->tv_sec > 31*24*60*60) timeout->tv_sec = 31*24*60*60; } /* first deal with the special situation of a plain microsecond delay */ if (nfd == 0 && rfds == NULL && wfds == NULL && efds == NULL && timeout != NULL) { if (timeout->tv_sec == 0 && timeout->tv_usec <= 10000 /* 1/100 second */) { /* very small delays are acceptable to be performed directly */ while ( pth_sc(select)(0, NULL, NULL, NULL, timeout) < 0 && errno == EINTR) ; } else { /* larger delays have to go through the scheduler */ ev = pth_event(PTH_EVENT_TIME|PTH_MODE_STATIC, &ev_key_timeout, pth_timeout(timeout->tv_sec, timeout->tv_usec)); if (ev_extra != NULL) pth_event_concat(ev, ev_extra, NULL); pth_wait(ev); if (ev_extra != NULL) { pth_event_isolate(ev); if (pth_event_status(ev) != PTH_STATUS_OCCURRED) return pth_error(-1, EINTR); } } /* POSIX.1-2001/SUSv3 compliance */ if (rfds != NULL) FD_ZERO(rfds); if (wfds != NULL) FD_ZERO(wfds); if (efds != NULL) FD_ZERO(efds); return 0; } /* now directly poll filedescriptor sets to avoid unnecessary (and resource consuming because of context switches, etc) event handling through the scheduler. We've to be carefully here, because not all platforms guaranty us that the sets are unmodified if an error or timeout occurred. */ delay.tv_sec = 0; delay.tv_usec = 0; rtmp = NULL; if (rfds != NULL) { memcpy(&rspare, rfds, sizeof(fd_set)); rtmp = &rspare; } wtmp = NULL; if (wfds != NULL) { memcpy(&wspare, wfds, sizeof(fd_set)); wtmp = &wspare; } etmp = NULL; if (efds != NULL) { memcpy(&espare, efds, sizeof(fd_set)); etmp = &espare; } while ((rc = pth_sc(select)(nfd, rtmp, wtmp, etmp, &delay)) < 0 && errno == EINTR) ; if (rc < 0) /* pass-through immediate error */ return pth_error(-1, errno); else if ( rc > 0 || ( rc == 0 && timeout != NULL && pth_time_cmp(timeout, PTH_TIME_ZERO) == 0)) { /* pass-through immediate success */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -