io_wait.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656
  1. /*
  2. * $Id$
  3. *
  4. * Copyright (C) 2005 iptelorg GmbH
  5. *
  6. * Permission to use, copy, modify, and distribute this software for any
  7. * purpose with or without fee is hereby granted, provided that the above
  8. * copyright notice and this permission notice appear in all copies.
  9. *
  10. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  11. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  12. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  13. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  14. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  15. * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  16. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  17. */
  18. /*
  19. * tcp io wait common stuff used by tcp_main.c & tcp_read.c
  20. * (see io_wait.h)
  21. */
  22. /*
  23. * History:
  24. * --------
  25. * 2005-06-15 created by andrei
  26. * 2005-06-26 added kqueue (andrei)
  27. * 2005-07-04 added /dev/poll (andrei)
  28. */
  29. /*!
  30. * \file
  31. * \brief SIP-router core ::
  32. * \ingroup core
  33. * Module: \ref core
  34. */
  35. #ifndef NO_IO_WAIT
  36. #ifdef HAVE_EPOLL
  37. #include <unistd.h> /* close() */
  38. #endif
  39. #ifdef HAVE_DEVPOLL
  40. #include <sys/types.h> /* open */
  41. #include <sys/stat.h>
  42. #include <fcntl.h>
  43. #include <unistd.h> /* close, ioctl */
  44. #endif
  45. #include <stdlib.h> /* strtol() */
  46. #include "io_wait.h"
  47. #include "ut.h" /* get_sys_ver() */
  48. #include "mem/mem.h"
  49. #ifndef local_malloc
  50. #define local_malloc pkg_malloc
  51. #endif
  52. #ifndef local_free
  53. #define local_free pkg_free
  54. #endif
  55. char* poll_support="poll"
  56. #ifdef HAVE_EPOLL
  57. ", epoll_lt, epoll_et"
  58. #endif
  59. #ifdef HAVE_SIGIO_RT
  60. ", sigio_rt"
  61. #endif
  62. #ifdef HAVE_SELECT
  63. ", select"
  64. #endif
  65. #ifdef HAVE_KQUEUE
  66. ", kqueue"
  67. #endif
  68. #ifdef HAVE_DEVPOLL
  69. ", /dev/poll"
  70. #endif
  71. ;
  72. char* poll_method_str[POLL_END]={ "none", "poll", "epoll_lt", "epoll_et",
  73. "sigio_rt", "select", "kqueue", "/dev/poll"
  74. };
  75. int _os_ver=0; /* os version number */
  76. #ifdef HAVE_SIGIO_RT
  77. static int _sigio_init=0;
  78. static int _sigio_crt_rtsig;
  79. static sigset_t _sigio_rtsig_used;
  80. #endif
  81. #ifdef HAVE_SIGIO_RT
  82. /* sigio specific init
  83. * returns -1 on error, 0 on success */
  84. static int init_sigio(io_wait_h* h, int rsig)
  85. {
  86. int r;
  87. int n;
  88. int signo;
  89. int start_sig;
  90. sigset_t oldset;
  91. if (!_sigio_init){
  92. _sigio_init=1;
  93. _sigio_crt_rtsig=SIGRTMIN;
  94. sigemptyset(&_sigio_rtsig_used);
  95. }
  96. h->signo=0;
  97. if (rsig==0){
  98. start_sig=_sigio_crt_rtsig;
  99. n=SIGRTMAX-SIGRTMIN;
  100. }else{
  101. if ((rsig < SIGRTMIN) || (rsig >SIGRTMAX)){
  102. LOG(L_CRIT, "ERROR: init_sigio: real time signal %d out of"
  103. " range [%d, %d]\n", rsig, SIGRTMIN, SIGRTMAX);
  104. goto error;
  105. }
  106. start_sig=rsig;
  107. n=0;
  108. }
  109. sigemptyset(&h->sset);
  110. sigemptyset(&oldset);
  111. retry1:
  112. /* get current block mask */
  113. if (sigprocmask(SIG_BLOCK, &h->sset, &oldset )==-1){
  114. if (errno==EINTR) goto retry1;
  115. LOG(L_ERR, "ERROR: init_sigio: 1st sigprocmask failed: %s [%d]\n",
  116. strerror(errno), errno);
  117. /* try to continue */
  118. }
  119. for (r=start_sig; r<=(n+start_sig); r++){
  120. signo=(r>SIGRTMAX)?r-SIGRTMAX+SIGRTMIN:r;
  121. if (! sigismember(&_sigio_rtsig_used, signo) &&
  122. ! sigismember(&oldset, signo)){
  123. sigaddset(&_sigio_rtsig_used, signo);
  124. h->signo=signo;
  125. _sigio_crt_rtsig=(signo<SIGRTMAX)?signo+1:SIGRTMIN;
  126. break;
  127. }
  128. }
  129. if (h->signo==0){
  130. LOG(L_CRIT, "ERROR: init_sigio: %s\n",
  131. rsig?"could not assign requested real-time signal":
  132. "out of real-time signals");
  133. goto error;
  134. }
  135. DBG("init_sigio: trying signal %d... \n", h->signo);
  136. if (sigaddset(&h->sset, h->signo)==-1){
  137. LOG(L_ERR, "ERROR: init_sigio: sigaddset failed for %d: %s [%d]\n",
  138. h->signo, strerror(errno), errno);
  139. goto error;
  140. }
  141. if (sigaddset(&h->sset, SIGIO)==-1){
  142. LOG(L_ERR, "ERROR: init_sigio: sigaddset failed for %d: %s [%d]\n",
  143. SIGIO, strerror(errno), errno);
  144. goto error;
  145. }
  146. retry:
  147. if (sigprocmask(SIG_BLOCK, &h->sset, 0)==-1){
  148. if (errno==EINTR) goto retry;
  149. LOG(L_ERR, "ERROR: init_sigio: sigprocmask failed: %s [%d]\n",
  150. strerror(errno), errno);
  151. goto error;
  152. }
  153. return 0;
  154. error:
  155. h->signo=0;
  156. sigemptyset(&h->sset);
  157. return -1;
  158. }
  159. /* sigio specific destroy */
  160. static void destroy_sigio(io_wait_h* h)
  161. {
  162. if (h->signo){
  163. sigprocmask(SIG_UNBLOCK, &h->sset, 0);
  164. sigemptyset(&h->sset);
  165. sigdelset(&_sigio_rtsig_used, h->signo);
  166. h->signo=0;
  167. }
  168. }
  169. #endif
  170. #ifdef HAVE_EPOLL
  171. /* epoll specific init
  172. * returns -1 on error, 0 on success */
  173. static int init_epoll(io_wait_h* h)
  174. {
  175. again:
  176. h->epfd=epoll_create(h->max_fd_no);
  177. if (h->epfd==-1){
  178. if (errno==EINTR) goto again;
  179. LOG(L_ERR, "ERROR: init_epoll: epoll_create: %s [%d]\n",
  180. strerror(errno), errno);
  181. return -1;
  182. }
  183. return 0;
  184. }
  185. static void destroy_epoll(io_wait_h* h)
  186. {
  187. if (h->epfd!=-1){
  188. close(h->epfd);
  189. h->epfd=-1;
  190. }
  191. }
  192. #endif
  193. #ifdef HAVE_KQUEUE
  194. /* kqueue specific init
  195. * returns -1 on error, 0 on success */
  196. static int init_kqueue(io_wait_h* h)
  197. {
  198. again:
  199. h->kq_fd=kqueue();
  200. if (h->kq_fd==-1){
  201. if (errno==EINTR) goto again;
  202. LOG(L_ERR, "ERROR: init_kqueue: kqueue: %s [%d]\n",
  203. strerror(errno), errno);
  204. return -1;
  205. }
  206. return 0;
  207. }
  208. static void destroy_kqueue(io_wait_h* h)
  209. {
  210. if (h->kq_fd!=-1){
  211. close(h->kq_fd);
  212. h->kq_fd=-1;
  213. }
  214. }
  215. #endif
  216. #ifdef HAVE_DEVPOLL
  217. /* /dev/poll specific init
  218. * returns -1 on error, 0 on success */
  219. static int init_devpoll(io_wait_h* h)
  220. {
  221. again:
  222. h->dpoll_fd=open("/dev/poll", O_RDWR);
  223. if (h->dpoll_fd==-1){
  224. if (errno==EINTR) goto again;
  225. LOG(L_ERR, "ERROR: init_/dev/poll: open: %s [%d]\n",
  226. strerror(errno), errno);
  227. return -1;
  228. }
  229. return 0;
  230. }
  231. static void destroy_devpoll(io_wait_h* h)
  232. {
  233. if (h->dpoll_fd!=-1){
  234. close(h->dpoll_fd);
  235. h->dpoll_fd=-1;
  236. }
  237. }
  238. #endif
  239. #ifdef HAVE_SELECT
  240. static int init_select(io_wait_h* h)
  241. {
  242. FD_ZERO(&h->master_rset);
  243. FD_ZERO(&h->master_wset);
  244. return 0;
  245. }
  246. #endif
  247. /*
  248. * returns 0 on success, and an error message on error
  249. */
  250. char* check_poll_method(enum poll_types poll_method)
  251. {
  252. char* ret;
  253. ret=0;
  254. if (_os_ver==0)
  255. _os_ver=get_sys_version(0,0,0);
  256. switch(poll_method){
  257. case POLL_NONE:
  258. break;
  259. case POLL_POLL:
  260. /* always supported */
  261. break;
  262. case POLL_SELECT:
  263. /* should be always supported */
  264. #ifndef HAVE_SELECT
  265. ret="select not supported, try re-compiling with -DHAVE_SELECT";
  266. #endif
  267. break;
  268. case POLL_EPOLL_LT:
  269. case POLL_EPOLL_ET:
  270. #ifndef HAVE_EPOLL
  271. ret="epoll not supported, try re-compiling with -DHAVE_EPOLL";
  272. #else
  273. /* only on 2.6 + */
  274. if (_os_ver<0x020542) /* if ver < 2.5.66 */
  275. ret="epoll not supported on kernels < 2.6";
  276. #endif
  277. break;
  278. case POLL_SIGIO_RT:
  279. #ifndef HAVE_SIGIO_RT
  280. ret="sigio_rt not supported, try re-compiling with"
  281. " -DHAVE_SIGIO_RT";
  282. #else
  283. /* only on 2.2 + ?? */
  284. if (_os_ver<0x020200) /* if ver < 2.2.0 */
  285. ret="epoll not supported on kernels < 2.2 (?)";
  286. #endif
  287. break;
  288. case POLL_KQUEUE:
  289. #ifndef HAVE_KQUEUE
  290. ret="kqueue not supported, try re-compiling with -DHAVE_KQUEUE";
  291. #else
  292. /* only in FreeBSD 4.1, NETBSD 2.0, OpenBSD 2.9, Darwin, DragonFly */
  293. #ifdef __OS_freebsd
  294. /* all DragonFly versions have kqueque */
  295. #ifndef __OS_dragonfly
  296. if (_os_ver<0x0401) /* if ver < 4.1 */
  297. ret="kqueue not supported on FreeBSD < 4.1";
  298. #endif /* __OS_dragonfly */
  299. #elif defined (__OS_netbsd)
  300. if (_os_ver<0x020000) /* if ver < 2.0 */
  301. ret="kqueue not supported on NetBSD < 2.0";
  302. #elif defined (__OS_openbsd)
  303. if (_os_ver<0x0209) /* if ver < 2.9 ? */
  304. ret="kqueue not supported on OpenBSD < 2.9 (?)";
  305. #endif /* assume that the rest support kqueue ifdef HAVE_KQUEUE */
  306. #endif
  307. break;
  308. case POLL_DEVPOLL:
  309. #ifndef HAVE_DEVPOLL
  310. ret="/dev/poll not supported, try re-compiling with"
  311. " -DHAVE_DEVPOLL";
  312. #else
  313. /* only in Solaris >= 7.0 (?) */
  314. #ifdef __OS_solaris
  315. if (_os_ver<0x0507) /* ver < 5.7 */
  316. ret="/dev/poll not supported on Solaris < 7.0 (SunOS 5.7)";
  317. #endif
  318. #endif
  319. break;
  320. default:
  321. ret="unknown not supported method";
  322. }
  323. return ret;
  324. }
  325. enum poll_types choose_poll_method()
  326. {
  327. enum poll_types poll_method;
  328. if (_os_ver==0)
  329. _os_ver=get_sys_version(0,0,0);
  330. poll_method=0;
  331. #ifdef HAVE_EPOLL
  332. if (_os_ver>=0x020542) /* if ver >= 2.5.66 */
  333. poll_method=POLL_EPOLL_LT; /* or POLL_EPOLL_ET */
  334. #endif
  335. #ifdef HAVE_KQUEUE
  336. if (poll_method==0)
  337. /* only in FreeBSD 4.1, NETBSD 2.0, OpenBSD 2.9, Darwin, DragonFly */
  338. #ifdef __OS_freebsd
  339. /* all DragonFly versions have kqueque */
  340. #ifndef __OS_dragonfly
  341. if (_os_ver>=0x0401) /* if ver >= 4.1 */
  342. #endif /**__OS_dragonfly */
  343. #elif defined (__OS_netbsd)
  344. if (_os_ver>=0x020000) /* if ver >= 2.0 */
  345. #elif defined (__OS_openbsd)
  346. if (_os_ver>=0x0209) /* if ver >= 2.9 (?) */
  347. #endif /* assume that the rest support kqueue ifdef HAVE_KQUEUE */
  348. poll_method=POLL_KQUEUE;
  349. #endif
  350. #ifdef HAVE_DEVPOLL
  351. #ifdef __OS_solaris
  352. if (poll_method==0)
  353. /* only in Solaris >= 7.0 (?) */
  354. if (_os_ver>=0x0507) /* if ver >=SunOS 5.7 */
  355. poll_method=POLL_DEVPOLL;
  356. #endif
  357. #endif
  358. #ifdef HAVE_SIGIO_RT
  359. if (poll_method==0)
  360. if (_os_ver>=0x020200) /* if ver >= 2.2.0 */
  361. poll_method=POLL_SIGIO_RT;
  362. #endif
  363. if (poll_method==0) poll_method=POLL_POLL;
  364. return poll_method;
  365. }
  366. char* poll_method_name(enum poll_types poll_method)
  367. {
  368. if ((poll_method>=POLL_NONE) && (poll_method<POLL_END))
  369. return poll_method_str[poll_method];
  370. else
  371. return "invalid poll method";
  372. }
  373. /* converts a string into a poll_method
  374. * returns POLL_NONE (0) on error, else the corresponding poll type */
  375. enum poll_types get_poll_type(char* s)
  376. {
  377. int r;
  378. int l;
  379. l=strlen(s);
  380. for (r=POLL_END-1; r>POLL_NONE; r--)
  381. if ((strlen(poll_method_str[r])==l) &&
  382. (strncasecmp(poll_method_str[r], s, l)==0))
  383. break;
  384. return r;
  385. }
  386. /* initializes the static vars/arrays
  387. * params: h - pointer to the io_wait_h that will be initialized
  388. * max_fd - maximum allowed fd number
  389. * poll_m - poll method (0 for automatic best fit)
  390. */
  391. int init_io_wait(io_wait_h* h, int max_fd, enum poll_types poll_method)
  392. {
  393. char * poll_err;
  394. if (_os_ver==0) _os_ver=get_sys_version(0,0,0);
  395. memset(h, 0, sizeof(*h));
  396. h->max_fd_no=max_fd;
  397. #ifdef HAVE_EPOLL
  398. h->epfd=-1;
  399. #endif
  400. #ifdef HAVE_KQUEUE
  401. h->kq_fd=-1;
  402. #endif
  403. #ifdef HAVE_DEVPOLL
  404. h->dpoll_fd=-1;
  405. #endif
  406. poll_err=check_poll_method(poll_method);
  407. /* set an appropiate poll method */
  408. if (poll_err || (poll_method==0)){
  409. poll_method=choose_poll_method();
  410. if (poll_err){
  411. LOG(L_ERR, "ERROR: init_io_wait: %s, using %s instead\n",
  412. poll_err, poll_method_str[poll_method]);
  413. }else{
  414. LOG(L_INFO, "init_io_wait: using %s as the io watch method"
  415. " (auto detected)\n", poll_method_str[poll_method]);
  416. }
  417. }
  418. h->poll_method=poll_method;
  419. /* common stuff, everybody has fd_hash */
  420. h->fd_hash=local_malloc(sizeof(*(h->fd_hash))*h->max_fd_no);
  421. if (h->fd_hash==0){
  422. LOG(L_CRIT, "ERROR: init_io_wait: could not alloc"
  423. " fd hashtable (%ld bytes)\n",
  424. (long)sizeof(*(h->fd_hash))*h->max_fd_no );
  425. goto error;
  426. }
  427. memset((void*)h->fd_hash, 0, sizeof(*(h->fd_hash))*h->max_fd_no);
  428. switch(poll_method){
  429. case POLL_POLL:
  430. #ifdef HAVE_SELECT
  431. case POLL_SELECT:
  432. #endif
  433. #ifdef HAVE_SIGIO_RT
  434. case POLL_SIGIO_RT:
  435. #endif
  436. #ifdef HAVE_DEVPOLL
  437. case POLL_DEVPOLL:
  438. #endif
  439. h->fd_array=local_malloc(sizeof(*(h->fd_array))*h->max_fd_no);
  440. if (h->fd_array==0){
  441. LOG(L_CRIT, "ERROR: init_io_wait: could not"
  442. " alloc fd array (%ld bytes)\n",
  443. (long)sizeof(*(h->fd_hash))*h->max_fd_no);
  444. goto error;
  445. }
  446. memset((void*)h->fd_array, 0, sizeof(*(h->fd_array))*h->max_fd_no);
  447. #ifdef HAVE_SIGIO_RT
  448. if ((poll_method==POLL_SIGIO_RT) && (init_sigio(h, 0)<0)){
  449. LOG(L_CRIT, "ERROR: init_io_wait: sigio init failed\n");
  450. goto error;
  451. }
  452. #endif
  453. #ifdef HAVE_DEVPOLL
  454. if ((poll_method==POLL_DEVPOLL) && (init_devpoll(h)<0)){
  455. LOG(L_CRIT, "ERROR: init_io_wait: /dev/poll init failed\n");
  456. goto error;
  457. }
  458. #endif
  459. #ifdef HAVE_SELECT
  460. if ((poll_method==POLL_SELECT) && (init_select(h)<0)){
  461. LOG(L_CRIT, "ERROR: init_io_wait: select init failed\n");
  462. goto error;
  463. }
  464. #endif
  465. break;
  466. #ifdef HAVE_EPOLL
  467. case POLL_EPOLL_LT:
  468. case POLL_EPOLL_ET:
  469. h->ep_array=local_malloc(sizeof(*(h->ep_array))*h->max_fd_no);
  470. if (h->ep_array==0){
  471. LOG(L_CRIT, "ERROR: init_io_wait: could not alloc"
  472. " epoll array\n");
  473. goto error;
  474. }
  475. memset((void*)h->ep_array, 0, sizeof(*(h->ep_array))*h->max_fd_no);
  476. if (init_epoll(h)<0){
  477. LOG(L_CRIT, "ERROR: init_io_wait: epoll init failed\n");
  478. goto error;
  479. }
  480. break;
  481. #endif
  482. #ifdef HAVE_KQUEUE
  483. case POLL_KQUEUE:
  484. h->kq_changes_size=KQ_CHANGES_ARRAY_SIZE;
  485. /* kevent returns different events for read & write
  486. => to get all the possible events in one call we
  487. need twice the number of added fds + space
  488. for possible changelist errors.
  489. OTOH if memory is to be saved at all costs, one can
  490. decrease the array size.
  491. */
  492. h->kq_array_size=2 * h->max_fd_no + h->kq_changes_size;
  493. h->kq_array=local_malloc(sizeof(*(h->kq_array))*h->kq_array_size);
  494. if (h->kq_array==0){
  495. LOG(L_CRIT, "ERROR: init_io_wait: could not alloc"
  496. " kqueue event array\n");
  497. goto error;
  498. }
  499. h->kq_changes=local_malloc(sizeof(*(h->kq_changes))*
  500. h->kq_changes_size);
  501. if (h->kq_changes==0){
  502. LOG(L_CRIT, "ERROR: init_io_wait: could not alloc"
  503. " kqueue changes array\n");
  504. goto error;
  505. }
  506. h->kq_nchanges=0;
  507. memset((void*)h->kq_array, 0,
  508. sizeof(*(h->kq_array))*h->kq_array_size);
  509. memset((void*)h->kq_changes, 0,
  510. sizeof(*(h->kq_changes))* h->kq_changes_size);
  511. if (init_kqueue(h)<0){
  512. LOG(L_CRIT, "ERROR: init_io_wait: kqueue init failed\n");
  513. goto error;
  514. }
  515. break;
  516. #endif
  517. default:
  518. LOG(L_CRIT, "BUG: init_io_wait: unknown/unsupported poll"
  519. " method %s (%d)\n",
  520. poll_method_str[poll_method], poll_method);
  521. goto error;
  522. }
  523. return 0;
  524. error:
  525. return -1;
  526. }
  527. /* destroys everything init_io_wait allocated */
  528. void destroy_io_wait(io_wait_h* h)
  529. {
  530. switch(h->poll_method){
  531. #ifdef HAVE_EPOLL
  532. case POLL_EPOLL_LT:
  533. case POLL_EPOLL_ET:
  534. destroy_epoll(h);
  535. if (h->ep_array){
  536. local_free(h->ep_array);
  537. h->ep_array=0;
  538. }
  539. break;
  540. #endif
  541. #ifdef HAVE_KQUEUE
  542. case POLL_KQUEUE:
  543. destroy_kqueue(h);
  544. if (h->kq_array){
  545. local_free(h->kq_array);
  546. h->kq_array=0;
  547. }
  548. if (h->kq_changes){
  549. local_free(h->kq_changes);
  550. h->kq_changes=0;
  551. }
  552. break;
  553. #endif
  554. #ifdef HAVE_SIGIO_RT
  555. case POLL_SIGIO_RT:
  556. destroy_sigio(h);
  557. break;
  558. #endif
  559. #ifdef HAVE_DEVPOLL
  560. case POLL_DEVPOLL:
  561. destroy_devpoll(h);
  562. break;
  563. #endif
  564. default: /*do nothing*/
  565. ;
  566. }
  567. if (h->fd_array){
  568. local_free(h->fd_array);
  569. h->fd_array=0;
  570. }
  571. if (h->fd_hash){
  572. local_free(h->fd_hash);
  573. h->fd_hash=0;
  574. }
  575. }
  576. #endif /*ifndef NO_IO_WAIT */