#define _GNU_SOURCE #include #define my_extern #include "io_internal.h" #undef my_extern #include "byte.h" #ifdef HAVE_SIGIO #include #include #endif #include #include #ifdef HAVE_KQUEUE #include #endif #ifdef HAVE_EPOLL #include #include #endif #include #ifdef HAVE_DEVPOLL #include #include #include #endif #ifdef __dietlibc__ #include #else #define __CAS(ptr,oldval,newval) __sync_val_compare_and_swap(ptr,oldval,newval) #endif #ifdef __MINGW32__ #include extern HANDLE io_comport; #endif iarray io_fds; static int io_fds_inited; uint64 io_wanted_fds; array io_pollfds; enum __io_waitmode io_waitmode; #if defined(HAVE_KQUEUE) || defined(HAVE_EPOLL) || defined(HAVE_DEVPOLL) int io_master; #endif #if defined(HAVE_SIGIO) int io_signum; sigset_t io_ss; #endif #if defined(HAVE_SIGIO) long alt_firstread, alt_firstwrite; long alt_curread, alt_curwrite; #endif /* put d on internal data structure, return 1 on success, 0 on error */ static io_entry* io_fd_internal(int64 d,int flags) { io_entry* e; #ifndef __MINGW32__ long r; if ((flags&(IO_FD_BLOCK|IO_FD_NONBLOCK))==0) { if ((r=fcntl(d,F_GETFL,0)) == -1) return 0; /* file descriptor not open */ } else if (flags&IO_FD_NONBLOCK) r=O_NDELAY; else r=0; #endif /* Problem: we might be the first to use io_fds. We need to make sure * we are the only ones to initialize it. So set io_fds_inited to 2 * and not to 1. We know we are done when it is 1. We know we need * to do something when it is 0. We know somebody else is doing it * when it is 2. */ if (__CAS(&io_fds_inited,0,2)==0) { iarray_init(&io_fds,sizeof(io_entry)); io_fds_inited=1; } else do { asm("" : : : "memory"); } while (io_fds_inited!=1); if (!(e=iarray_allocate(&io_fds,d))) return 0; if (e->inuse) return e; byte_zero(e,sizeof(io_entry)); e->inuse=1; e->next_defer=-1; #ifdef __MINGW32__ e->mh=0; #else if (r&O_NDELAY) e->nonblock=1; #endif e->next_read=e->next_write=-1; if (io_waitmode==UNDECIDED) { first_readable=first_writeable=-1; #if defined(HAVE_EPOLL) io_master=epoll_create(1000); if (io_master!=-1) io_waitmode=EPOLL; #endif #if defined(HAVE_KQUEUE) if (io_waitmode==UNDECIDED) { /* who knows, maybe someone supports both one day */ io_master=kqueue(); if (io_master!=-1) io_waitmode=KQUEUE; } #endif #if defined(HAVE_DEVPOLL) if (io_waitmode==UNDECIDED) { io_master=open("/dev/poll",O_RDWR); if (io_master!=-1) io_waitmode=DEVPOLL; } #endif #if defined(HAVE_SIGIO) alt_firstread=alt_firstwrite=alt_curread=alt_curwrite=-1; if (io_waitmode==UNDECIDED) { io_signum=SIGRTMIN+1; if (sigemptyset(&io_ss)==0 && sigaddset(&io_ss,io_signum)==0 && sigaddset(&io_ss,SIGIO)==0 && sigprocmask(SIG_BLOCK,&io_ss,0)==0) io_waitmode=_SIGIO; } #endif #ifdef __MINGW32__ io_comport=CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,0,0); if (io_comport) { io_waitmode=COMPLETIONPORT; fprintf(stderr,"Initialized completion port: %p\n",io_comport); } else { fprintf(stderr,"ARGH! Could not init completion port!\n"); errno=EINVAL; return 0; } #endif } #if defined(HAVE_SIGIO) if (io_waitmode==_SIGIO) { fcntl(d,F_SETOWN,getpid()); fcntl(d,F_SETSIG,io_signum); #if defined(O_ONESIGFD) && defined(F_SETAUXFL) fcntl(d, F_SETAUXFL, O_ONESIGFD); #endif fcntl(d,F_SETFL,fcntl(d,F_GETFL)|O_NONBLOCK|O_ASYNC); } #endif #ifdef __MINGW32__ if (io_comport) { fprintf(stderr,"Queueing %p at completion port %p...",d,io_comport); if (CreateIoCompletionPort((HANDLE)d,io_comport,(ULONG_PTR)d,0)==0) { fprintf(stderr," failed!\n"); errno=EBADF; return 0; } fprintf(stderr," OK!\n"); } #endif return e; } int io_fd(int64 d) { io_entry* e=io_fd_internal(d,0); return !!e; } int io_fd_canwrite(int64 d) { io_entry* e=io_fd_internal(d,0); if (e) e->canwrite=1; return !!e; } int io_fd_flags(int64 d,int flags) { io_entry* e=io_fd_internal(d,flags); if (e && (flags&IO_FD_CANWRITE)) e->canwrite=1; return !!e; }