head 1.111; access; symbols PTH_2_0_7:1.110 PTH_2_0_6:1.109 PTH_2_0_5:1.109 PTH_2_0_4:1.109 PTH_2_0_3:1.108 PTH_2_0_2:1.108 PTH_2_0_1:1.106 PTH_2_0_0:1.105 PTH_2_0b2:1.104 PTH_2_0b1:1.104 PTH_2_0b0:1.99 PTH_1_4:1.83.0.2 PTH_1_4_1:1.83 PTH_1_4_0:1.76 PTH_1_3_7:1.71.2.1 PTH_1_4a3:1.73 PTH_1_3_6:1.71.2.1 PTH_1_4a2:1.73 PTH_1_3_5:1.71 PTH_1_4a1:1.72 PTH_1_3_4:1.71 PTH_1_3:1.71.0.2 PTH_1_3_3:1.71 PTH_1_3_2:1.70 PTH_1_3_1:1.70 PTH_1_3_0:1.69 PTH_1_3b3:1.69 PTH_1_2_3:1.62.2.2 PTH_1_3b2:1.67 PTH_1_3b1:1.67 PTH_1_3a5:1.66 PTH_1_3a4:1.66 PTH_1_3a3:1.66 PTH_1_2_2:1.62.2.2 PTH_1_3a2:1.66 PTH_1_2_1:1.62.2.2 PTH_1_3a1:1.63 PTH_1_2:1.62.0.2 PTH_1_2_0:1.62 PTH_1_2b8:1.61 PTH_1_2b7:1.61 PTH_1_1_6:1.55 PTH_1_2b6:1.59 PTH_1_2b5:1.59 PTH_1_2b4:1.59 PTH_1_2b3:1.59 PTH_1_2b2:1.57 PTH_1_2b1:1.57 PTH_1_1_5:1.55 PTH_1_0_6:1.40.2.2 PTH_1_0_5:1.40.2.1 PTH_1_0:1.40.0.2 PTH_1_1:1.55.0.2 PTH_1_1_4:1.55 PTH_1_1_3:1.55 PTH_1_1_2:1.55 PTH_1_1_1:1.55 PTH_1_1_0:1.54 PTH_1_1b7:1.53 PTH_1_1b6:1.53 PTH_1_1b5:1.51 PTH_1_1b4:1.50 PTH_1_1b3:1.47 PTH_1_1b2:1.45 PTH_1_1b1:1.45 PTH_1_0_4:1.40 PTH_1_0_3:1.37 PTH_1_0_2:1.37 PTH_1_0_1:1.37 PTH_1_0_0:1.35 PTH_1_0b8:1.34 PTH_1_0b7:1.31 PTH_1_0b6:1.31 PTH_1_0b5:1.31 PTH_1_0b4:1.25 PTH_1_0b3:1.22 PTH_1_0b2:1.22 PTH_1_0b1:1.21 PTH_0_9_21:1.20 PTH_0_9_20:1.19 PTH_0_9_19:1.19 PTH_0_9_18:1.19 PTH_0_9_17:1.19 PTH_0_9_16:1.19 PTH_0_9_15:1.18 PTH_0_9_14:1.16 PTH_0_9_13:1.15 PTH_0_9_12:1.13 PTH_0_9_11:1.9 PTH_0_9_10:1.8 PTH_0_9_9:1.8 PTH_0_9_8:1.8 PTH_0_9_7:1.4 PTH_0_9_6:1.4 PTH_0_9_5:1.3 PTH_0_9_4:1.2 PTH_0_9_3:1.2; locks; strict; comment @ * @; 1.111 date 2007.01.01.18.23.53; author rse; state Exp; branches; next 1.110; commitid 9DhdiirNzQPBIP0s; 1.110 date 2006.06.08.17.54.53; author rse; state Exp; branches; next 1.109; commitid x8N3mLVdQgkbdeAr; 1.109 date 2004.12.31.19.34.45; author rse; state Exp; branches; next 1.108; 1.108 date 2004.09.12.12.18.23; author rse; state Exp; branches; next 1.107; 1.107 date 2004.09.12.11.52.33; author rse; state Exp; branches; next 1.106; 1.106 date 2004.07.13.10.50.49; author rse; state Exp; branches; next 1.105; 1.105 date 2003.01.01.15.49.11; author rse; state Exp; branches; next 1.104; 1.104 date 2002.11.08.20.26.02; author rse; state Exp; branches; next 1.103; 1.103 date 2002.11.08.19.12.13; author rse; state Exp; branches; next 1.102; 1.102 date 2002.11.08.16.20.15; author rse; state Exp; branches; next 1.101; 1.101 date 2002.11.08.15.35.30; author rse; state Exp; branches; next 1.100; 1.100 date 2002.11.08.12.38.39; author rse; state Exp; branches; next 1.99; 1.99 date 2002.11.07.15.21.06; author rse; state Exp; branches; next 1.98; 1.98 date 2002.11.07.15.07.54; author rse; state Exp; branches; next 1.97; 1.97 date 2002.11.07.13.27.56; author rse; state Exp; branches; next 1.96; 1.96 date 2002.11.05.19.47.07; author rse; state Exp; branches; next 1.95; 1.95 date 2002.11.05.19.39.09; author rse; state Exp; branches; next 1.94; 1.94 date 2002.10.25.11.59.18; author rse; state Exp; branches; next 1.93; 1.93 date 2002.10.25.11.56.16; author rse; state Exp; branches; next 1.92; 1.92 date 2002.10.25.11.53.28; author rse; state Exp; branches; next 1.91; 1.91 date 2002.10.24.15.21.13; author rse; state Exp; branches; next 1.90; 1.90 date 2002.10.24.09.07.51; author rse; state Exp; branches; next 1.89; 1.89 date 2002.10.23.14.04.00; author rse; state Exp; branches; next 1.88; 1.88 date 2002.10.20.17.49.03; author rse; state Exp; branches; next 1.87; 1.87 date 2002.10.20.16.19.39; author rse; state Exp; branches; next 1.86; 1.86 date 2002.10.20.16.10.38; author rse; state Exp; branches; next 1.85; 1.85 date 2002.10.15.21.14.27; author rse; state Exp; branches; next 1.84; 1.84 date 2002.10.15.20.34.22; author rse; state Exp; branches; next 1.83; 1.83 date 2002.01.27.13.15.28; author rse; state Exp; branches; next 1.82; 1.82 date 2002.01.27.12.39.10; author rse; state Exp; branches; next 1.81; 1.81 date 2002.01.27.11.03.40; author rse; state Exp; branches; next 1.80; 1.80 date 2002.01.27.10.47.01; author rse; state Exp; branches; next 1.79; 1.79 date 2001.07.30.18.47.28; author rse; state Exp; branches; next 1.78; 1.78 date 2001.07.30.18.45.07; author rse; state Exp; branches; next 1.77; 1.77 date 2001.07.30.18.29.32; author rse; state Exp; branches; next 1.76; 1.76 date 2001.03.24.14.51.04; author rse; state Exp; branches; next 1.75; 1.75 date 2000.10.03.09.26.47; author rse; state Exp; branches; next 1.74; 1.74 date 2000.08.18.08.35.29; author rse; state Exp; branches; next 1.73; 1.73 date 2000.04.30.09.38.54; author rse; state Exp; branches; next 1.72; 1.72 date 2000.03.12.19.13.43; author rse; state Exp; branches; next 1.71; 1.71 date 2000.03.09.12.11.51; author rse; state Exp; branches 1.71.2.1; next 1.70; 1.70 date 2000.02.20.11.59.15; author rse; state Exp; branches; next 1.69; 1.69 date 2000.02.13.17.24.02; author rse; state Exp; branches; next 1.68; 1.68 date 2000.02.11.07.44.16; author rse; state Exp; branches; next 1.67; 1.67 date 2000.01.24.15.07.25; author rse; state Exp; branches; next 1.66; 1.66 date 99.12.30.21.59.00; author rse; state Exp; branches; next 1.65; 1.65 date 99.11.14.13.01.17; author rse; state Exp; branches; next 1.64; 1.64 date 99.11.09.08.11.31; author rse; state Exp; branches; next 1.63; 1.63 date 99.11.01.10.27.19; author rse; state Exp; branches; next 1.62; 1.62 date 99.10.31.11.46.12; author rse; state Exp; branches 1.62.2.1; next 1.61; 1.61 date 99.10.19.14.40.25; author rse; state Exp; branches; next 1.60; 1.60 date 99.10.19.13.39.33; author rse; state Exp; branches; next 1.59; 1.59 date 99.09.17.08.01.55; author rse; state Exp; branches; next 1.58; 1.58 date 99.09.09.17.36.26; author rse; state Exp; branches; next 1.57; 1.57 date 99.09.01.08.51.23; author rse; state Exp; branches; next 1.56; 1.56 date 99.08.31.11.24.58; author rse; state Exp; branches; next 1.55; 1.55 date 99.08.20.07.06.37; author rse; state Exp; branches; next 1.54; 1.54 date 99.08.19.15.08.52; author rse; state Exp; branches; next 1.53; 1.53 date 99.08.18.12.59.33; author rse; state Exp; branches; next 1.52; 1.52 date 99.08.18.11.38.21; author rse; state Exp; branches; next 1.51; 1.51 date 99.08.17.08.08.36; author rse; state Exp; branches; next 1.50; 1.50 date 99.08.13.15.46.13; author rse; state Exp; branches; next 1.49; 1.49 date 99.08.13.15.44.54; author rse; state Exp; branches; next 1.48; 1.48 date 99.08.13.06.59.26; author rse; state Exp; branches; next 1.47; 1.47 date 99.08.10.17.32.42; author rse; state Exp; branches; next 1.46; 1.46 date 99.08.10.14.23.16; author rse; state Exp; branches; next 1.45; 1.45 date 99.08.05.06.55.30; author rse; state Exp; branches; next 1.44; 1.44 date 99.08.03.18.16.37; author rse; state Exp; branches; next 1.43; 1.43 date 99.08.03.15.14.28; author rse; state Exp; branches; next 1.42; 1.42 date 99.08.03.15.05.51; author rse; state Exp; branches; next 1.41; 1.41 date 99.08.03.12.24.03; author rse; state Exp; branches; next 1.40; 1.40 date 99.08.02.16.05.48; author rse; state Exp; branches 1.40.2.1; next 1.39; 1.39 date 99.08.02.14.50.36; author rse; state Exp; branches; next 1.38; 1.38 date 99.08.02.12.26.10; author rse; state Exp; branches; next 1.37; 1.37 date 99.07.22.15.47.20; author rse; state Exp; branches; next 1.36; 1.36 date 99.07.22.14.55.10; author rse; state Exp; branches; next 1.35; 1.35 date 99.07.16.11.15.09; author rse; state Exp; branches; next 1.34; 1.34 date 99.07.15.16.36.41; author rse; state Exp; branches; next 1.33; 1.33 date 99.07.15.16.01.16; author rse; state Exp; branches; next 1.32; 1.32 date 99.07.15.15.51.05; author rse; state Exp; branches; next 1.31; 1.31 date 99.07.11.15.34.23; author rse; state Exp; branches; next 1.30; 1.30 date 99.07.11.15.09.25; author rse; state Exp; branches; next 1.29; 1.29 date 99.07.11.11.55.21; author rse; state Exp; branches; next 1.28; 1.28 date 99.07.10.15.14.47; author rse; state Exp; branches; next 1.27; 1.27 date 99.07.10.14.21.17; author rse; state Exp; branches; next 1.26; 1.26 date 99.07.09.08.06.41; author rse; state Exp; branches; next 1.25; 1.25 date 99.07.08.10.34.01; author rse; state Exp; branches; next 1.24; 1.24 date 99.07.08.10.19.11; author rse; state Exp; branches; next 1.23; 1.23 date 99.07.08.09.41.00; author rse; state Exp; branches; next 1.22; 1.22 date 99.07.04.12.05.35; author rse; state Exp; branches; next 1.21; 1.21 date 99.06.28.07.51.56; author rse; state Exp; branches; next 1.20; 1.20 date 99.06.26.12.30.55; author rse; state Exp; branches; next 1.19; 1.19 date 99.06.04.21.26.10; author rse; state Exp; branches; next 1.18; 1.18 date 99.06.04.10.47.42; author rse; state Exp; branches; next 1.17; 1.17 date 99.06.03.09.30.51; author rse; state Exp; branches; next 1.16; 1.16 date 99.06.01.14.36.33; author rse; state Exp; branches; next 1.15; 1.15 date 99.06.01.10.05.29; author rse; state Exp; branches; next 1.14; 1.14 date 99.06.01.09.55.26; author rse; state Exp; branches; next 1.13; 1.13 date 99.05.30.13.08.37; author rse; state Exp; branches; next 1.12; 1.12 date 99.05.30.09.49.28; author rse; state Exp; branches; next 1.11; 1.11 date 99.05.30.09.33.59; author rse; state Exp; branches; next 1.10; 1.10 date 99.05.30.09.28.36; author rse; state Exp; branches; next 1.9; 1.9 date 99.05.28.16.23.14; author rse; state Exp; branches; next 1.8; 1.8 date 99.05.24.11.45.15; author rse; state Exp; branches; next 1.7; 1.7 date 99.05.24.10.24.25; author rse; state Exp; branches; next 1.6; 1.6 date 99.05.24.07.58.13; author rse; state Exp; branches; next 1.5; 1.5 date 99.05.24.07.45.56; author rse; state Exp; branches; next 1.4; 1.4 date 99.05.22.14.37.52; author rse; state Exp; branches; next 1.3; 1.3 date 99.05.21.15.22.32; author rse; state Exp; branches; next 1.2; 1.2 date 99.05.14.19.44.07; author rse; state Exp; branches; next 1.1; 1.1 date 99.05.14.16.29.08; author rse; state Exp; branches; next ; 1.71.2.1 date 2000.05.28.11.11.13; author rse; state Exp; branches; next ; 1.62.2.1 date 99.11.01.10.25.00; author rse; state Exp; branches; next 1.62.2.2; 1.62.2.2 date 99.11.14.13.03.44; author rse; state Exp; branches; next 1.62.2.3; 1.62.2.3 date 2000.02.11.11.31.39; author rse; state Exp; branches; next ; 1.40.2.1 date 99.08.31.08.30.29; author rse; state Exp; branches; next 1.40.2.2; 1.40.2.2 date 99.08.31.08.32.18; author rse; state Exp; branches; next ; desc @@ 1.111 log @Adjusted all copyright messages for new year 2007. @ text @/* ** GNU Pth - The GNU Portable Threads ** Copyright (c) 1999-2007 Ralf S. Engelschall ** ** 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 . ** ** 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 */ if (rfds != NULL) memcpy(rfds, &rspare, sizeof(fd_set)); if (wfds != NULL) memcpy(wfds, &wspare, sizeof(fd_set)); if (efds != NULL) memcpy(efds, &espare, sizeof(fd_set)); return rc; } /* suspend current thread until one filedescriptor is ready or the timeout occurred */ rc = -1; ev = ev_select = pth_event(PTH_EVENT_SELECT|PTH_MODE_STATIC, &ev_key_select, &rc, nfd, rfds, wfds, efds); ev_timeout = NULL; if (timeout != NULL) { ev_timeout = pth_event(PTH_EVENT_TIME|PTH_MODE_STATIC, &ev_key_timeout, pth_timeout(timeout->tv_sec, timeout->tv_usec)); pth_event_concat(ev, ev_timeout, NULL); } if (ev_extra != NULL) pth_event_concat(ev, ev_extra, NULL); pth_wait(ev); if (ev_extra != NULL) pth_event_isolate(ev_extra); if (timeout != NULL) pth_event_isolate(ev_timeout); /* select return code semantics */ if (pth_event_status(ev_select) == PTH_STATUS_FAILED) return pth_error(-1, EBADF); selected = FALSE; if (pth_event_status(ev_select) == PTH_STATUS_OCCURRED) selected = TRUE; if ( timeout != NULL && pth_event_status(ev_timeout) == PTH_STATUS_OCCURRED) { selected = TRUE; /* POSIX.1-2001/SUSv3 compliance */ if (rfds != NULL) FD_ZERO(rfds); if (wfds != NULL) FD_ZERO(wfds); if (efds != NULL) FD_ZERO(efds); rc = 0; } if (ev_extra != NULL && !selected) return pth_error(-1, EINTR); return rc; } /* Pth variant of pth_pselect(2) */ int pth_pselect(int nfds, fd_set *rfds, fd_set *wfds, fd_set *efds, const struct timespec *ts, const sigset_t *mask) { sigset_t omask; struct timeval tv; struct timeval *tvp; int rv; /* convert timeout */ if (ts != NULL) { tv.tv_sec = ts->tv_sec; tv.tv_usec = ts->tv_nsec / 1000; tvp = &tv; } else tvp = NULL; /* optionally set signal mask */ if (mask != NULL) if (pth_sc(sigprocmask)(SIG_SETMASK, mask, &omask) < 0) return pth_error(-1, errno); rv = pth_select(nfds, rfds, wfds, efds, tvp); /* optionally set signal mask */ if (mask != NULL) pth_shield { pth_sc(sigprocmask)(SIG_SETMASK, &omask, NULL); } return rv; } /* Pth variant of poll(2) */ int pth_poll(struct pollfd *pfd, nfds_t nfd, int timeout) { return pth_poll_ev(pfd, nfd, timeout, NULL); } /* Pth variant of poll(2) with extra events: NOTICE: THIS HAS TO BE BASED ON pth_select(2) BECAUSE INTERNALLY THE SCHEDULER IS ONLY select(2) BASED!! */ int pth_poll_ev(struct pollfd *pfd, nfds_t nfd, int timeout, pth_event_t ev_extra) { fd_set rfds, wfds, efds, xfds; struct timeval tv, *ptv; int maxfd, rc, n; unsigned int i; char data[64]; pth_implicit_init(); pth_debug2("pth_poll_ev: called from thread \"%s\"", pth_current->name); /* argument sanity checks */ if (pfd == NULL) return pth_error(-1, EFAULT); if (nfd < 0 || nfd > FD_SETSIZE) return pth_error(-1, EINVAL); /* convert timeout number into a timeval structure */ ptv = &tv; if (timeout == 0) { /* return immediately */ ptv->tv_sec = 0; ptv->tv_usec = 0; } else if (timeout == INFTIM /* (-1) */) { /* wait forever */ ptv = NULL; } else if (timeout > 0) { /* return after timeout */ ptv->tv_sec = (timeout / 1000); ptv->tv_usec = (timeout % 1000) * 1000; } else return pth_error(-1, EINVAL); /* create fd sets and determine max fd */ maxfd = -1; FD_ZERO(&rfds); FD_ZERO(&wfds); FD_ZERO(&efds); FD_ZERO(&xfds); for (i = 0; i < nfd; i++) { /* convert into fd_sets but remember that BSD select(2) says "the only exceptional condition detectable is out-of-band data received on a socket", hence we push POLLWRBAND events onto wfds instead of efds. Additionally, remember invalid filedescriptors in an extra fd_set xfds. */ if (!pth_util_fd_valid(pfd[i].fd)) { FD_SET(pfd[i].fd, &xfds); continue; } if (pfd[i].events & (POLLIN|POLLRDNORM)) FD_SET(pfd[i].fd, &rfds); if (pfd[i].events & (POLLOUT|POLLWRNORM|POLLWRBAND)) FD_SET(pfd[i].fd, &wfds); if (pfd[i].events & (POLLPRI|POLLRDBAND)) FD_SET(pfd[i].fd, &efds); if ( pfd[i].fd >= maxfd && (pfd[i].events & (POLLIN|POLLOUT|POLLPRI| POLLRDNORM|POLLRDBAND| POLLWRNORM|POLLWRBAND))) maxfd = pfd[i].fd; } /* examine fd sets with pth_select(3) */ rc = -1; if (maxfd != -1) { rc = pth_select_ev(maxfd+1, &rfds, &wfds, &efds, ptv, ev_extra); if (rc < 0) return pth_error(-1, errno); else if (rc == 0) return 0; } /* POSIX.1-2001/SUSv3 compliant result establishment */ n = 0; for (i = 0; i < nfd; i++) { pfd[i].revents = 0; if (FD_ISSET(pfd[i].fd, &xfds)) { if (pfd[i].fd >= 0) { pfd[i].revents |= POLLNVAL; n++; } continue; } if (maxfd == -1) continue; if (FD_ISSET(pfd[i].fd, &rfds)) { if (pfd[i].events & POLLIN) pfd[i].revents |= POLLIN; if (pfd[i].events & POLLRDNORM) pfd[i].revents |= POLLRDNORM; n++; /* support for POLLHUP */ if ( recv(pfd[i].fd, data, sizeof(data), MSG_PEEK) == -1 && ( errno == ESHUTDOWN || errno == ECONNRESET || errno == ECONNABORTED || errno == ENETRESET )) { pfd[i].revents &= ~(POLLIN); pfd[i].revents &= ~(POLLRDNORM); pfd[i].revents |= POLLHUP; } } else if (FD_ISSET(pfd[i].fd, &wfds)) { if (pfd[i].events & POLLOUT) pfd[i].revents |= POLLOUT; if (pfd[i].events & POLLWRNORM) pfd[i].revents |= POLLWRNORM; if (pfd[i].events & POLLWRBAND) pfd[i].revents |= POLLWRBAND; n++; } else if (FD_ISSET(pfd[i].fd, &efds)) { if (pfd[i].events & POLLPRI) pfd[i].revents |= POLLPRI; if (pfd[i].events & POLLRDBAND) pfd[i].revents |= POLLRDBAND; n++; } } return n; } /* Pth variant of connect(2) */ int pth_connect(int s, const struct sockaddr *addr, socklen_t addrlen) { return pth_connect_ev(s, addr, addrlen, NULL); } /* Pth variant of connect(2) with extra events */ int pth_connect_ev(int s, const struct sockaddr *addr, socklen_t addrlen, pth_event_t ev_extra) { pth_event_t ev; static pth_key_t ev_key = PTH_KEY_INIT; int rv, err; socklen_t errlen; int fdmode; pth_implicit_init(); pth_debug2("pth_connect_ev: enter from thread \"%s\"", pth_current->name); /* POSIX compliance */ if (!pth_util_fd_valid(s)) return pth_error(-1, EBADF); /* force filedescriptor into non-blocking mode */ if ((fdmode = pth_fdmode(s, PTH_FDMODE_NONBLOCK)) == PTH_FDMODE_ERROR) return pth_error(-1, EBADF); /* try to connect */ while ( (rv = pth_sc(connect)(s, (struct sockaddr *)addr, addrlen)) == -1 && errno == EINTR) ; /* restore filedescriptor mode */ pth_shield { pth_fdmode(s, fdmode); } /* if it is still on progress wait until socket is really writeable */ if (rv == -1 && errno == EINPROGRESS && fdmode != PTH_FDMODE_NONBLOCK) { if ((ev = pth_event(PTH_EVENT_FD|PTH_UNTIL_FD_WRITEABLE|PTH_MODE_STATIC, &ev_key, s)) == NULL) return pth_error(-1, 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(-1, EINTR); } errlen = sizeof(err); if (getsockopt(s, SOL_SOCKET, SO_ERROR, (void *)&err, &errlen) == -1) return -1; if (err == 0) return 0; return pth_error(rv, err); } pth_debug2("pth_connect_ev: leave to thread \"%s\"", pth_current->name); return rv; } /* Pth variant of accept(2) */ int pth_accept(int s, struct sockaddr *addr, socklen_t *addrlen) { return pth_accept_ev(s, addr, addrlen, NULL); } /* Pth variant of accept(2) with extra events */ int pth_accept_ev(int s, struct sockaddr *addr, socklen_t *addrlen, pth_event_t ev_extra) { pth_event_t ev; static pth_key_t ev_key = PTH_KEY_INIT; int fdmode; int rv; pth_implicit_init(); pth_debug2("pth_accept_ev: enter from thread \"%s\"", pth_current->name); /* POSIX compliance */ if (!pth_util_fd_valid(s)) return pth_error(-1, EBADF); /* force filedescriptor into non-blocking mode */ if ((fdmode = pth_fdmode(s, PTH_FDMODE_NONBLOCK)) == PTH_FDMODE_ERROR) return pth_error(-1, EBADF); /* poll socket via accept */ ev = NULL; while ((rv = pth_sc(accept)(s, addr, addrlen)) == -1 && (errno == EAGAIN || errno == EWOULDBLOCK) && fdmode != PTH_FDMODE_NONBLOCK) { /* do lazy event allocation */ if (ev == NULL) { if ((ev = pth_event(PTH_EVENT_FD|PTH_UNTIL_FD_READABLE|PTH_MODE_STATIC, &ev_key, s)) == NULL) return pth_error(-1, errno); if (ev_extra != NULL) pth_event_concat(ev, ev_extra, NULL); } /* wait until accept has a chance */ pth_wait(ev); /* check for the extra events */ if (ev_extra != NULL) { pth_event_isolate(ev); if (pth_event_status(ev) != PTH_STATUS_OCCURRED) { pth_fdmode(s, fdmode); return pth_error(-1, EINTR); } } } /* restore filedescriptor mode */ pth_shield { pth_fdmode(s, fdmode); if (rv != -1) pth_fdmode(rv, fdmode); } pth_debug2("pth_accept_ev: leave to thread \"%s\"", pth_current->name); return rv; } /* Pth variant of read(2) */ ssize_t pth_read(int fd, void *buf, size_t nbytes) { return pth_read_ev(fd, buf, nbytes, NULL); } /* Pth variant of read(2) with extra event(s) */ ssize_t pth_read_ev(int fd, void *buf, size_t nbytes, pth_event_t ev_extra) { struct timeval delay; pth_event_t ev; static pth_key_t ev_key = PTH_KEY_INIT; fd_set fds; int fdmode; int n; pth_implicit_init(); pth_debug2("pth_read_ev: enter from thread \"%s\"", pth_current->name); /* POSIX compliance */ if (nbytes == 0) return 0; if (!pth_util_fd_valid(fd)) return pth_error(-1, EBADF); /* check mode of filedescriptor */ if ((fdmode = pth_fdmode(fd, PTH_FDMODE_POLL)) == PTH_FDMODE_ERROR) return pth_error(-1, EBADF); /* poll filedescriptor if not already in non-blocking operation */ if (fdmode == PTH_FDMODE_BLOCK) { /* now directly poll filedescriptor for readability to avoid unneccessary (and resource consuming because of context switches, etc) event handling through the scheduler */ FD_ZERO(&fds); FD_SET(fd, &fds); delay.tv_sec = 0; delay.tv_usec = 0; while ((n = pth_sc(select)(fd+1, &fds, NULL, NULL, &delay)) < 0 && errno == EINTR) ; if (n < 0 && (errno == EINVAL || errno == EBADF)) return pth_error(-1, errno); /* if filedescriptor is still not readable, let thread sleep until it is or the extra event occurs */ if (n == 0) { ev = pth_event(PTH_EVENT_FD|PTH_UNTIL_FD_READABLE|PTH_MODE_STATIC, &ev_key, fd); if (ev_extra != NULL) pth_event_concat(ev, ev_extra, NULL); n = pth_wait(ev); if (ev_extra != NULL) { pth_event_isolate(ev); if (pth_event_status(ev) != PTH_STATUS_OCCURRED) return pth_error(-1, EINTR); } } } /* Now perform the actual read. We're now guarrantied to not block, either because we were already in non-blocking mode or we determined above by polling that the next read(2) call will not block. But keep in mind, that only 1 next read(2) call is guarrantied to not block (except for the EINTR situation). */ while ((n = pth_sc(read)(fd, buf, nbytes)) < 0 && errno == EINTR) ; pth_debug2("pth_read_ev: leave to thread \"%s\"", pth_current->name); return n; } /* Pth variant of write(2) */ ssize_t pth_write(int fd, const void *buf, size_t nbytes) { return pth_write_ev(fd, buf, nbytes, NULL); } /* Pth variant of write(2) with extra event(s) */ ssize_t pth_write_ev(int fd, const void *buf, size_t nbytes, pth_event_t ev_extra) { struct timeval delay; pth_event_t ev; static pth_key_t ev_key = PTH_KEY_INIT; fd_set fds; int fdmode; ssize_t rv; ssize_t s; int n; pth_implicit_init(); pth_debug2("pth_write_ev: enter from thread \"%s\"", pth_current->name); /* POSIX compliance */ if (nbytes == 0) return 0; if (!pth_util_fd_valid(fd)) return pth_error(-1, EBADF); /* force filedescriptor into non-blocking mode */ if ((fdmode = pth_fdmode(fd, PTH_FDMODE_NONBLOCK)) == PTH_FDMODE_ERROR) return pth_error(-1, EBADF); /* poll filedescriptor if not already in non-blocking operation */ if (fdmode != PTH_FDMODE_NONBLOCK) { /* now directly poll filedescriptor for writeability to avoid unneccessary (and resource consuming because of context switches, etc) event handling through the scheduler */ FD_ZERO(&fds); FD_SET(fd, &fds); delay.tv_sec = 0; delay.tv_usec = 0; while ((n = pth_sc(select)(fd+1, NULL, &fds, NULL, &delay)) < 0 && errno == EINTR) ; if (n < 0 && (errno == EINVAL || errno == EBADF)) return pth_error(-1, errno); rv = 0; for (;;) { /* if filedescriptor is still not writeable, let thread sleep until it is or event occurs */ if (n < 1) { ev = pth_event(PTH_EVENT_FD|PTH_UNTIL_FD_WRITEABLE|PTH_MODE_STATIC, &ev_key, fd); 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) { pth_fdmode(fd, fdmode); return pth_error(-1, EINTR); } } } /* now perform the actual write operation */ while ((s = pth_sc(write)(fd, buf, nbytes)) < 0 && errno == EINTR) ; if (s > 0) rv += s; /* although we're physically now in non-blocking mode, iterate unless all data is written or an error occurs, because we've to mimic the usual blocking I/O behaviour of write(2). */ if (s > 0 && s < (ssize_t)nbytes) { nbytes -= s; buf = (void *)((char *)buf + s); n = 0; continue; } /* pass error to caller, but not for partial writes (rv > 0) */ if (s < 0 && rv == 0) rv = -1; /* stop looping */ break; } } else { /* just perform the actual write operation */ while ((rv = pth_sc(write)(fd, buf, nbytes)) < 0 && errno == EINTR) ; } /* restore filedescriptor mode */ pth_shield { pth_fdmode(fd, fdmode); } pth_debug2("pth_write_ev: leave to thread \"%s\"", pth_current->name); return rv; } /* Pth variant of readv(2) */ ssize_t pth_readv(int fd, const struct iovec *iov, int iovcnt) { return pth_readv_ev(fd, iov, iovcnt, NULL); } /* Pth variant of readv(2) with extra event(s) */ ssize_t pth_readv_ev(int fd, const struct iovec *iov, int iovcnt, pth_event_t ev_extra) { struct timeval delay; pth_event_t ev; static pth_key_t ev_key = PTH_KEY_INIT; fd_set fds; int fdmode; int n; pth_implicit_init(); pth_debug2("pth_readv_ev: enter from thread \"%s\"", pth_current->name); /* POSIX compliance */ if (iovcnt <= 0 || iovcnt > UIO_MAXIOV) return pth_error(-1, EINVAL); if (!pth_util_fd_valid(fd)) return pth_error(-1, EBADF); /* check mode of filedescriptor */ if ((fdmode = pth_fdmode(fd, PTH_FDMODE_POLL)) == PTH_FDMODE_ERROR) return pth_error(-1, EBADF); /* poll filedescriptor if not already in non-blocking operation */ if (fdmode == PTH_FDMODE_BLOCK) { /* first directly poll filedescriptor for readability to avoid unneccessary (and resource consuming because of context switches, etc) event handling through the scheduler */ FD_ZERO(&fds); FD_SET(fd, &fds); delay.tv_sec = 0; delay.tv_usec = 0; while ((n = pth_sc(select)(fd+1, &fds, NULL, NULL, &delay)) < 0 && errno == EINTR) ; /* if filedescriptor is still not readable, let thread sleep until it is or event occurs */ if (n < 1) { ev = pth_event(PTH_EVENT_FD|PTH_UNTIL_FD_READABLE|PTH_MODE_STATIC, &ev_key, fd); if (ev_extra != NULL) pth_event_concat(ev, ev_extra, NULL); n = pth_wait(ev); if (ev_extra != NULL) { pth_event_isolate(ev); if (pth_event_status(ev) != PTH_STATUS_OCCURRED) return pth_error(-1, EINTR); } } } /* Now perform the actual read. We're now guarrantied to not block, either because we were already in non-blocking mode or we determined above by polling that the next read(2) call will not block. But keep in mind, that only 1 next read(2) call is guarrantied to not block (except for the EINTR situation). */ #if PTH_FAKE_RWV while ((n = pth_readv_faked(fd, iov, iovcnt)) < 0 && errno == EINTR) ; #else while ((n = pth_sc(readv)(fd, iov, iovcnt)) < 0 && errno == EINTR) ; #endif pth_debug2("pth_readv_ev: leave to thread \"%s\"", pth_current->name); return n; } /* A faked version of readv(2) */ intern ssize_t pth_readv_faked(int fd, const struct iovec *iov, int iovcnt) { char *buffer; size_t bytes, copy, rv; int i; /* determine total number of bytes to read */ bytes = 0; for (i = 0; i < iovcnt; i++) { if (iov[i].iov_len <= 0) return pth_error((ssize_t)(-1), EINVAL); bytes += iov[i].iov_len; } if (bytes <= 0) return pth_error((ssize_t)(-1), EINVAL); /* allocate a temporary buffer */ if ((buffer = (char *)malloc(bytes)) == NULL) return (ssize_t)(-1); /* read data into temporary buffer (caller guarrantied us to not block) */ rv = pth_sc(read)(fd, buffer, bytes); /* scatter read data into callers vector */ if (rv > 0) { bytes = rv; for (i = 0; i < iovcnt; i++) { copy = pth_util_min(iov[i].iov_len, bytes); memcpy(iov[i].iov_base, buffer, copy); buffer += copy; bytes -= copy; if (bytes <= 0) break; } } /* remove the temporary buffer */ pth_shield { free(buffer); } /* return number of read bytes */ return(rv); } /* Pth variant of writev(2) */ ssize_t pth_writev(int fd, const struct iovec *iov, int iovcnt) { return pth_writev_ev(fd, iov, iovcnt, NULL); } /* Pth variant of writev(2) with extra event(s) */ ssize_t pth_writev_ev(int fd, const struct iovec *iov, int iovcnt, pth_event_t ev_extra) { struct timeval delay; pth_event_t ev; static pth_key_t ev_key = PTH_KEY_INIT; fd_set fds; int fdmode; struct iovec *liov; int liovcnt; size_t nbytes; ssize_t rv; ssize_t s; int n; struct iovec tiov_stack[32]; struct iovec *tiov; int tiovcnt; pth_implicit_init(); pth_debug2("pth_writev_ev: enter from thread \"%s\"", pth_current->name); /* POSIX compliance */ if (iovcnt <= 0 || iovcnt > UIO_MAXIOV) return pth_error(-1, EINVAL); if (!pth_util_fd_valid(fd)) return pth_error(-1, EBADF); /* force filedescriptor into non-blocking mode */ if ((fdmode = pth_fdmode(fd, PTH_FDMODE_NONBLOCK)) == PTH_FDMODE_ERROR) return pth_error(-1, EBADF); /* poll filedescriptor if not already in non-blocking operation */ if (fdmode != PTH_FDMODE_NONBLOCK) { /* provide temporary iovec structure */ if (iovcnt > sizeof(tiov_stack)) { tiovcnt = (sizeof(struct iovec) * UIO_MAXIOV); if ((tiov = (struct iovec *)malloc(tiovcnt)) == NULL) return pth_error(-1, errno); } else { tiovcnt = sizeof(tiov_stack); tiov = tiov_stack; } /* init return value and number of bytes to write */ rv = 0; nbytes = pth_writev_iov_bytes(iov, iovcnt); /* init local iovec structure */ liov = NULL; liovcnt = 0; pth_writev_iov_advance(iov, iovcnt, 0, &liov, &liovcnt, tiov, tiovcnt); /* first directly poll filedescriptor for writeability to avoid unneccessary (and resource consuming because of context switches, etc) event handling through the scheduler */ FD_ZERO(&fds); FD_SET(fd, &fds); delay.tv_sec = 0; delay.tv_usec = 0; while ((n = pth_sc(select)(fd+1, NULL, &fds, NULL, &delay)) < 0 && errno == EINTR) ; for (;;) { /* if filedescriptor is still not writeable, let thread sleep until it is or event occurs */ if (n < 1) { ev = pth_event(PTH_EVENT_FD|PTH_UNTIL_FD_WRITEABLE|PTH_MODE_STATIC, &ev_key, fd); 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) { pth_fdmode(fd, fdmode); if (iovcnt > sizeof(tiov_stack)) free(tiov); return pth_error(-1, EINTR); } } } /* now perform the actual write operation */ #if PTH_FAKE_RWV while ((s = pth_writev_faked(fd, liov, liovcnt)) < 0 && errno == EINTR) ; #else while ((s = pth_sc(writev)(fd, liov, liovcnt)) < 0 && errno == EINTR) ; #endif if (s > 0) rv += s; /* although we're physically now in non-blocking mode, iterate unless all data is written or an error occurs, because we've to mimic the usual blocking I/O behaviour of writev(2) */ if (s > 0 && s < (ssize_t)nbytes) { nbytes -= s; pth_writev_iov_advance(iov, iovcnt, s, &liov, &liovcnt, tiov, tiovcnt); n = 0; continue; } /* pass error to caller, but not for partial writes (rv > 0) */ if (s < 0 && rv == 0) rv = -1; /* stop looping */ break; } /* cleanup */ if (iovcnt > sizeof(tiov_stack)) free(tiov); } else { /* just perform the actual write operation */ #if PTH_FAKE_RWV while ((rv = pth_writev_faked(fd, iov, iovcnt)) < 0 && errno == EINTR) ; #else while ((rv = pth_sc(writev)(fd, iov, iovcnt)) < 0 && errno == EINTR) ; #endif } /* restore filedescriptor mode */ pth_shield { pth_fdmode(fd, fdmode); } pth_debug2("pth_writev_ev: leave to thread \"%s\"", pth_current->name); return rv; } /* calculate number of bytes in a struct iovec */ intern ssize_t pth_writev_iov_bytes(const struct iovec *iov, int iovcnt) { ssize_t bytes; int i; bytes = 0; for (i = 0; i < iovcnt; i++) { if (iov[i].iov_len <= 0) continue; bytes += iov[i].iov_len; } return bytes; } /* advance the virtual pointer of a struct iov */ intern void pth_writev_iov_advance(const struct iovec *riov, int riovcnt, size_t advance, struct iovec **liov, int *liovcnt, struct iovec *tiov, int tiovcnt) { int i; if (*liov == NULL && *liovcnt == 0) { /* initialize with real (const) structure on first step */ *liov = (struct iovec *)riov; *liovcnt = riovcnt; } if (advance > 0) { if (*liov == riov && *liovcnt == riovcnt) { /* reinitialize with a copy to be able to adjust it */ *liov = &tiov[0]; for (i = 0; i < riovcnt; i++) { tiov[i].iov_base = riov[i].iov_base; tiov[i].iov_len = riov[i].iov_len; } } /* advance the virtual pointer */ while (*liovcnt > 0 && advance > 0) { if ((*liov)->iov_len > advance) { (*liov)->iov_base = (char *)((*liov)->iov_base) + advance; (*liov)->iov_len -= advance; break; } else { advance -= (*liov)->iov_len; (*liovcnt)--; (*liov)++; } } } return; } /* A faked version of writev(2) */ intern ssize_t pth_writev_faked(int fd, const struct iovec *iov, int iovcnt) { char *buffer, *cp; size_t bytes, to_copy, copy, rv; int i; /* determine total number of bytes to write */ bytes = 0; for (i = 0; i < iovcnt; i++) { if (iov[i].iov_len <= 0) return pth_error((ssize_t)(-1), EINVAL); bytes += iov[i].iov_len; } if (bytes <= 0) return pth_error((ssize_t)(-1), EINVAL); /* allocate a temporary buffer to hold the data */ if ((buffer = (char *)malloc(bytes)) == NULL) return (ssize_t)(-1); /* concatenate the data from callers vector into buffer */ to_copy = bytes; cp = buffer; for (i = 0; i < iovcnt; i++) { copy = pth_util_min(iov[i].iov_len, to_copy); memcpy(cp, iov[i].iov_base, copy); to_copy -= copy; if (to_copy <= 0) break; } /* write continuous chunck of data (caller guarrantied us to not block) */ rv = pth_sc(write)(fd, buffer, bytes); /* remove the temporary buffer */ pth_shield { free(buffer); } return(rv); } /* Pth variant of POSIX pread(3) */ ssize_t pth_pread(int fd, void *buf, size_t nbytes, off_t offset) { static pth_mutex_t mutex = PTH_MUTEX_INIT; off_t old_offset; ssize_t rc; /* protect us: pth_read can yield! */ if (!pth_mutex_acquire(&mutex, FALSE, NULL)) return (-1); /* remember current offset */ if ((old_offset = lseek(fd, 0, SEEK_CUR)) == (off_t)(-1)) { pth_mutex_release(&mutex); return (-1); } /* seek to requested offset */ if (lseek(fd, offset, SEEK_SET) == (off_t)(-1)) { pth_mutex_release(&mutex); return (-1); } /* perform the read operation */ rc = pth_read(fd, buf, nbytes); /* restore the old offset situation */ pth_shield { lseek(fd, old_offset, SEEK_SET); } /* unprotect and return result of read */ pth_mutex_release(&mutex); return rc; } /* Pth variant of POSIX pwrite(3) */ ssize_t pth_pwrite(int fd, const void *buf, size_t nbytes, off_t offset) { static pth_mutex_t mutex = PTH_MUTEX_INIT; off_t old_offset; ssize_t rc; /* protect us: pth_write can yield! */ if (!pth_mutex_acquire(&mutex, FALSE, NULL)) return (-1); /* remember current offset */ if ((old_offset = lseek(fd, 0, SEEK_CUR)) == (off_t)(-1)) { pth_mutex_release(&mutex); return (-1); } /* seek to requested offset */ if (lseek(fd, offset, SEEK_SET) == (off_t)(-1)) { pth_mutex_release(&mutex); return (-1); } /* perform the write operation */ rc = pth_write(fd, buf, nbytes); /* restore the old offset situation */ pth_shield { lseek(fd, old_offset, SEEK_SET); } /* unprotect and return result of write */ pth_mutex_release(&mutex); return rc; } /* Pth variant of SUSv2 recv(2) */ ssize_t pth_recv(int s, void *buf, size_t len, int flags) { return pth_recv_ev(s, buf, len, flags, NULL); } /* Pth variant of SUSv2 recv(2) with extra event(s) */ ssize_t pth_recv_ev(int s, void *buf, size_t len, int flags, pth_event_t ev) { return pth_recvfrom_ev(s, buf, len, flags, NULL, 0, ev); } /* Pth variant of SUSv2 recvfrom(2) */ ssize_t pth_recvfrom(int s, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen) { return pth_recvfrom_ev(s, buf, len, flags, from, fromlen, NULL); } /* Pth variant of SUSv2 recvfrom(2) with extra event(s) */ ssize_t pth_recvfrom_ev(int fd, void *buf, size_t nbytes, int flags, struct sockaddr *from, socklen_t *fromlen, pth_event_t ev_extra) { struct timeval delay; pth_event_t ev; static pth_key_t ev_key = PTH_KEY_INIT; fd_set fds; int fdmode; int n; pth_implicit_init(); pth_debug2("pth_recvfrom_ev: enter from thread \"%s\"", pth_current->name); /* POSIX compliance */ if (nbytes == 0) return 0; if (!pth_util_fd_valid(fd)) return pth_error(-1, EBADF); /* check mode of filedescriptor */ if ((fdmode = pth_fdmode(fd, PTH_FDMODE_POLL)) == PTH_FDMODE_ERROR) return pth_error(-1, EBADF); /* poll filedescriptor if not already in non-blocking operation */ if (fdmode == PTH_FDMODE_BLOCK) { /* now directly poll filedescriptor for readability to avoid unneccessary (and resource consuming because of context switches, etc) event handling through the scheduler */ if (!pth_util_fd_valid(fd)) return pth_error(-1, EBADF); FD_ZERO(&fds); FD_SET(fd, &fds); delay.tv_sec = 0; delay.tv_usec = 0; while ((n = pth_sc(select)(fd+1, &fds, NULL, NULL, &delay)) < 0 && errno == EINTR) ; if (n < 0 && (errno == EINVAL || errno == EBADF)) return pth_error(-1, errno); /* if filedescriptor is still not readable, let thread sleep until it is or the extra event occurs */ if (n == 0) { ev = pth_event(PTH_EVENT_FD|PTH_UNTIL_FD_READABLE|PTH_MODE_STATIC, &ev_key, fd); if (ev_extra != NULL) pth_event_concat(ev, ev_extra, NULL); n = pth_wait(ev); if (ev_extra != NULL) { pth_event_isolate(ev); if (pth_event_status(ev) != PTH_STATUS_OCCURRED) return pth_error(-1, EINTR); } } } /* now perform the actual read. We're now guarrantied to not block, either because we were already in non-blocking mode or we determined above by polling that the next recvfrom(2) call will not block. But keep in mind, that only 1 next recvfrom(2) call is guarrantied to not block (except for the EINTR situation). */ while ((n = pth_sc(recvfrom)(fd, buf, nbytes, flags, from, fromlen)) < 0 && errno == EINTR) ; pth_debug2("pth_recvfrom_ev: leave to thread \"%s\"", pth_current->name); return n; } /* Pth variant of SUSv2 send(2) */ ssize_t pth_send(int s, const void *buf, size_t len, int flags) { return pth_send_ev(s, buf, len, flags, NULL); } /* Pth variant of SUSv2 send(2) with extra event(s) */ ssize_t pth_send_ev(int s, const void *buf, size_t len, int flags, pth_event_t ev) { return pth_sendto_ev(s, buf, len, flags, NULL, 0, ev); } /* Pth variant of SUSv2 sendto(2) */ ssize_t pth_sendto(int s, const void *buf, size_t len, int flags, const struct sockaddr *to, socklen_t tolen) { return pth_sendto_ev(s, buf, len, flags, to, tolen, NULL); } /* Pth variant of SUSv2 sendto(2) with extra event(s) */ ssize_t pth_sendto_ev(int fd, const void *buf, size_t nbytes, int flags, const struct sockaddr *to, socklen_t tolen, pth_event_t ev_extra) { struct timeval delay; pth_event_t ev; static pth_key_t ev_key = PTH_KEY_INIT; fd_set fds; int fdmode; ssize_t rv; ssize_t s; int n; pth_implicit_init(); pth_debug2("pth_sendto_ev: enter from thread \"%s\"", pth_current->name); /* POSIX compliance */ if (nbytes == 0) return 0; if (!pth_util_fd_valid(fd)) return pth_error(-1, EBADF); /* force filedescriptor into non-blocking mode */ if ((fdmode = pth_fdmode(fd, PTH_FDMODE_NONBLOCK)) == PTH_FDMODE_ERROR) return pth_error(-1, EBADF); /* poll filedescriptor if not already in non-blocking operation */ if (fdmode != PTH_FDMODE_NONBLOCK) { /* now directly poll filedescriptor for writeability to avoid unneccessary (and resource consuming because of context switches, etc) event handling through the scheduler */ if (!pth_util_fd_valid(fd)) { pth_fdmode(fd, fdmode); return pth_error(-1, EBADF); } FD_ZERO(&fds); FD_SET(fd, &fds); delay.tv_sec = 0; delay.tv_usec = 0; while ((n = pth_sc(select)(fd+1, NULL, &fds, NULL, &delay)) < 0 && errno == EINTR) ; if (n < 0 && (errno == EINVAL || errno == EBADF)) return pth_error(-1, errno); rv = 0; for (;;) { /* if filedescriptor is still not writeable, let thread sleep until it is or event occurs */ if (n == 0) { ev = pth_event(PTH_EVENT_FD|PTH_UNTIL_FD_WRITEABLE|PTH_MODE_STATIC, &ev_key, fd); 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) { pth_fdmode(fd, fdmode); return pth_error(-1, EINTR); } } } /* now perform the actual send operation */ while ((s = pth_sc(sendto)(fd, buf, nbytes, flags, to, tolen)) < 0 && errno == EINTR) ; if (s > 0) rv += s; /* although we're physically now in non-blocking mode, iterate unless all data is written or an error occurs, because we've to mimic the usual blocking I/O behaviour of write(2). */ if (s > 0 && s < (ssize_t)nbytes) { nbytes -= s; buf = (void *)((char *)buf + s); n = 0; continue; } /* pass error to caller, but not for partial writes (rv > 0) */ if (s < 0 && rv == 0) rv = -1; /* stop looping */ break; } } else { /* just perform the actual send operation */ while ((rv = pth_sc(sendto)(fd, buf, nbytes, flags, to, tolen)) < 0 && errno == EINTR) ; } /* restore filedescriptor mode */ pth_shield { pth_fdmode(fd, fdmode); } pth_debug2("pth_sendto_ev: leave to thread \"%s\"", pth_current->name); return rv; } @ 1.110 log @Adjusted all copyright messages for new year 2006 @ text @d3 1 a3 1 ** Copyright (c) 1999-2006 Ralf S. Engelschall @ 1.109 log @Adjusted all copyright messages for new year 2005. @ text @d3 1 a3 1 ** Copyright (c) 1999-2005 Ralf S. Engelschall @ 1.108 log @Internally handle errors returned from pth_event() in order to pass them upstream to the caller in pth_high.c functions. @ text @d3 1 a3 1 ** Copyright (c) 1999-2004 Ralf S. Engelschall @ 1.107 log @Use "(char *)NULL" instead of plain "NULL" in last argument to execl(2) to avoid GCC 3.5's "warning: missing sentinel in function call". @ text @d63 2 a64 1 ev = pth_event(PTH_EVENT_TIME|PTH_MODE_STATIC, &ev_key, until); d96 2 a97 1 ev = pth_event(PTH_EVENT_TIME|PTH_MODE_STATIC, &ev_key, until); d121 2 a122 1 ev = pth_event(PTH_EVENT_TIME|PTH_MODE_STATIC, &ev_key, until); d172 2 a173 1 ev = pth_event(PTH_EVENT_SIGS|PTH_MODE_STATIC, &ev_key, set, sigp); d626 2 a627 1 ev = pth_event(PTH_EVENT_FD|PTH_UNTIL_FD_WRITEABLE|PTH_MODE_STATIC, &ev_key, s); d680 2 a681 1 ev = pth_event(PTH_EVENT_FD|PTH_UNTIL_FD_READABLE|PTH_MODE_STATIC, &ev_key, s); @ 1.106 log @Adjusted all copyright messages for new year 2004. @ text @d255 1 a255 1 execl(PTH_PATH_BINSH, "sh", "-c", cmd, NULL); @ 1.105 log @Adjusted all copyright messages for new year 2003. @ text @d3 1 a3 1 ** Copyright (c) 1999-2003 Ralf S. Engelschall @ 1.104 log @Make pth_poll(3) more compliant to POSIX.1-2001/SUSv3 poll(2). Parts submitted by: Nick Hudson @ text @d3 1 a3 1 ** Copyright (c) 1999-2002 Ralf S. Engelschall @ 1.103 log @Make pth_select(2) more compliant to POSIX.1-2001/SUSv3 select(2). This especially fixes the polling-only situation (timeout = (0,0)). @ text @d464 1 a464 1 fd_set rfds, wfds, efds; d466 1 a466 1 int maxfd, rc, ok; d473 1 a473 1 /* poll(2) semantics */ d476 2 d492 1 a492 1 ptv->tv_sec = timeout / 1000; d503 11 a513 3 for(i = 0; i < nfd; i++) { if (!pth_util_fd_valid(pfd[i].fd)) return pth_error(-1, EBADF); a515 3 /* see select(2): "the only exceptional condition detectable is out-of-band data received on a socket", hence we push POLLWRBAND events onto wfds instead of efds. */ a525 2 if (maxfd == -1) return pth_error(-1, EINVAL); d527 9 a535 2 /* examine fd sets */ rc = pth_select_ev(maxfd+1, &rfds, &wfds, &efds, ptv, ev_extra); d537 6 a542 7 /* establish results */ if (rc > 0) { rc = 0; for (i = 0; i < nfd; i++) { ok = 0; pfd[i].revents = 0; if (pfd[i].fd < 0) { d544 1 a544 1 continue; d546 17 a562 16 if (FD_ISSET(pfd[i].fd, &rfds)) { if (pfd[i].events & POLLIN) pfd[i].revents |= POLLIN; if (pfd[i].events & POLLRDNORM) pfd[i].revents |= POLLRDNORM; ok++; /* support for POLLHUP */ if (recv(pfd[i].fd, data, sizeof(data), MSG_PEEK) == -1) { if ( errno == ESHUTDOWN || errno == ECONNRESET || errno == ECONNABORTED || errno == ENETRESET) { pfd[i].revents &= ~(POLLIN); pfd[i].revents &= ~(POLLRDNORM); pfd[i].revents |= POLLHUP; ok--; } } d564 16 a579 18 if (FD_ISSET(pfd[i].fd, &wfds)) { if (pfd[i].events & POLLOUT) pfd[i].revents |= POLLOUT; if (pfd[i].events & POLLWRNORM) pfd[i].revents |= POLLWRNORM; if (pfd[i].events & POLLWRBAND) pfd[i].revents |= POLLWRBAND; ok++; } if (FD_ISSET(pfd[i].fd, &efds)) { if (pfd[i].events & POLLPRI) pfd[i].revents |= POLLPRI; if (pfd[i].events & POLLRDBAND) pfd[i].revents |= POLLRDBAND; ok++; } if (ok) rc++; d582 2 a583 1 return rc; @ 1.102 log @cosmetics: be conistent with pth_select_ev parameter names @ text @d300 1 a300 1 /* POSIX compliance */ a304 1 || timeout->tv_sec > 100000000 /* about 3 years */ d306 1 a306 1 || timeout->tv_usec >= 1000000 /* a full second */) d308 2 d314 1 a314 3 if (timeout->tv_sec < 0 || timeout->tv_usec < 0) return pth_error(-1, EINVAL); if (timeout->tv_sec == 0 && timeout->tv_usec < 500000) { d320 1 a320 1 /* larger delays have to use the scheduler */ d332 1 a332 1 /* POSIX compliance */ d339 1 a339 1 /* now directly poll filedescriptor sets to avoid unneccessary d342 2 a343 2 all platforms guarranty us that the sets are unmodified if an error or timeout occured. */ d348 1 a348 1 rspare = *rfds; d353 1 a353 1 wspare = *wfds; d358 1 a358 1 espare = *efds; d361 11 a371 2 while ((rc = pth_sc(select)(nfd, rtmp, wtmp, etmp, &delay)) < 0 && errno == EINTR) ; if (rc > 0) { d373 1 a373 1 *rfds = rspare; d375 1 a375 1 *wfds = wspare; d377 1 a377 1 *efds = espare; a379 11 if (rc == 0 && timeout != NULL) { if (pth_time_cmp(timeout, PTH_TIME_ZERO) == 0) { /* POSIX compliance */ if (rfds != NULL) FD_ZERO(rfds); if (wfds != NULL) FD_ZERO(wfds); if (efds != NULL) FD_ZERO(efds); return 0; } } if (rc < 0 && (errno == EINVAL || errno == EBADF)) return pth_error(-1, errno); d381 2 a382 1 /* suspend current thread until one fd is ready or the timeout occurred */ d395 8 a403 1 pth_event_isolate(ev_select); d406 8 a413 10 if (timeout != NULL) { pth_event_isolate(ev_timeout); if (pth_event_status(ev_timeout) == PTH_STATUS_OCCURRED) { selected = TRUE; /* POSIX compliance */ if (rfds != NULL) FD_ZERO(rfds); if (wfds != NULL) FD_ZERO(wfds); if (efds != NULL) FD_ZERO(efds); rc = 0; } a414 2 if (pth_event_status(ev_select) == PTH_STATUS_FAILED) return pth_error(-1, EBADF); d417 1 @ 1.101 log @maintain size only at a single position @ text @d276 2 a277 2 int pth_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) d279 1 a279 1 return pth_select_ev(nfds, readfds, writefds, exceptfds, timeout, NULL); @ 1.100 log @Fix poll(2) semantics: remove POLLRDNORM from polling result if POLLHUP is detected. Hint by: Paolo Bonzini @ text @d540 1 a540 1 if (recv(pfd[i].fd, data, 64, MSG_PEEK) == -1) { @ 1.99 log @Check for PTH_FDMODE_ERROR error conditions internally, too. @ text @d544 1 @ 1.98 log @recognize fcntl(3) errors in pth_fdmode @ text @d701 1 d713 4 d718 1 a718 1 if (pth_fdmode(fd, PTH_FDMODE_POLL) == PTH_FDMODE_BLOCK) { d873 1 d885 4 d890 1 a890 1 if (pth_fdmode(fd, PTH_FDMODE_POLL) == PTH_FDMODE_BLOCK) { d1302 1 d1314 4 d1319 1 a1319 1 if (pth_fdmode(fd, PTH_FDMODE_POLL) == PTH_FDMODE_BLOCK) { @ 1.97 log @more POSIX conformance for pth_send/pth_recv @ text @d595 2 a596 1 fdmode = pth_fdmode(s, PTH_FDMODE_NONBLOCK); d651 2 a652 1 fdmode = pth_fdmode(s, PTH_FDMODE_NONBLOCK); d782 2 a783 1 fdmode = pth_fdmode(fd, PTH_FDMODE_NONBLOCK); d1002 2 a1003 1 fdmode = pth_fdmode(fd, PTH_FDMODE_NONBLOCK); d1387 2 a1388 1 fdmode = pth_fdmode(fd, PTH_FDMODE_NONBLOCK); @ 1.96 log @use even more POSIX conforming semantics @ text @d1313 2 d1318 1 a1318 1 if (n < 1) { d1401 2 d1408 1 a1408 1 if (n < 1) { @ 1.95 log @1. The function "int pth_event_occurred(pth_event_t)" was replaced with "pth_status_t pth_event_status(pth_event_t)" where pth_status_t can have values of PTH_STATUS_PENDING (replacing the old FALSE return value of pth_event_occurred), PTH_STATUS_OCCURRED (replacing the old TRUE return value of pth_event_occurred), and PTH_STATUS_FAILED (a new return value indicating an error in processing the event). This was scheduler/event-manager errors can be indicated which happended while processing the event. For backward compatibility reasons, a macro pth_event_occurred() was added. This will be removed soon. 2. Use the new PTH_STATUS_FAILED event status in the scheduler's event-manager for filedescriptor events if the internal select(2) call returned with an error. Additionally this PTH_STATUS_FAILED is recognized by the high-level API functions (pth_select, etc) and produce the necessary POSIX conforming return codes (usually -1 and errno == EBADF). Parts submitted by: Thanh Luu @ text @d722 2 d727 1 a727 1 if (n < 1) { d794 2 @ 1.94 log @also complain on EINVAL immediately without asking the scheduler @ text @d175 1 a175 1 if (!pth_event_occurred(ev)) d329 1 a329 1 if (!pth_event_occurred(ev)) d399 1 a399 1 if (pth_event_occurred(ev_select)) d403 1 a403 1 if (pth_event_occurred(ev_timeout)) { d412 2 d613 1 a613 1 if (!pth_event_occurred(ev)) d668 1 a668 1 if (!pth_event_occurred(ev)) { d732 1 a732 1 if (!pth_event_occurred(ev)) d804 1 a804 1 if (!pth_event_occurred(ev)) { d894 1 a894 1 if (!pth_event_occurred(ev)) d1039 1 a1039 1 if (!pth_event_occurred(ev)) { d1319 1 a1319 1 if (!pth_event_occurred(ev)) d1407 1 a1407 1 if (!pth_event_occurred(ev)) { @ 1.93 log @Add a Pth variant of the new POSIX pselect(2) function, including soft and hard syscall mapping support for it. @ text @d381 2 a382 2 if (rc < 0 && errno == EBADF) return pth_error(-1, EBADF); @ 1.92 log @More POSIX compliance for pth_select() in case of invalid timeout values and invalid filedescriptors. @ text @d417 32 @ 1.91 log @Internally switch from "errno_shield {...}" to "pth_shield {...}" and from "return_errno(..)" to "return pth_error(...)" in order to make the internal error handling a little bit more consistent. @ text @d303 7 d381 2 @ 1.90 log @Added POSIX-compliant sanity checks for bad filedescriptors to mostly all filedescriptor-based I/O functions in pth_high.c @ text @d49 1 a49 1 return_errno(-1, EFAULT); d51 1 a51 1 return_errno(-1, EINVAL); d155 1 a155 1 return_errno(EINVAL, EINVAL); d176 1 a176 1 return_errno(EINTR, EINTR); d302 1 a302 1 return_errno(-1, EINVAL); d307 1 a307 1 return_errno(-1, EINVAL); d323 1 a323 1 return_errno(-1, EINTR); d404 1 a404 1 return_errno(-1, EINTR); d430 1 a430 1 return_errno(-1, EFAULT); d449 1 a449 1 return_errno(-1, EINVAL); d458 1 a458 1 return_errno(-1, EBADF); d475 1 a475 1 return_errno(-1, EINVAL); d549 1 a549 1 return_errno(-1, EBADF); d560 1 a560 1 errno_shield { pth_fdmode(s, fdmode); } d571 1 a571 1 return_errno(-1, EINTR); d578 1 a578 1 return_errno(rv, err); d604 1 a604 1 return_errno(-1, EBADF); d627 1 a627 1 return_errno(-1, EINTR); d633 1 a633 1 errno_shield { d665 1 a665 1 return_errno(-1, EBADF); d690 1 a690 1 return_errno(-1, EINTR); d732 1 a732 1 return_errno(-1, EBADF); d763 1 a763 1 return_errno(-1, EINTR); d799 1 a799 1 errno_shield { pth_fdmode(fd, fdmode); } d825 1 a825 1 return_errno(-1, EINVAL); d827 1 a827 1 return_errno(-1, EBADF); d852 1 a852 1 return_errno(-1, EINTR); d885 1 a885 1 return_errno((ssize_t)(-1), EINVAL); d889 1 a889 1 return_errno((ssize_t)(-1), EINVAL); d912 1 a912 1 errno_shield { free(buffer); } d947 1 a947 1 return_errno(-1, EINVAL); d949 1 a949 1 return_errno(-1, EBADF); d960 1 a960 1 return -1 /* errno is set */; d1000 1 a1000 1 return_errno(-1, EINTR); d1050 1 a1050 1 errno_shield { pth_fdmode(fd, fdmode); } d1120 1 a1120 1 return_errno((ssize_t)(-1), EINVAL); d1124 1 a1124 1 return_errno((ssize_t)(-1), EINVAL); d1145 1 a1145 1 errno_shield { free(buffer); } d1176 1 a1176 1 errno_shield { lseek(fd, old_offset, SEEK_SET); } d1209 1 a1209 1 errno_shield { lseek(fd, old_offset, SEEK_SET); } d1250 1 a1250 1 return_errno(-1, EBADF); d1259 1 a1259 1 return_errno(-1, EBADF); d1277 1 a1277 1 return_errno(-1, EINTR); d1331 1 a1331 1 return_errno(-1, EBADF); d1344 1 a1344 1 return_errno(-1, EBADF); d1366 1 a1366 1 return_errno(-1, EINTR); d1402 1 a1402 1 errno_shield { pth_fdmode(fd, fdmode); } @ 1.89 log @Added pth_nanosleep() function. Obtained from: NetBSD, Nick Hudson @ text @d300 2 a301 2 /* sanity checking */ if (!pth_util_fd_valid(nfd-1)) d547 4 d602 4 d664 2 a672 2 if (!pth_util_fd_valid(fd)) return_errno(-1, EBADF); d731 2 a742 4 if (!pth_util_fd_valid(fd)) { pth_fdmode(fd, fdmode); return_errno(-1, EBADF); } d826 2 a834 2 if (!pth_util_fd_valid(fd)) return_errno(-1, EBADF); d948 2 a978 6 if (!pth_util_fd_valid(fd)) { pth_fdmode(fd, fdmode); if (iovcnt > sizeof(tiov_stack)) free(tiov); return_errno(-1, EBADF); } d1249 2 d1330 2 @ 1.88 log @add extra sanity check for nfds in pth_select @ text @d38 39 @ 1.87 log @Changes three direct sigprocmask(2) calls with the corresponding "hard syscall mapping" macro calls. Submitted by: Nick Hudson @ text @d261 4 @ 1.86 log @fix left-over from cut & paste @ text @d198 1 a198 1 sigprocmask(SIG_BLOCK, &ss_block, &ss_old); d210 1 a210 1 sigprocmask(SIG_SETMASK, &ss_old, NULL); d230 1 a230 1 sigprocmask(SIG_SETMASK, &ss_old, NULL); @ 1.85 log @Added support to pth_poll(3) for the poll(2) XPG.4 flags POLLD{RD,WR}{NORM,BAND}. Submitted by: Jason Evans Final shaping by: Ralf S. Engelschall @ text @d365 1 a365 1 /* Pth variant of select(2) */ @ 1.84 log @remove trailing whitespaces @ text @d416 1 a416 1 if (pfd[i].events & POLLIN) d418 4 a421 1 if (pfd[i].events & POLLOUT) d423 1 a423 1 if (pfd[i].events & POLLPRI) d425 4 a428 1 if (pfd[i].fd >= maxfd && (pfd[i].events & (POLLIN|POLLOUT|POLLPRI))) d448 4 a451 1 pfd[i].revents |= POLLIN; d464 6 a469 1 pfd[i].revents |= POLLOUT; d473 4 a476 1 pfd[i].revents |= POLLPRI; @ 1.83 log @Internally make sure an invalid file-descriptor (integer not between 0 and (FD_SETSIZE-1) does not lead to any segfaults or other undefined behaviour. Instead an error is returned and errno is set to EBADF, similar to what the OS functions do. Especially pth_poll() now return with this error (instead of skipping the fd) if an fd in the "struct pollfd" is invalid. Hint by: Archie Cobbs @ text @d155 1 a155 1 while ( (pid = pth_sc(waitpid)(wpid, status, options|WNOHANG)) < 0 d1011 1 a1011 1 struct iovec **liov, int *liovcnt, @ 1.82 log @Correctly support PTH_FDMODE_NONBLOCK in pth_connect and pth_accept. Submitted by: Archie Cobbs @ text @d414 2 a415 2 if (pfd[i].fd < 0) continue; d603 2 d673 4 d767 2 d911 6 d1194 2 d1276 4 @ 1.81 log @bump copyright year @ text @d499 1 a499 1 if (rv == -1 && errno == EINPROGRESS) { d544 2 a545 1 && (errno == EAGAIN || errno == EWOULDBLOCK)) { @ 1.80 log @Replaced thread-unsafe usage of a static struct iovec in pth_writev_ev() with a thread-safe stack/heap-based solution. Submitted by: Mark Burton @ text @d3 1 a3 1 ** Copyright (c) 1999-2001 Ralf S. Engelschall @ 1.79 log @*** empty log message *** @ text @d863 3 d879 10 d897 1 a897 1 pth_writev_iov_advance(iov, iovcnt, 0, &liov, &liovcnt); d921 2 d944 1 a944 1 pth_writev_iov_advance(iov, iovcnt, s, &liov, &liovcnt); d956 4 d996 2 a997 1 struct iovec **liov, int *liovcnt) a998 1 static struct iovec siov[UIO_MAXIOV]; d1009 1 a1009 1 *liov = &siov[0]; d1011 2 a1012 2 siov[i].iov_base = riov[i].iov_base; siov[i].iov_len = riov[i].iov_len; @ 1.78 log @*** empty log message *** @ text @d913 1 a913 1 /* Now perform the actual write operation */ d943 1 a943 1 /* Now perform the actual write operation */ d1182 1 a1182 1 /* Now perform the actual read. We're now guarrantied to not block, d1290 1 a1290 1 /* Now perform the actual send operation */ @ 1.77 log @*** empty log message *** @ text @d293 1 a293 1 all platforms guarranty us that the sets are unmodified when an error d498 1 a498 1 /* when it is still on progress wait until socket is really writeable */ d596 1 a596 1 /* poll filedescriptor when not already in non-blocking operation */ d609 1 a609 1 /* when filedescriptor is still not readable, d664 1 a664 1 /* poll filedescriptor when not already in non-blocking operation */ d679 1 a679 1 /* when filedescriptor is still not writeable, d720 1 a720 1 /* Now perform the actual write operation */ d754 1 a754 1 /* poll filedescriptor when not already in non-blocking operation */ d767 1 a767 1 /* when filedescriptor is still not readable, d874 1 a874 1 /* poll filedescriptor when not already in non-blocking operation */ d897 1 a897 1 /* when filedescriptor is still not writeable, d1154 1 a1154 1 /* poll filedescriptor when not already in non-blocking operation */ d1167 1 a1167 1 /* when filedescriptor is still not readable, d1234 1 a1234 1 /* poll filedescriptor when not already in non-blocking operation */ d1249 1 a1249 1 /* when filedescriptor is still not writeable, @ 1.76 log @*** empty log message *** @ text @d929 1 a929 1 pth_writev_iov_advance(iov, iovcnt, n, &liov, &liovcnt); @ 1.75 log @*** empty log message *** @ text @d3 1 a3 1 ** Copyright (c) 1999-2000 Ralf S. Engelschall @ 1.74 log @*** empty log message *** @ text @d144 92 a729 27 } /* 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; @ 1.73 log @*** empty log message *** @ text @d46 4 d69 4 @ 1.72 log @*** empty log message *** @ text @d47 1 a47 1 offset = pth_time(0, (long)usec); @ 1.71 log @*** empty log message *** @ text @d1047 182 @ 1.71.2.1 log @*** empty log message *** @ text @d47 1 a47 1 offset = pth_time((long)(usec / 1000000), (long)(usec % 1000000)); @ 1.70 log @*** empty log message *** @ text @d47 1 a47 1 offset = pth_time(0, usec); @ 1.69 log @*** empty log message *** @ text @d33 1 a33 1 * block these variants let only the tread sleep. @ 1.68 log @*** empty log message *** @ text @d641 4 a644 7 /* 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 < 0 || pid > 0) return pid; if (pid == 0 && (options & WNOHANG)) return 0; d646 3 a648 4 /* else create a pid event and block current thread until it occurred */ ev = pth_event(PTH_EVENT_PID|PTH_MODE_STATIC, &ev_key, wpid, status, options); pth_wait(ev); d650 6 a655 2 /* finally do the waitpid again for the result value and return it (?REALLY?) */ /* pid = pth_sc(waitpid)(wpid, status, options); */ @ 1.67 log @*** empty log message *** @ text @d382 7 d394 4 d416 2 @ 1.66 log @*** empty log message *** @ text @d166 1 a166 1 /* very small delays we can accept to perform directly */ @ 1.65 log @*** empty log message *** @ text @d3 1 a3 1 ** Copyright (c) 1999 Ralf S. Engelschall @ 1.64 log @*** empty log message *** @ text @d598 4 d847 4 @ 1.63 log @*** empty log message *** @ text @d70 1 a70 1 d137 1 a137 1 int pth_select(int nfds, fd_set *readfds, fd_set *writefds, d144 1 a144 1 int pth_select_ev(int nfd, fd_set *rfds, fd_set *wfds, d157 1 a157 1 d167 1 a167 1 while ( pth_sc(select)(0, NULL, NULL, NULL, timeout) < 0 d172 1 a172 1 ev = pth_event(PTH_EVENT_TIME|PTH_MODE_STATIC, &ev_key_timeout, d190 1 a190 1 /* now directly poll filedescriptor sets to avoid unneccessary d195 1 a195 1 delay.tv_sec = 0; d212 1 a212 1 while ((rc = pth_sc(select)(nfd, rtmp, wtmp, etmp, &delay)) < 0 && errno == EINTR) ; d234 1 a234 1 ev = ev_select = pth_event(PTH_EVENT_SELECT|PTH_MODE_STATIC, d238 1 a238 1 ev_timeout = pth_event(PTH_EVENT_TIME|PTH_MODE_STATIC, &ev_key_timeout, d271 1 a271 1 /* Pth variant of poll(2) with extra events: d305 1 a305 1 else d382 1 a382 1 d384 1 a384 1 while ( (rv = pth_sc(connect)(s, (struct sockaddr *)addr, addrlen)) == -1 d401 1 a401 1 if (err == 0) d452 1 a452 1 pth_fdmode(s, fdmode); d486 1 a486 1 /* now directly poll filedescriptor for readability d491 1 a491 1 delay.tv_sec = 0; d493 2 a494 2 while ((n = pth_sc(select)(fd+1, &fds, NULL, NULL, &delay)) < 0 && errno == EINTR) ; d496 1 a496 1 /* when filedescriptor is still not readable, d516 2 a517 2 while ((n = pth_sc(read)(fd, buf, nbytes)) < 0 && errno == EINTR) ; d554 1 a554 1 /* now directly poll filedescriptor for writeability d559 1 a559 1 delay.tv_sec = 0; d561 2 a562 2 while ((n = pth_sc(select)(fd+1, NULL, &fds, NULL, &delay)) < 0 && errno == EINTR) ; d566 1 a566 1 /* when filedescriptor is still not writeable, d583 1 a583 1 while ((s = pth_sc(write)(fd, buf, nbytes)) < 0 d604 1 a604 1 while ((rv = pth_sc(write)(fd, buf, nbytes)) < 0 d621 1 a621 1 d623 1 a623 1 d633 1 a633 1 ev = pth_event(PTH_EVENT_PID|PTH_MODE_STATIC, d667 1 a667 1 /* first directly poll filedescriptor for readability d672 1 a672 1 delay.tv_sec = 0; d674 2 a675 2 while ((n = pth_sc(select)(fd+1, &fds, NULL, NULL, &delay)) < 0 && errno == EINTR) ; d677 1 a677 1 /* when filedescriptor is still not readable, d698 1 a698 1 while ((n = pth_readv_faked(fd, iov, iovcnt)) < 0 d701 1 a701 1 while ((n = pth_sc(readv)(fd, iov, iovcnt)) < 0 d729 1 a729 1 d746 1 a746 1 /* remove the temporary buffer */ d780 1 a780 1 d796 1 a796 1 /* first directly poll filedescriptor for writeability d801 1 a801 1 delay.tv_sec = 0; d803 2 a804 2 while ((n = pth_sc(select)(fd+1, NULL, &fds, NULL, &delay)) < 0 && errno == EINTR) ; d807 1 a807 1 /* when filedescriptor is still not writeable, d825 1 a825 1 while ((s = pth_writev_faked(fd, liov, liovcnt)) < 0 d828 1 a828 1 while ((s = pth_sc(writev)(fd, liov, liovcnt)) < 0 d851 1 a851 1 while ((rv = pth_writev_faked(fd, iov, iovcnt)) < 0 d854 1 a854 1 while ((rv = pth_sc(writev)(fd, iov, iovcnt)) < 0 d954 1 a954 1 /* remove the temporary buffer */ d984 1 a984 1 d1017 1 a1017 1 d1019 1 a1019 1 errno_shield { lseek(fd, old_offset, SEEK_SET); } @ 1.62 log @*** empty log message *** @ text @d2 1 a2 2 ** pth_high.c -- Pth high-level replacement functions ** d22 2 @ 1.62.2.1 log @*** empty log message *** @ text @d2 2 a3 1 ** GNU Pth - The GNU Portable Threads a22 2 ** ** pth_high.c: Pth high-level replacement functions @ 1.62.2.2 log @*** empty log message *** @ text @a597 4 /* pass error to caller, but not for partial writes (rv > 0) */ if (s < 0 && rv == 0) rv = -1; a842 4 /* pass error to caller, but not for partial writes (rv > 0) */ if (s < 0 && rv == 0) rv = -1; @ 1.62.2.3 log @*** empty log message *** @ text @d382 1 a382 8 int fdmode; pth_implicit_init(); pth_debug2("pth_connect_ev: enter from thread \"%s\"", pth_current->name); /* force filedescriptor into non-blocking mode */ fdmode = pth_fdmode(s, PTH_FDMODE_NONBLOCK); a386 4 /* restore filedescriptor mode */ errno_shield { pth_fdmode(s, fdmode); } a404 2 pth_debug2("pth_connect_ev: leave to thread \"%s\"", pth_current->name); @ 1.61 log @*** empty log message *** @ text @d12 1 a12 1 ** version 2 of the License, or (at your option) any later version. @ 1.60 log @*** empty log message *** @ text @d265 1 a265 1 int pth_poll(struct pollfd *pfd, unsigned int nfd, int timeout) d273 1 a273 1 int pth_poll_ev(struct pollfd *pfd, unsigned int nfd, int timeout, pth_event_t ev_extra) @ 1.59 log @*** empty log message *** @ text @d904 2 a905 2 (*liov)->iov_base = (void *)((char *)((*liov)->iov_base) + advance); (*liov)->iov_len -= advance; @ 1.58 log @*** empty log message *** @ text @d10 1 a10 1 ** modify it under the terms of the GNU Library General Public d17 1 a17 1 ** Library General Public License for more details. d19 1 a19 1 ** You should have received a copy of the GNU Library General Public @ 1.57 log @*** empty log message *** @ text @d450 1 a450 1 errno_safe( d454 1 a454 1 ); d608 1 a608 1 errno_safe( pth_fdmode(fd, fdmode) ); d746 1 a746 1 errno_safe( free(buffer) ); d859 1 a859 1 errno_safe( pth_fdmode(fd, fdmode) ); d954 1 a954 1 errno_safe( free(buffer) ); d985 1 a985 1 errno_safe( lseek(fd, old_offset, SEEK_SET) ); d1018 1 a1018 1 errno_safe( lseek(fd, old_offset, SEEK_SET) ); @ 1.56 log @*** empty log message *** @ text @d80 10 a89 4 /* that's really easy in PTH, we just call POSIX sigprocmask(). Because the Pth machine context switching is written to safe and restore also the complete signal set */ return sigprocmask(how, set, oset); @ 1.55 log @*** empty log message *** @ text @d363 1 a363 1 int pth_connect(int s, const struct sockaddr *addr, int addrlen) d369 1 a369 1 int pth_connect_ev(int s, const struct sockaddr *addr, int addrlen, pth_event_t ev_extra) d373 2 a374 1 int rv, err, errlen; d402 1 a402 1 int pth_accept(int s, struct sockaddr *addr, int *addrlen) d408 1 a408 1 int pth_accept_ev(int s, struct sockaddr *addr, int *addrlen, pth_event_t ev_extra) @ 1.54 log @*** empty log message *** @ text @d289 1 a289 1 else if (timeout == INFTIM) { d293 1 a293 1 else { d298 2 @ 1.53 log @*** empty log message *** @ text @d24 4 @ 1.52 log @*** empty log message *** @ text @d571 1 a571 1 if (rv > 0) d817 1 a817 1 if (rv > 0) @ 1.51 log @*** empty log message *** @ text @d891 1 a891 1 (*liov)->iov_base += advance; @ 1.50 log @*** empty log message *** @ text @d267 2 a268 1 int maxfd, rc, i, ok; d523 2 a524 1 int rv; d569 1 a569 1 while ((n = pth_sc(write)(fd, buf, nbytes)) < 0 d572 1 a572 1 rv += n; d577 3 a579 3 if (n > 0 && n < nbytes) { nbytes -= n; buf = (void *)((char *)buf + n); d696 1 a696 1 intern size_t pth_readv_faked(int fd, const struct iovec *iov, int iovcnt) d706 1 a706 1 return_errno(-1, EINVAL); d710 1 a710 1 return_errno(-1, EINVAL); d713 2 a714 2 if ((buffer = malloc(bytes)) == NULL) return -1; d756 2 a757 1 int rv; d811 1 a811 1 while ((n = pth_writev_faked(fd, liov, liovcnt)) < 0 d814 1 a814 1 while ((n = pth_sc(writev)(fd, liov, liovcnt)) < 0 d818 1 a818 1 rv += n; d823 2 a824 2 if (n > 0 && n < nbytes) { nbytes -= n; d853 1 a853 1 intern size_t pth_writev_iov_bytes(const struct iovec *iov, int iovcnt) d855 1 a855 1 size_t bytes; d868 1 a868 1 intern void pth_writev_iov_advance(const struct iovec *riov, int riovcnt, int advance, d906 1 a906 1 intern size_t pth_writev_faked(int fd, const struct iovec *iov, int iovcnt) d916 1 a916 1 return_errno(-1, EINVAL); d920 1 a920 1 return_errno(-1, EINVAL); d923 2 a924 2 if ((buffer = malloc(bytes)) == NULL) return -1; @ 1.49 log @*** empty log message *** @ text @d172 4 d179 1 a179 1 /* first directly poll filedescriptor sets to avoid unneccessary d211 6 a216 2 if (rc == 0 && timeout != NULL) if (pth_time_cmp(timeout, PTH_TIME_ZERO) == 0) d218 2 d222 1 d240 1 a240 1 if (pth_event_occurred(ev_timeout)) d242 6 d569 1 a569 1 if (rv >= 0) d588 1 a588 1 while ((n = pth_sc(write)(fd, buf, nbytes)) < 0 a589 1 rv = n; d645 1 a645 1 if (iovcnt <= 0) d751 4 d761 1 a761 1 if (iovcnt <= 0) d770 9 d789 14 a802 12 /* when filedescriptor is still not writeable, let thread sleep until it is or event occurs */ if (n < 1) { ev = pth_event(PTH_EVENT_FD|PTH_UNTIL_FD_WRITEABLE|PTH_MODE_STATIC, &ev_key, fd); 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_occurred(ev)) { pth_fdmode(fd, fdmode); return_errno(-1, EINTR); d805 24 d831 2 a832 3 /* Now perform the actual write operation. We are guarrantied to not block, because we are now in non-blocking mode. */ d834 2 a835 2 while ((n = pth_writev_faked(fd, iov, iovcnt)) < 0 && errno == EINTR) ; d837 2 a838 2 while ((n = pth_sc(writev)(fd, iov, iovcnt)) < 0 && errno == EINTR) ; d840 1 d846 54 a899 1 return n; @ 1.48 log @*** empty log message *** @ text @d213 1 a213 1 &ev_key_select, &rc, nfd, rfds, wfds, wfds); @ 1.47 log @*** empty log message *** @ text @d558 1 a558 1 if (0 <= n && n < nbytes) { d561 1 @ 1.46 log @*** empty log message *** @ text @d505 1 d531 1 d552 2 d572 1 d579 1 a579 1 return n; @ 1.45 log @*** empty log message *** @ text @d530 14 a543 12 /* when filedescriptor is still not writeable, let thread sleep until it is or event occurs */ if (n < 1) { ev = pth_event(PTH_EVENT_FD|PTH_UNTIL_FD_WRITEABLE|PTH_MODE_STATIC, &ev_key, fd); 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_occurred(ev)) { pth_fdmode(fd, fdmode); return_errno(-1, EINTR); d546 16 d564 5 a568 5 /* Now perform the actual write operation. We are guarrantied to not block, because we are now in non-blocking mode. */ while ((n = pth_sc(write)(fd, buf, nbytes)) < 0 && errno == EINTR) ; @ 1.44 log @*** empty log message *** @ text @d161 2 a162 2 ev = pth_event(PTH_EVENT_TIME|PTH_MODE_STATIC, &ev_key_timeout, pth_timeout(timeout->tv_sec, timeout->tv_usec)); d216 2 a217 2 ev_timeout = pth_event(PTH_EVENT_TIME|PTH_MODE_STATIC, &ev_key_timeout, pth_timeout(timeout->tv_sec, timeout->tv_usec)); @ 1.43 log @*** empty log message *** @ text @d146 3 d162 1 a162 1 &ev_key_timeout, *timeout); d217 1 a217 1 &ev_key_timeout, *timeout); d253 3 d418 1 a418 1 /* restore filedescriptor mode (on _BOTH_ socket descriptors!) */ d421 2 a422 1 pth_fdmode(rv, fdmode); @ 1.42 log @*** empty log message *** @ text @d413 4 a416 2 errno_safe( pth_fdmode(s, fdmode) ); errno_safe( pth_fdmode(rv, fdmode) ); @ 1.41 log @*** empty log message *** @ text @d412 1 a412 1 /* restore filedescriptor mode */ d414 1 @ 1.40 log @*** empty log message *** @ text @d381 1 d387 3 d405 2 a406 1 if (!pth_event_occurred(ev)) d408 1 d411 4 d441 25 a465 21 /* first directly poll filedescriptor for readability to avoid unneccessary (and resource consuming because of context switches, etc) event handling through the scheduler */ FD_ZERO(&fds); FD_SET(fd, &fds); delay.tv_sec = 0; delay.tv_usec = 0; while ((n = pth_sc(select)(fd+1, &fds, NULL, NULL, &delay)) < 0 && errno == EINTR) ; /* when filedescriptor is still not readable, let thread sleep until it is or event occurs */ if (n < 1) { ev = pth_event(PTH_EVENT_FD|PTH_UNTIL_FD_READABLE|PTH_MODE_STATIC, &ev_key, fd); if (ev_extra != NULL) pth_event_concat(ev, ev_extra, NULL); n = pth_wait(ev); if (ev_extra != NULL) { pth_event_isolate(ev); if (!pth_event_occurred(ev)) return_errno(-1, EINTR); d469 5 a473 1 /* now perform the actual read */ d475 1 a475 1 && errno == EINTR) ; d494 1 d504 30 a533 21 /* first directly poll filedescriptor for writeability to avoid unneccessary (and resource consuming because of context switches, etc) event handling through the scheduler */ FD_ZERO(&fds); FD_SET(fd, &fds); delay.tv_sec = 0; delay.tv_usec = 0; while ((n = pth_sc(select)(fd+1, NULL, &fds, NULL, &delay)) < 0 && errno == EINTR) ; /* when filedescriptor is still not writeable, let thread sleep until it is or event occurs */ if (n < 1) { ev = pth_event(PTH_EVENT_FD|PTH_UNTIL_FD_WRITEABLE|PTH_MODE_STATIC, &ev_key, fd); 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_occurred(ev)) return_errno(-1, EINTR); d537 2 a538 1 /* now perform the actual write */ d542 3 d598 25 a622 21 /* first directly poll filedescriptor for readability to avoid unneccessary (and resource consuming because of context switches, etc) event handling through the scheduler */ FD_ZERO(&fds); FD_SET(fd, &fds); delay.tv_sec = 0; delay.tv_usec = 0; while ((n = pth_sc(select)(fd+1, &fds, NULL, NULL, &delay)) < 0 && errno == EINTR) ; /* when filedescriptor is still not readable, let thread sleep until it is or event occurs */ if (n < 1) { ev = pth_event(PTH_EVENT_FD|PTH_UNTIL_FD_READABLE|PTH_MODE_STATIC, &ev_key, fd); if (ev_extra != NULL) pth_event_concat(ev, ev_extra, NULL); n = pth_wait(ev); if (ev_extra != NULL) { pth_event_isolate(ev); if (!pth_event_occurred(ev)) return_errno(-1, EINTR); d626 5 a630 1 /* now perform the actual read */ d664 1 a664 1 /* read data into temporary buffer */ d700 1 d709 3 d713 27 a739 21 /* first directly poll filedescriptor for writeability to avoid unneccessary (and resource consuming because of context switches, etc) event handling through the scheduler */ FD_ZERO(&fds); FD_SET(fd, &fds); delay.tv_sec = 0; delay.tv_usec = 0; while ((n = pth_sc(select)(fd+1, NULL, &fds, NULL, &delay)) < 0 && errno == EINTR) ; /* when filedescriptor is still not writeable, let thread sleep until it is or event occurs */ if (n < 1) { ev = pth_event(PTH_EVENT_FD|PTH_UNTIL_FD_WRITEABLE|PTH_MODE_STATIC, &ev_key, fd); 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_occurred(ev)) return_errno(-1, EINTR); d743 2 a744 1 /* now perform the actual write */ d753 3 d792 1 a792 1 /* write continuous chunck of data */ @ 1.40.2.1 log @*** empty log message *** @ text @d158 2 a159 2 ev = pth_event(PTH_EVENT_TIME|PTH_MODE_STATIC, &ev_key_timeout, pth_timeout(timeout->tv_sec, timeout->tv_usec)); d213 2 a214 2 ev_timeout = pth_event(PTH_EVENT_TIME|PTH_MODE_STATIC, &ev_key_timeout, pth_timeout(timeout->tv_sec, timeout->tv_usec)); @ 1.40.2.2 log @*** empty log message *** @ text @d210 1 a210 1 &ev_key_select, &rc, nfd, rfds, wfds, efds); @ 1.39 log @*** empty log message *** @ text @d625 1 a625 1 rv = read(fd, buffer, bytes); d739 1 a739 1 rv = write(fd, buffer, bytes); @ 1.38 log @*** empty log message *** @ text @d427 4 d481 4 d562 4 d664 4 @ 1.37 log @*** empty log message *** @ text @d383 3 d405 1 @ 1.36 log @*** empty log message *** @ text @d753 1 a753 3 errno_preserve; lseek(fd, old_offset, SEEK_SET); errno_restore; d786 1 a786 3 errno_preserve; lseek(fd, old_offset, SEEK_SET); errno_restore; @ 1.35 log @*** empty log message *** @ text @d532 265 @ 1.34 log @*** empty log message *** @ text @a531 87 /* * implementation of a convinient greedy tread-safe line reading function to * avoid slow byte-wise reading from filedescriptors - which is important for * high-performance situations. */ #define READLINE_MAXLEN 1024 static pth_key_t readline_key; static pth_once_t readline_once_ctrl = PTH_ONCE_INIT; typedef struct { int rl_cnt; char *rl_bufptr; char rl_buf[READLINE_MAXLEN]; } readline_buf; static void readline_buf_destroy(void *vp) { free(vp); return; } static void readline_init(void *vp) { pth_key_create(&readline_key, readline_buf_destroy); return; } static ssize_t readline_read(readline_buf *rl, int fd, char *ptr, pth_event_t ev_extra) { if (rl->rl_cnt <= 0) { for (;;) { if ((rl->rl_cnt = pth_read_ev(fd, rl->rl_buf, READLINE_MAXLEN, ev_extra)) < 0) return -1; else if (rl->rl_cnt == 0) return 0; rl->rl_bufptr = rl->rl_buf; break; } } rl->rl_cnt--; *ptr = *rl->rl_bufptr++; return 1; } ssize_t pth_readline(int fd, void *buf, size_t buflen) { return pth_readline_ev(fd, buf, buflen, NULL); } ssize_t pth_readline_ev(int fd, void *buf, size_t buflen, pth_event_t ev_extra) { int n, rc; char c, *cp; readline_buf *rl; pth_once(&readline_once_ctrl, readline_init, NULL); if ((rl = (readline_buf *)pth_key_getdata(readline_key)) == NULL) { rl = malloc(sizeof(readline_buf)); rl->rl_cnt = 0; rl->rl_bufptr = NULL; pth_key_setdata(readline_key, rl); } cp = (char *)buf; for (n = 1; n < buflen; n++) { if ((rc = readline_read(rl, fd, &c, ev_extra)) == 1) { if (c == '\r') { n--; continue; } *cp++ = c; if (c == '\n') break; } else if (rc == 0) { if (n == 1) return 0; else break; } else return -1; } *cp = NUL; return n; } @ 1.33 log @*** empty log message *** @ text @d420 1 d470 1 d511 2 @ 1.32 log @*** empty log message *** @ text @d153 1 a153 1 while ( select(0, NULL, NULL, NULL, timeout) < 0 @ 1.31 log @*** empty log message *** @ text @d420 2 d449 2 d469 2 d498 2 @ 1.30 log @*** empty log message *** @ text @d308 1 a308 1 if (recv(pfd[i].fd, &data, 64, MSG_PEEK) == -1) { d346 2 a347 1 while ((rv = pth_sc(connect)(s, addr, addrlen)) == -1 && errno == EINTR) @ 1.29 log @*** empty log message *** @ text @d147 25 d205 2 a206 2 if (pth_time_cmp(timeout, PTH_TIME_ZERO)) return_errno(0, EINTR); d272 1 a272 1 maxfd = 0; d288 2 a297 1 continue; d502 1 a502 1 pid = pth_sc(waitpid)(wpid, status, options | WNOHANG); d504 1 a504 1 if ((pid != 0) || (options & WNOHANG)) d506 2 d514 2 a515 2 /* finally do the waitpid again for the result value and return it */ pid = pth_sc(waitpid)(wpid, status, options); @ 1.28 log @*** empty log message *** @ text @d144 1 d179 3 a181 2 if (rc == 0 && pth_time_cmp(timeout, PTH_TIME_ZERO)) return_errno(0, EINTR); d184 8 a191 5 ev_select = pth_event(PTH_EVENT_SELECT|PTH_MODE_STATIC, &ev_key_select, &rc, nfd, rfds, wfds, wfds); ev_timeout = pth_event(PTH_EVENT_TIME|PTH_MODE_STATIC, &ev_key_timeout, *timeout); ev = pth_event_concat(ev_select, ev_timeout, NULL); d195 1 d197 9 a205 5 pth_event_isolate(ev_timeout); if ( ev_extra != NULL && !pth_event_occurred(ev_select) && !pth_event_occurred(ev_timeout)) return_errno(-1, EINTR); @ 1.27 log @*** empty log message *** @ text @d199 97 @ 1.26 log @*** empty log message *** @ text @d168 1 a168 1 while ((rc = select(nfd, rtmp, wtmp, etmp, &delay)) < 0 && errno == EINTR) ; d200 1 a200 1 int pth_connect(int s, struct sockaddr *addr, int addrlen) d206 1 a206 1 int pth_connect_ev(int s, struct sockaddr *addr, int addrlen, pth_event_t ev_extra) d213 1 a213 1 while ((rv = connect(s, addr, addrlen)) == -1 && errno == EINTR) d251 1 a251 1 while ((rv = accept(s, addr, addrlen)) == -1 d293 1 a293 1 while ((n = select(fd+1, &fds, NULL, NULL, &delay)) < 0 d311 1 a311 1 while ((n = read(fd, buf, nbytes)) < 0 d338 1 a338 1 while ((n = select(fd+1, NULL, &fds, NULL, &delay)) < 0 d356 1 a356 1 while ((n = write(fd, buf, nbytes)) < 0 d369 1 a369 1 pid = waitpid(wpid, status, options | WNOHANG); d380 1 a380 1 pid = waitpid(wpid, status, options); @ 1.25 log @*** empty log message *** @ text @d125 74 @ 1.24 log @*** empty log message *** @ text @d2 1 a2 1 ** pth_high.c -- PTH high-level replacement functions d33 1 a33 1 /* PTH variant of usleep(3) */ d53 1 a53 1 /* PTH variant of sleep(3) */ d73 1 a73 1 /* PTH variant of POSIX pthread_sigmask(3) */ d77 1 a77 1 Because the PTH machine context switching is written to d82 1 a82 1 /* PTH variant of POSIX sigwait(3) */ d88 1 a88 1 /* PTH variant of POSIX sigwait(3) with extra events */ d125 1 a125 1 /* PTH variant of connect(2) */ d131 1 a131 1 /* PTH variant of connect(2) with extra events */ d162 1 a162 1 /* PTH variant of accept(2) */ d168 1 a168 1 /* PTH variant of accept(2) with extra events */ d197 1 a197 1 /* PTH variant of read(2) */ d203 1 a203 1 /* PTH variant of read(2) with extra event(s) */ d242 1 a242 1 /* PTH variant of write(2) */ d248 1 a248 1 /* PTH variant of write(2) with extra event(s) */ d287 1 a287 1 /* PTH variant of waitpid(2) */ @ 1.23 log @*** empty log message *** @ text @d6 2 a7 2 ** This file is part of PTH, a non-preemptive thread scheduling library ** which can be found at http://www.gnu.org/software/pth/. @ 1.22 log @*** empty log message *** @ text @a32 18 /* PTH variant of fork(2) to fork a thread into a new process */ pid_t pth_fork(void) { pid_t pid; pid = fork(); if (pid != 0) { /* parent has nothing to do. It just returns the child pid to the calling thread */ } else { /* child has to kick out all threads except for the currently running thread and the scheduler */ pth_scheduler_drop(); } return pid; } @ 1.21 log @*** empty log message *** @ text @d7 1 a7 1 ** which can be found at http://www.engelschall.com/sw/pth/. @ 1.20 log @*** empty log message *** @ text @d91 9 @ 1.19 log @*** empty log message *** @ text @d377 1 a377 1 rl = (readline_buf *)malloc(sizeof(readline_buf)); @ 1.18 log @*** empty log message *** @ text @d91 43 a293 27 } /* PTH variant of POSIX sigwait(3) */ int pth_sigwait(const sigset_t *set, int *sig) { pth_event_t ev; static pth_key_t ev_key = PTH_KEY_INIT; sigset_t pending; sigset_t omask; if (set == NULL || sig == NULL) return_errno(EINVAL, EINVAL); /* preserve current mask and block the signals on which to wait */ sigprocmask(SIG_BLOCK, set, &omask); /* determine currently pending signals */ if (sigpending(&pending) < 0) sigemptyset(&pending); /* create event and wait on it */ ev = pth_event(PTH_EVENT_SIGS|PTH_MODE_STATIC, &ev_key, set, sig); pth_wait(ev); /* cleanup and return */ sigprocmask(SIG_SETMASK, &omask, NULL); return 0; @ 1.17 log @*** empty log message *** @ text @d253 27 @ 1.16 log @*** empty log message *** @ text @d118 1 @ 1.15 log @*** empty log message *** @ text @d65 1 a65 1 ev = pth_event(PTH_EVENT_TIME|PTH_UNTIL_ELAPSED|PTH_MODE_STATIC, &ev_key, until); d85 1 a85 1 ev = pth_event(PTH_EVENT_TIME|PTH_UNTIL_ELAPSED|PTH_MODE_STATIC, &ev_key, until); d109 1 a109 1 ev = pth_event(PTH_EVENT_FD|PTH_UNTIL_WRITEABLE|PTH_MODE_STATIC, &ev_key, s); d146 1 a146 1 ev = pth_event(PTH_EVENT_FD|PTH_UNTIL_READABLE|PTH_MODE_STATIC, &ev_key, s); d190 1 a190 1 ev = pth_event(PTH_EVENT_FD|PTH_UNTIL_READABLE|PTH_MODE_STATIC, &ev_key, fd); d235 1 a235 1 ev = pth_event(PTH_EVENT_FD|PTH_UNTIL_WRITEABLE|PTH_MODE_STATIC, &ev_key, fd); d266 1 a266 1 ev = pth_event(PTH_EVENT_PID|PTH_UNTIL_AVAILABLE|PTH_MODE_STATIC, d358 1 a358 1 *cp = '\0'; @ 1.14 log @*** empty log message *** @ text @d92 1 a92 1 int pth_connect(int s, const struct sockaddr *addr, int addrlen) d98 1 a98 1 int pth_connect_ev(int s, const struct sockaddr *addr, int addrlen, pth_event_t ev_extra) @ 1.13 log @*** empty log message *** @ text @d20 1 a20 1 ** License along with this library; if not, write to the Free @ 1.12 log @*** empty log message *** @ text @d56 1 a56 1 pth_event_t ev_wait; d65 2 a66 2 ev_wait = pth_event(PTH_EVENT_TIME|PTH_UNTIL_ELAPSED|PTH_MODE_STATIC, &ev_key, until); pth_wait(&ev_wait, NULL); d76 1 a76 1 pth_event_t ev_wait; d85 2 a86 2 ev_wait = pth_event(PTH_EVENT_TIME|PTH_UNTIL_ELAPSED|PTH_MODE_STATIC, &ev_key, until); pth_wait(&ev_wait, NULL); d100 1 a100 1 pth_event_t ev_wait; d109 1 a109 1 ev_wait = pth_event(PTH_EVENT_FD|PTH_UNTIL_WRITEABLE|PTH_MODE_STATIC, &ev_key, s); d111 2 a112 2 pth_event_concat(ev_wait, ev_extra, NULL); pth_wait(&ev_wait, NULL); d114 2 a115 2 pth_event_isolate(ev_wait); if (!pth_event_occurred(ev_wait)) d136 1 a136 1 pth_event_t ev_wait; d141 1 a141 1 ev_wait = NULL; d145 2 a146 2 if (ev_wait == NULL) { ev_wait = pth_event(PTH_EVENT_FD|PTH_UNTIL_READABLE|PTH_MODE_STATIC, &ev_key, s); d148 1 a148 1 pth_event_concat(ev_wait, ev_extra, NULL); d151 1 a151 1 pth_wait(&ev_wait, NULL); d154 2 a155 2 pth_event_isolate(ev_wait); if (!pth_event_occurred(ev_wait)) d188 1 a188 1 let thread sleep until it is or timeout occurs */ d192 2 a193 2 ev = pth_event_concat(ev, ev_extra, NULL); pth_wait(&ev, NULL); d233 1 a233 1 let thread sleep until it is... */ d237 2 a238 2 ev = pth_event_concat(ev, ev_extra, NULL); pth_wait(&ev, NULL); d255 1 a255 1 pth_event_t ev_wait; d266 3 a268 3 ev_wait = pth_event(PTH_EVENT_PID|PTH_UNTIL_AVAILABLE|PTH_MODE_STATIC, &ev_key, wpid, status, options); pth_wait(&ev_wait, NULL); @ 1.11 log @*** empty log message *** @ text @a174 1 int b_extra; a188 1 b_extra = FALSE; d197 1 a197 1 b_extra = TRUE; a199 2 if (b_extra) return_errno(-1, EINTR); a219 1 int b_extra; a233 1 b_extra = FALSE; d242 1 a242 1 b_extra = TRUE; a244 2 if (b_extra) return_errno(-1, EINTR); @ 1.10 log @*** empty log message *** @ text @a90 35 /* PTH variant of accept(2) */ int pth_accept(int s, struct sockaddr *addr, int *addrlen) { return pth_accept_ev(s, addr, addrlen, NULL); } /* PTH variant of accept(2) with extra events */ int pth_accept_ev(int s, struct sockaddr *addr, int *addrlen, pth_event_t ev_extra) { pth_event_t ev_wait; static pth_key_t ev_key = PTH_KEY_INIT; int rv; /* poll socket via accept */ ev_wait = NULL; while ((rv = accept(s, addr, addrlen)) == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) { /* do lazy event allocation */ if (ev_wait == NULL) { ev_wait = pth_event(PTH_EVENT_FD|PTH_UNTIL_READABLE|PTH_MODE_STATIC, &ev_key, s); if (ev_extra != NULL) pth_event_concat(ev_wait, ev_extra, NULL); } /* wait until accept has a chance */ pth_wait(&ev_wait, NULL); /* check for the extra events */ if (ev_extra != NULL) { pth_event_isolate(ev_wait); if (!pth_event_occurred(ev_wait)) return_errno(-1, EINTR); } } return rv; } d123 35 @ 1.9 log @*** empty log message *** @ text @d94 6 d109 1 a109 1 if (ev_wait == NULL) d111 3 d116 42 d194 1 a194 1 ev = pth_event_concat(ev, ev_extra); d243 1 a243 1 ev = pth_event_concat(ev, ev_extra); @ 1.8 log @*** empty log message *** @ text @d57 1 d65 1 a65 1 ev_wait = pth_event(PTH_EVENT_TIME|PTH_UNTIL_ELAPSED, until); a66 1 pth_event_free(ev_wait, PTH_FREE_THIS); d77 1 d85 1 a85 1 ev_wait = pth_event(PTH_EVENT_TIME|PTH_UNTIL_ELAPSED, until); a86 1 pth_event_free(ev_wait, PTH_FREE_THIS); d95 1 d104 1 a104 1 ev_wait = pth_event(PTH_EVENT_FD|PTH_UNTIL_READABLE, s); a107 4 /* cleanup */ if (ev_wait != NULL) pth_event_free(ev_wait, PTH_FREE_THIS); d122 1 d141 1 a141 1 ev = pth_event(PTH_EVENT_FD|PTH_UNTIL_READABLE, fd); a149 1 pth_event_free(ev, PTH_FREE_THIS); d171 1 d190 1 a190 1 ev = pth_event(PTH_EVENT_FD|PTH_UNTIL_WRITEABLE, fd); a198 1 pth_event_free(ev, PTH_FREE_THIS); d213 1 a213 1 pth_event_t ev_done; d223 3 a225 3 ev_wait = pth_event(PTH_EVENT_PID|PTH_UNTIL_AVAILABLE, wpid, status, options); pth_wait(&ev_wait, &ev_done); pth_event_free(ev_wait, PTH_FREE_THIS); @ 1.7 log @*** empty log message *** @ text @d292 1 a292 1 if ((rl = pth_key_getdata(readline_key)) == NULL) { d298 1 a298 1 cp = buf; @ 1.6 log @*** empty log message *** @ text @d149 1 a149 1 if (!pth_event_occured(ev)) d198 1 a198 1 if (!pth_event_occured(ev)) d225 1 a225 1 /* else create a pid event and block current thread until it occured */ @ 1.5 log @*** empty log message *** @ text @d6 1 a6 1 ** This file is part of PTH, a non-preemtive thread scheduling library @ 1.4 log @*** empty log message *** @ text @d33 18 @ 1.3 log @*** empty log message *** @ text @a0 40 /* ==================================================================== * Copyright (c) 1999 Ralf S. Engelschall. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. All advertising materials mentioning features or use of this * software must display the following acknowledgment: * "This product includes software developed by * Ralf S. Engelschall ." * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by * Ralf S. Engelschall ." * * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * ==================================================================== */ d2 11 a12 2 ** Non-Preemtive Scheduler Library (PTH) ** pth_high.c -- high-level functions d14 9 a22 3 ** These functions used by the applications instead of the ** regular Unix/POSIX functions. When the regular functions would ** block these variants let only the tread sleep. d24 6 @ 1.2 log @*** empty log message *** @ text @a114 1 d118 6 d125 1 a125 1 pth_event_t ev_wait; d127 1 d141 2 a142 1 let thread sleep until it is... */ d144 10 a153 3 ev_wait = pth_event(PTH_EVENT_FD|PTH_UNTIL_READABLE, fd); pth_wait(&ev_wait, NULL); pth_event_free(ev_wait, PTH_FREE_THIS); d155 2 d167 6 d174 1 a174 1 pth_event_t ev_wait; d176 1 d191 1 d193 10 a202 3 ev_wait = pth_event(PTH_EVENT_FD|PTH_UNTIL_WRITEABLE, fd); pth_wait(&ev_wait, NULL); pth_event_free(ev_wait, PTH_FREE_THIS); d204 2 d264 1 a264 1 static ssize_t readline_read(readline_buf *rl, int fd, char *ptr) d268 1 a268 3 if ((rl->rl_cnt = pth_read(fd, rl->rl_buf, READLINE_MAXLEN)) < 0) { if (errno == EINTR) continue; a269 1 } d283 5 d293 1 a293 1 if ((rl = pth_getspecific(readline_key)) == NULL) { d297 1 a297 1 pth_setspecific(readline_key, rl); d301 1 a301 1 if ((rc = readline_read(rl, fd, &c)) == 1) { @ 1.1 log @*** empty log message *** @ text @d203 85 @