├── AUTHORS ├── COPYING ├── Makefile ├── README.md ├── build.sh ├── c__src ├── CMakeLists.txt ├── GNUmakefile ├── ev.c ├── ev.h ├── ev_epoll.c ├── ev_kqueue.c ├── ev_poll.c ├── ev_port.c ├── ev_select.c ├── ev_vars.h ├── ev_win32.c ├── ev_wrap.h └── raw_socket.cpp ├── images ├── gen_socket_pollet_binding.PNG ├── gen_socket_pollset.PNG └── gen_tcp_pollset.PNG ├── rebar ├── rebar.config ├── src ├── gen_socket.app.src ├── gen_socket.erl ├── gen_socket.hrl ├── gen_socket_app.erl ├── gen_socket_poller.erl ├── gen_socket_sup.erl └── raw_socket.erl └── test ├── gensocket_test.erl ├── simple_client.erl └── simple_server.erl /AUTHORS: -------------------------------------------------------------------------------- 1 | Zunbao Feng 2 | Yifeng Xu 3 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | README for OTP_GEN_SOCKET 2 | ======================================================================== 3 | 4 | ## TABLE OF CONTENT ## 5 | 6 | * [Introduction](#introduction) 7 | * [Compile](#compile) 8 | * [Example](#example) 9 | * [Design](#Design) 10 | ## Introduction ## 11 | 12 | We all know gen_tcp is a standard network module in Erlang world. 13 | But Erlang vm supports only one PollSet which means there is at most one 14 | os thread handling events in the same time. 15 | The way only one PollSet in the vm is not scalable, especially with a rapid growth of NIC bandwidth and CPU cores on one single machine. 16 | 17 | In order to make full use of multi-core in Eralng network programming, we develop gen_socket module. 18 | The gen_socket is completely compatible with gen_tcp interface, but with amazing features: 19 | - PollSet per scheduler. Meaning the gen_sockete perfectly solved the problem there is at most one PollSet in erlang vm. 20 | - Socket binding policy. The gen_socket supports binding socket to specific scheduler and specific PollSet. Which reduces thread switchings and cache missing. 21 | 22 | According to our experiment, gen_socket increases throughput of echo server by **110%** on a 24-core, 1000Mb/s machine based on redhat6.2. 23 | 24 | OTP_GEN_SOCKET(v1.0) has already deployed in the Aliyun RDS production environment. 25 | OTP_GEN_SOCKET is released under GPLv2. 26 | 27 | ## Compile ## 28 | 29 | 1) git clone https://github.com/max-feng/otp_gen_socket.git 30 | 2) cd gen_socket && make 31 | 32 | ## Example ## 33 | ### server side ### 34 | * start gen_socket application 35 | 36 | max@max-gentoo ~/Code/gen_socket $ erl -pa ./ebin/ 37 | Erlang/OTP 17 [erts-6.4] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false] 38 | 39 | Eshell V6.4 (abort with ^G) 40 | 1> application:start(gen_socket). 41 | BindPoller=true, BindSocketPolicy=1 42 | 43 | * listen 44 | 45 | 2> {ok, L } = gen_socket:listen(8008, []). 46 | {ok,{140091975271824,<<>>}} 47 | 48 | * accept 49 | When client side connect to 8008, accept() will return a gen_socket: 50 | 51 | 3> {ok, S} = gen_socket:accept(L). 52 | {ok,{140091975272200,<<>>}} 53 | 54 | * send 55 | 56 | 4> gen_socket:send(S, <<"hello">>). 57 | ok 58 | 59 | ### client side ### 60 | * start gen_socket application 61 | 62 | max@max-gentoo ~/Code/gen_socket $ erl -pa ./ebin/ 63 | Erlang/OTP 17 [erts-6.4] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false] 64 | 65 | Eshell V6.4 (abort with ^G) 66 | 1> application:start(gen_socket). 67 | BindPoller=true, BindSocketPolicy=1 68 | 69 | * connect 70 | 71 | 2> {ok, S} = gen_socket:connect("127.0.0.1", 8008, []). 72 | {ok,{140532682066528,<<>>}} 73 | 74 | * recv 75 | 76 | 3> gen_socket:recv(S, 0). 77 | {ok,<<"hello">>} 78 | 79 | ## Design ## 80 | 81 | The gen_socket supports one PollSet per scheduler. The key concept is PollSet struct, Poller Process and Watcher Thread. 82 | PollSet struct is IO events affiliated point. PollSet is based on libev. 83 | Poller Process is an Erlang process which is executed in Erlang scheduler. It polls IO events from PollSet in nonblocking way. 84 | Watcher Thread is an os thread. When no IO events in PollSet, Watcher Thread will take over the right of polling the PollSet. 85 | 86 | ### Multi PollSet ### 87 | ### Binding ### 88 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | REBAR=./rebar 2 | all:clean compile 3 | 4 | compile: 5 | @$(REBAR) compile 6 | 7 | clean: 8 | @$(REBAR) clean 9 | @rm -f priv/*.so c__src/*.o .eunit 10 | 11 | 12 | eunit: clean_eunit 13 | @mkdir -p .eunit 14 | @mkdir -p .eunit/log 15 | @$(REBAR) skip_deps=true eunit 16 | 17 | clean_eunit: 18 | @rm -rf .eunit 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > README for erlang_multi_pollset 2 | ======================================================================== 3 | 4 | ## TABLE OF CONTENT ## 5 | 6 | * [Introduction](#introduction) 7 | * [Compile](#compile) 8 | * [Example0](#example0) 9 | * [Example1](#example1) 10 | * [Design](#Design) 11 | 12 | ## Introduction ## 13 | 14 | We all know gen_tcp is a standard network module in Erlang world. 15 | But Erlang vm supports only one PollSet which means there is at most one 16 | os thread handling events in the same time. 17 | 18 | The way only one PollSet in the vm is not scalable, especially with a rapid growth of NIC bandwidth and CPU cores on one single machine. 19 | 20 | In order to make full use of multi-core in Eralng network programming, we develop gen_socket module. 21 | The gen_socket is completely compatible with gen_tcp interface, but with amazing features: 22 | - PollSet per scheduler. Meaning the gen_sockete perfectly solved the problem there is at most one PollSet in erlang vm. 23 | - Socket binding policy. The gen_socket supports binding socket to specific scheduler and specific PollSet. Which reduces thread switchings and cache missing. 24 | 25 | According to our experiment, gen_socket increases throughput of echo server by **110%** on a 24-core, 1000Mb/s machine based on redhat6.2. 26 | 27 | gen_socket has already deployed in the Aliyun RDS production environment. 28 | gen_socket is released under GPLv2. 29 | 30 | ## Compile ## 31 | ``` 32 | git clone https://github.com/max-feng/erlang_multi_pollset.gi 33 | cd gen_socket && make 34 | ``` 35 | ## Example0 ## 36 | ### server side ### 37 | * start gen_socket application 38 | ``` 39 | max@max-gentoo ~/Code/gen_socket $ erl -pa ./ebin/ 40 | Erlang/OTP 17 [erts-6.4] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false] 41 | 42 | Eshell V6.4 (abort with ^G) 43 | 1> application:start(gen_socket). 44 | BindPoller=true, BindSocketPolicy=1 45 | ``` 46 | * listen 47 | ``` 48 | 2> {ok, L } = gen_socket:listen(8008, []). 49 | {ok,{140091975271824,<<>>}} 50 | ``` 51 | * accept 52 | When client side connect to 8008, accept() will return a gen_socket: 53 | ``` 54 | 3> {ok, S} = gen_socket:accept(L). 55 | {ok,{140091975272200,<<>>}} 56 | ``` 57 | * send 58 | ``` 59 | 4> gen_socket:send(S, <<"hello">>). 60 | ok 61 | ``` 62 | ### client side ### 63 | * start gen_socket application 64 | ``` 65 | max@max-gentoo ~/Code/gen_socket $ erl -pa ./ebin/ 66 | Erlang/OTP 17 [erts-6.4] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false] 67 | 68 | Eshell V6.4 (abort with ^G) 69 | 1> application:start(gen_socket). 70 | BindPoller=true, BindSocketPolicy=1 71 | ``` 72 | * connect 73 | ``` 74 | 2> {ok, S} = gen_socket:connect("127.0.0.1", 8008, []). 75 | {ok,{140532682066528,<<>>}} 76 | ``` 77 | * recv 78 | ``` 79 | 3> gen_socket:recv(S, 0). 80 | {ok,<<"hello">>} 81 | ``` 82 | 83 | ## Example1 ## 84 | I write a simple echo server and echo client under test folder. 85 | ### compile server and client ### 86 | ``` 87 | cd test/ && erlc *.erl 88 | ``` 89 | ### run server ### 90 | When a new connection established, echo server will spawn a process to echo msg. 91 | Echo server runs in active = false mode. 92 | 93 | ``` 94 | cd test && erl -pa ../ebin/ -noshell -s simple_server start 95 | ``` 96 | ### run client ### 97 | Echo client will connect to echo server, and send 5 <<"hello">>. 98 | Echo client runs in active = true mode. 99 | ``` 100 | cd test && erl -pa ../ebin/ -noshell -s simple_client start -s init stop 101 | ``` 102 | 103 | ## Design ## 104 | 105 | The gen_socket supports one PollSet per scheduler. The key concept is PollSet struct, Poller Process and Watcher Thread. 106 | 107 | PollSet struct is IO events's affiliated point. PollSet is based on libev. 108 | 109 | Poller Process is an Erlang process which is executed in Erlang scheduler. It polls IO events from PollSet in nonblocking way. 110 | 111 | Watcher Thread is an os thread. When there is no IO events in PollSet, Watcher Thread will take over the right of polling the PollSet. 112 | 113 | ### Current Architecture ### 114 | ![enter description here][1] 115 | 116 | In Erlang VM all schedulers are in follower/leader mode. There is at most one scheduler thread handing IO events. 117 | 118 | ### New Architecture ### 119 | #### Multi PollSet #### 120 | ![enter description here][2] 121 | 122 | In gen_socket module, there is a PollSet per scheduler. 123 | 124 | In above image, One PollSet has 2 Poller Process. 125 | 126 | Poller Process call evrun() in nonblocking way through Erlang nif interface. 127 | 128 | When evrun() return some IO events, callback will be processed in Poller Process. Callbck just ::recv() data, convert received data to Erlang binary, and send it to the socket's owner; 129 | When evrun() return zero IO events, Poller Process will give up the right to use PollSet, Watcher Thread will take over the right to use PollSet. Poller Process will be blocked on receive primitive. 130 | 131 | Watcher Thread call evrun() in blocking way. So when there is no IO events in PollSet, Watch Thread will be blocked and in interruptible state; When there are some new IO events coming, Watch Thread will be waked up, and send msg to Poller Process. Poller Process will enter into next loop. 132 | 133 | #### Binding #### 134 | ![enter description here][3] 135 | 136 | Process's socket managed by PollSet. 137 | When creating a new socket, gen_socket selects a minimum PollSet by a min-heap. And bind this socket to this PollSet, meanwhile bind this process to PollSet's scheduler. 138 | 139 | This binding way can reach the effect that an IO event handed by only one scheduler. 140 | 141 | 142 | [1]: ./images/gen_tcp_pollset.PNG "gen_tcp_pollset.PNG" 143 | [2]: ./images/gen_socket_pollset.PNG "gen_socket_pollset.PNG" 144 | [3]: ./images/gen_socket_pollet_binding.PNG "gen_socket_pollet_binding.PNG" 145 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | if [ ! -d priv ]; 4 | then 5 | mkdir priv 6 | fi 7 | 8 | cd c__src && make all && cd - 9 | -------------------------------------------------------------------------------- /c__src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 2.6) 2 | include(CheckFunctionExists) 3 | include(CheckIncludeFile) 4 | project(raw_socket CXX C ASM) 5 | 6 | macro (assert TEST COMMENT) 7 | if (NOT ${TEST}) 8 | message(FATAL_ERROR "Assertion failed: ${COMMENT}") 9 | endif () 10 | endmacro (assert) 11 | 12 | execute_process(COMMAND erl -eval "io:format(\"~s\", [lists:concat([code:root_dir(), \"/erts-\", erlang:system_info(version), \"/include\"])])" -s init stop -noshell OUTPUT_VARIABLE ERLANG_ROOT) 13 | 14 | include_directories(${ERLANG_ROOT}) 15 | message("Erlang root path: " ${ERLANG_ROOT}) 16 | 17 | find_path(LIBEV_INCLUDE_DIR 18 | NAMES ev.h 19 | PATHS /usr/include /usr/local/include) 20 | assert(LIBEV_INCLUDE_DIR "libev header file path not found") 21 | include_directories( ${LIBEV_INCLUDE_DIR}) 22 | find_library(LIBEV_LIB 23 | NAMES ev 24 | PATHS /usr/lib /usr/local/lib) 25 | assert(LIBEV_LIB "libev not found") 26 | 27 | set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} "-fpermissive") 28 | set(SOURCES raw_socket.cpp) 29 | add_library(raw_socket SHARED ${SOURCES}) 30 | set_target_properties(raw_socket PROPERTIES PREFIX "") 31 | set_target_properties(raw_socket PROPERTIES LIBRARY_OUTPUT_DIRECTORY 32 | "${PROJECT_SOURCE_DIR}/../priv") 33 | target_link_libraries(raw_socket ${LIBEV_LIB}) 34 | 35 | -------------------------------------------------------------------------------- /c__src/GNUmakefile: -------------------------------------------------------------------------------- 1 | ERLANG_PATH = $(shell erl -eval 'io:format("~s", [lists:concat([code:root_dir(), "/erts-", erlang:system_info(version), "/include"])])' -s init stop -noshell) 2 | NIF_INCLUDE:=-I./ -I$(ERLANG_PATH) 3 | 4 | CFLAGS = -g -Wall -O2 -fno-strict-aliasing -DEV_STANDALONE $(NIF_INCLUDE) 5 | 6 | ifneq ($(OS),Windows_NT) 7 | CFLAGS += -fPIC 8 | 9 | ifeq ($(shell uname),Darwin) 10 | LDFLAGS += -dynamiclib -undefined dynamic_lookup 11 | else 12 | LDFLAGS += -shared 13 | endif 14 | endif 15 | 16 | all: ../priv/raw_socket.so 17 | 18 | raw_socket.o : %.o: %.cpp 19 | c++ $(CFLAGS) $(LDFLAGS) -c $< 20 | 21 | ev.o: %.o: %.c 22 | cc $(CFLAGS) -Wno-unused-value -Wno-unused-function -c $< 23 | 24 | ../priv/raw_socket.so : raw_socket.o ev.o 25 | c++ $(CFLAGS) $(LDFLAGS) -o $@ $^ 26 | 27 | clean: 28 | rm -f *.o 29 | 30 | -------------------------------------------------------------------------------- /c__src/ev.h: -------------------------------------------------------------------------------- 1 | /* 2 | * libev native API header 3 | * 4 | * Copyright (c) 2007,2008,2009,2010,2011,2012,2015 Marc Alexander Lehmann 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without modifica- 8 | * tion, are permitted provided that the following conditions are met: 9 | * 10 | * 1. Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 18 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- 19 | * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 20 | * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- 21 | * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 23 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 24 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- 25 | * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 26 | * OF THE POSSIBILITY OF SUCH DAMAGE. 27 | * 28 | * Alternatively, the contents of this file may be used under the terms of 29 | * the GNU General Public License ("GPL") version 2 or any later version, 30 | * in which case the provisions of the GPL are applicable instead of 31 | * the above. If you wish to allow the use of your version of this file 32 | * only under the terms of the GPL and not to allow others to use your 33 | * version of this file under the BSD license, indicate your decision 34 | * by deleting the provisions above and replace them with the notice 35 | * and other provisions required by the GPL. If you do not delete the 36 | * provisions above, a recipient may use your version of this file under 37 | * either the BSD or the GPL. 38 | */ 39 | 40 | #ifndef EV_H_ 41 | #define EV_H_ 42 | 43 | #ifdef __cplusplus 44 | # define EV_CPP(x) x 45 | # if __cplusplus >= 201103L 46 | # define EV_THROW noexcept 47 | # else 48 | # define EV_THROW throw () 49 | # endif 50 | #else 51 | # define EV_CPP(x) 52 | # define EV_THROW 53 | #endif 54 | 55 | EV_CPP(extern "C" {) 56 | 57 | /*****************************************************************************/ 58 | 59 | /* pre-4.0 compatibility */ 60 | #ifndef EV_COMPAT3 61 | # define EV_COMPAT3 1 62 | #endif 63 | 64 | #ifndef EV_FEATURES 65 | # if defined __OPTIMIZE_SIZE__ 66 | # define EV_FEATURES 0x7c 67 | # else 68 | # define EV_FEATURES 0x7f 69 | # endif 70 | #endif 71 | 72 | #define EV_FEATURE_CODE ((EV_FEATURES) & 1) 73 | #define EV_FEATURE_DATA ((EV_FEATURES) & 2) 74 | #define EV_FEATURE_CONFIG ((EV_FEATURES) & 4) 75 | #define EV_FEATURE_API ((EV_FEATURES) & 8) 76 | #define EV_FEATURE_WATCHERS ((EV_FEATURES) & 16) 77 | #define EV_FEATURE_BACKENDS ((EV_FEATURES) & 32) 78 | #define EV_FEATURE_OS ((EV_FEATURES) & 64) 79 | 80 | /* these priorities are inclusive, higher priorities will be invoked earlier */ 81 | #ifndef EV_MINPRI 82 | # define EV_MINPRI (EV_FEATURE_CONFIG ? -2 : 0) 83 | #endif 84 | #ifndef EV_MAXPRI 85 | # define EV_MAXPRI (EV_FEATURE_CONFIG ? +2 : 0) 86 | #endif 87 | 88 | #ifndef EV_MULTIPLICITY 89 | # define EV_MULTIPLICITY EV_FEATURE_CONFIG 90 | #endif 91 | 92 | #ifndef EV_PERIODIC_ENABLE 93 | # define EV_PERIODIC_ENABLE EV_FEATURE_WATCHERS 94 | #endif 95 | 96 | #ifndef EV_STAT_ENABLE 97 | # define EV_STAT_ENABLE EV_FEATURE_WATCHERS 98 | #endif 99 | 100 | #ifndef EV_PREPARE_ENABLE 101 | # define EV_PREPARE_ENABLE EV_FEATURE_WATCHERS 102 | #endif 103 | 104 | #ifndef EV_CHECK_ENABLE 105 | # define EV_CHECK_ENABLE EV_FEATURE_WATCHERS 106 | #endif 107 | 108 | #ifndef EV_IDLE_ENABLE 109 | # define EV_IDLE_ENABLE EV_FEATURE_WATCHERS 110 | #endif 111 | 112 | #ifndef EV_FORK_ENABLE 113 | # define EV_FORK_ENABLE EV_FEATURE_WATCHERS 114 | #endif 115 | 116 | #ifndef EV_CLEANUP_ENABLE 117 | # define EV_CLEANUP_ENABLE EV_FEATURE_WATCHERS 118 | #endif 119 | 120 | #ifndef EV_SIGNAL_ENABLE 121 | # define EV_SIGNAL_ENABLE EV_FEATURE_WATCHERS 122 | #endif 123 | 124 | #ifndef EV_CHILD_ENABLE 125 | # ifdef _WIN32 126 | # define EV_CHILD_ENABLE 0 127 | # else 128 | # define EV_CHILD_ENABLE EV_FEATURE_WATCHERS 129 | #endif 130 | #endif 131 | 132 | #ifndef EV_ASYNC_ENABLE 133 | # define EV_ASYNC_ENABLE EV_FEATURE_WATCHERS 134 | #endif 135 | 136 | #ifndef EV_EMBED_ENABLE 137 | # define EV_EMBED_ENABLE EV_FEATURE_WATCHERS 138 | #endif 139 | 140 | #ifndef EV_WALK_ENABLE 141 | # define EV_WALK_ENABLE 0 /* not yet */ 142 | #endif 143 | 144 | /*****************************************************************************/ 145 | 146 | #if EV_CHILD_ENABLE && !EV_SIGNAL_ENABLE 147 | # undef EV_SIGNAL_ENABLE 148 | # define EV_SIGNAL_ENABLE 1 149 | #endif 150 | 151 | /*****************************************************************************/ 152 | 153 | typedef double ev_tstamp; 154 | 155 | #include /* for memmove */ 156 | 157 | #ifndef EV_ATOMIC_T 158 | # include 159 | # define EV_ATOMIC_T sig_atomic_t volatile 160 | #endif 161 | 162 | #if EV_STAT_ENABLE 163 | # ifdef _WIN32 164 | # include 165 | # include 166 | # endif 167 | # include 168 | #endif 169 | 170 | /* support multiple event loops? */ 171 | #if EV_MULTIPLICITY 172 | struct ev_loop; 173 | # define EV_P struct ev_loop *loop /* a loop as sole parameter in a declaration */ 174 | # define EV_P_ EV_P, /* a loop as first of multiple parameters */ 175 | # define EV_A loop /* a loop as sole argument to a function call */ 176 | # define EV_A_ EV_A, /* a loop as first of multiple arguments */ 177 | # define EV_DEFAULT_UC ev_default_loop_uc_ () /* the default loop, if initialised, as sole arg */ 178 | # define EV_DEFAULT_UC_ EV_DEFAULT_UC, /* the default loop as first of multiple arguments */ 179 | # define EV_DEFAULT ev_default_loop (0) /* the default loop as sole arg */ 180 | # define EV_DEFAULT_ EV_DEFAULT, /* the default loop as first of multiple arguments */ 181 | #else 182 | # define EV_P void 183 | # define EV_P_ 184 | # define EV_A 185 | # define EV_A_ 186 | # define EV_DEFAULT 187 | # define EV_DEFAULT_ 188 | # define EV_DEFAULT_UC 189 | # define EV_DEFAULT_UC_ 190 | # undef EV_EMBED_ENABLE 191 | #endif 192 | 193 | /* EV_INLINE is used for functions in header files */ 194 | #if __STDC_VERSION__ >= 199901L || __GNUC__ >= 3 195 | # define EV_INLINE static inline 196 | #else 197 | # define EV_INLINE static 198 | #endif 199 | 200 | #ifdef EV_API_STATIC 201 | # define EV_API_DECL static 202 | #else 203 | # define EV_API_DECL extern 204 | #endif 205 | 206 | /* EV_PROTOTYPES can be used to switch of prototype declarations */ 207 | #ifndef EV_PROTOTYPES 208 | # define EV_PROTOTYPES 1 209 | #endif 210 | 211 | /*****************************************************************************/ 212 | 213 | #define EV_VERSION_MAJOR 4 214 | #define EV_VERSION_MINOR 20 215 | 216 | /* eventmask, revents, events... */ 217 | enum { 218 | EV_UNDEF = (int)0xFFFFFFFF, /* guaranteed to be invalid */ 219 | EV_NONE = 0x00, /* no events */ 220 | EV_READ = 0x01, /* ev_io detected read will not block */ 221 | EV_WRITE = 0x02, /* ev_io detected write will not block */ 222 | EV__IOFDSET = 0x80, /* internal use only */ 223 | EV_IO = EV_READ, /* alias for type-detection */ 224 | EV_TIMER = 0x00000100, /* timer timed out */ 225 | #if EV_COMPAT3 226 | EV_TIMEOUT = EV_TIMER, /* pre 4.0 API compatibility */ 227 | #endif 228 | EV_PERIODIC = 0x00000200, /* periodic timer timed out */ 229 | EV_SIGNAL = 0x00000400, /* signal was received */ 230 | EV_CHILD = 0x00000800, /* child/pid had status change */ 231 | EV_STAT = 0x00001000, /* stat data changed */ 232 | EV_IDLE = 0x00002000, /* event loop is idling */ 233 | EV_PREPARE = 0x00004000, /* event loop about to poll */ 234 | EV_CHECK = 0x00008000, /* event loop finished poll */ 235 | EV_EMBED = 0x00010000, /* embedded event loop needs sweep */ 236 | EV_FORK = 0x00020000, /* event loop resumed in child */ 237 | EV_CLEANUP = 0x00040000, /* event loop resumed in child */ 238 | EV_ASYNC = 0x00080000, /* async intra-loop signal */ 239 | EV_CUSTOM = 0x01000000, /* for use by user code */ 240 | EV_ERROR = (int)0x80000000 /* sent when an error occurs */ 241 | }; 242 | 243 | /* can be used to add custom fields to all watchers, while losing binary compatibility */ 244 | #ifndef EV_COMMON 245 | # define EV_COMMON void *data; 246 | #endif 247 | 248 | #ifndef EV_CB_DECLARE 249 | # define EV_CB_DECLARE(type) void (*cb)(EV_P_ struct type *w, int revents); 250 | #endif 251 | #ifndef EV_CB_INVOKE 252 | # define EV_CB_INVOKE(watcher,revents) (watcher)->cb (EV_A_ (watcher), (revents)) 253 | #endif 254 | 255 | /* not official, do not use */ 256 | #define EV_CB(type,name) void name (EV_P_ struct ev_ ## type *w, int revents) 257 | 258 | /* 259 | * struct member types: 260 | * private: you may look at them, but not change them, 261 | * and they might not mean anything to you. 262 | * ro: can be read anytime, but only changed when the watcher isn't active. 263 | * rw: can be read and modified anytime, even when the watcher is active. 264 | * 265 | * some internal details that might be helpful for debugging: 266 | * 267 | * active is either 0, which means the watcher is not active, 268 | * or the array index of the watcher (periodics, timers) 269 | * or the array index + 1 (most other watchers) 270 | * or simply 1 for watchers that aren't in some array. 271 | * pending is either 0, in which case the watcher isn't, 272 | * or the array index + 1 in the pendings array. 273 | */ 274 | 275 | #if EV_MINPRI == EV_MAXPRI 276 | # define EV_DECL_PRIORITY 277 | #elif !defined (EV_DECL_PRIORITY) 278 | # define EV_DECL_PRIORITY int priority; 279 | #endif 280 | 281 | /* shared by all watchers */ 282 | #define EV_WATCHER(type) \ 283 | int active; /* private */ \ 284 | int pending; /* private */ \ 285 | EV_DECL_PRIORITY /* private */ \ 286 | EV_COMMON /* rw */ \ 287 | EV_CB_DECLARE (type) /* private */ 288 | 289 | #define EV_WATCHER_LIST(type) \ 290 | EV_WATCHER (type) \ 291 | struct ev_watcher_list *next; /* private */ 292 | 293 | #define EV_WATCHER_TIME(type) \ 294 | EV_WATCHER (type) \ 295 | ev_tstamp at; /* private */ 296 | 297 | /* base class, nothing to see here unless you subclass */ 298 | typedef struct ev_watcher 299 | { 300 | EV_WATCHER (ev_watcher) 301 | } ev_watcher; 302 | 303 | /* base class, nothing to see here unless you subclass */ 304 | typedef struct ev_watcher_list 305 | { 306 | EV_WATCHER_LIST (ev_watcher_list) 307 | } ev_watcher_list; 308 | 309 | /* base class, nothing to see here unless you subclass */ 310 | typedef struct ev_watcher_time 311 | { 312 | EV_WATCHER_TIME (ev_watcher_time) 313 | } ev_watcher_time; 314 | 315 | /* invoked when fd is either EV_READable or EV_WRITEable */ 316 | /* revent EV_READ, EV_WRITE */ 317 | typedef struct ev_io 318 | { 319 | EV_WATCHER_LIST (ev_io) 320 | 321 | int fd; /* ro */ 322 | int events; /* ro */ 323 | } ev_io; 324 | 325 | /* invoked after a specific time, repeatable (based on monotonic clock) */ 326 | /* revent EV_TIMEOUT */ 327 | typedef struct ev_timer 328 | { 329 | EV_WATCHER_TIME (ev_timer) 330 | 331 | ev_tstamp repeat; /* rw */ 332 | } ev_timer; 333 | 334 | /* invoked at some specific time, possibly repeating at regular intervals (based on UTC) */ 335 | /* revent EV_PERIODIC */ 336 | typedef struct ev_periodic 337 | { 338 | EV_WATCHER_TIME (ev_periodic) 339 | 340 | ev_tstamp offset; /* rw */ 341 | ev_tstamp interval; /* rw */ 342 | ev_tstamp (*reschedule_cb)(struct ev_periodic *w, ev_tstamp now) EV_THROW; /* rw */ 343 | } ev_periodic; 344 | 345 | /* invoked when the given signal has been received */ 346 | /* revent EV_SIGNAL */ 347 | typedef struct ev_signal 348 | { 349 | EV_WATCHER_LIST (ev_signal) 350 | 351 | int signum; /* ro */ 352 | } ev_signal; 353 | 354 | /* invoked when sigchld is received and waitpid indicates the given pid */ 355 | /* revent EV_CHILD */ 356 | /* does not support priorities */ 357 | typedef struct ev_child 358 | { 359 | EV_WATCHER_LIST (ev_child) 360 | 361 | int flags; /* private */ 362 | int pid; /* ro */ 363 | int rpid; /* rw, holds the received pid */ 364 | int rstatus; /* rw, holds the exit status, use the macros from sys/wait.h */ 365 | } ev_child; 366 | 367 | #if EV_STAT_ENABLE 368 | /* st_nlink = 0 means missing file or other error */ 369 | # ifdef _WIN32 370 | typedef struct _stati64 ev_statdata; 371 | # else 372 | typedef struct stat ev_statdata; 373 | # endif 374 | 375 | /* invoked each time the stat data changes for a given path */ 376 | /* revent EV_STAT */ 377 | typedef struct ev_stat 378 | { 379 | EV_WATCHER_LIST (ev_stat) 380 | 381 | ev_timer timer; /* private */ 382 | ev_tstamp interval; /* ro */ 383 | const char *path; /* ro */ 384 | ev_statdata prev; /* ro */ 385 | ev_statdata attr; /* ro */ 386 | 387 | int wd; /* wd for inotify, fd for kqueue */ 388 | } ev_stat; 389 | #endif 390 | 391 | #if EV_IDLE_ENABLE 392 | /* invoked when the nothing else needs to be done, keeps the process from blocking */ 393 | /* revent EV_IDLE */ 394 | typedef struct ev_idle 395 | { 396 | EV_WATCHER (ev_idle) 397 | } ev_idle; 398 | #endif 399 | 400 | /* invoked for each run of the mainloop, just before the blocking call */ 401 | /* you can still change events in any way you like */ 402 | /* revent EV_PREPARE */ 403 | typedef struct ev_prepare 404 | { 405 | EV_WATCHER (ev_prepare) 406 | } ev_prepare; 407 | 408 | /* invoked for each run of the mainloop, just after the blocking call */ 409 | /* revent EV_CHECK */ 410 | typedef struct ev_check 411 | { 412 | EV_WATCHER (ev_check) 413 | } ev_check; 414 | 415 | #if EV_FORK_ENABLE 416 | /* the callback gets invoked before check in the child process when a fork was detected */ 417 | /* revent EV_FORK */ 418 | typedef struct ev_fork 419 | { 420 | EV_WATCHER (ev_fork) 421 | } ev_fork; 422 | #endif 423 | 424 | #if EV_CLEANUP_ENABLE 425 | /* is invoked just before the loop gets destroyed */ 426 | /* revent EV_CLEANUP */ 427 | typedef struct ev_cleanup 428 | { 429 | EV_WATCHER (ev_cleanup) 430 | } ev_cleanup; 431 | #endif 432 | 433 | #if EV_EMBED_ENABLE 434 | /* used to embed an event loop inside another */ 435 | /* the callback gets invoked when the event loop has handled events, and can be 0 */ 436 | typedef struct ev_embed 437 | { 438 | EV_WATCHER (ev_embed) 439 | 440 | struct ev_loop *other; /* ro */ 441 | ev_io io; /* private */ 442 | ev_prepare prepare; /* private */ 443 | ev_check check; /* unused */ 444 | ev_timer timer; /* unused */ 445 | ev_periodic periodic; /* unused */ 446 | ev_idle idle; /* unused */ 447 | ev_fork fork; /* private */ 448 | #if EV_CLEANUP_ENABLE 449 | ev_cleanup cleanup; /* unused */ 450 | #endif 451 | } ev_embed; 452 | #endif 453 | 454 | #if EV_ASYNC_ENABLE 455 | /* invoked when somebody calls ev_async_send on the watcher */ 456 | /* revent EV_ASYNC */ 457 | typedef struct ev_async 458 | { 459 | EV_WATCHER (ev_async) 460 | 461 | EV_ATOMIC_T sent; /* private */ 462 | } ev_async; 463 | 464 | # define ev_async_pending(w) (+(w)->sent) 465 | #endif 466 | 467 | /* the presence of this union forces similar struct layout */ 468 | union ev_any_watcher 469 | { 470 | struct ev_watcher w; 471 | struct ev_watcher_list wl; 472 | 473 | struct ev_io io; 474 | struct ev_timer timer; 475 | struct ev_periodic periodic; 476 | struct ev_signal signal; 477 | struct ev_child child; 478 | #if EV_STAT_ENABLE 479 | struct ev_stat stat; 480 | #endif 481 | #if EV_IDLE_ENABLE 482 | struct ev_idle idle; 483 | #endif 484 | struct ev_prepare prepare; 485 | struct ev_check check; 486 | #if EV_FORK_ENABLE 487 | struct ev_fork fork; 488 | #endif 489 | #if EV_CLEANUP_ENABLE 490 | struct ev_cleanup cleanup; 491 | #endif 492 | #if EV_EMBED_ENABLE 493 | struct ev_embed embed; 494 | #endif 495 | #if EV_ASYNC_ENABLE 496 | struct ev_async async; 497 | #endif 498 | }; 499 | 500 | /* flag bits for ev_default_loop and ev_loop_new */ 501 | enum { 502 | /* the default */ 503 | EVFLAG_AUTO = 0x00000000U, /* not quite a mask */ 504 | /* flag bits */ 505 | EVFLAG_NOENV = 0x01000000U, /* do NOT consult environment */ 506 | EVFLAG_FORKCHECK = 0x02000000U, /* check for a fork in each iteration */ 507 | /* debugging/feature disable */ 508 | EVFLAG_NOINOTIFY = 0x00100000U, /* do not attempt to use inotify */ 509 | #if EV_COMPAT3 510 | EVFLAG_NOSIGFD = 0, /* compatibility to pre-3.9 */ 511 | #endif 512 | EVFLAG_SIGNALFD = 0x00200000U, /* attempt to use signalfd */ 513 | EVFLAG_NOSIGMASK = 0x00400000U /* avoid modifying the signal mask */ 514 | }; 515 | 516 | /* method bits to be ored together */ 517 | enum { 518 | EVBACKEND_SELECT = 0x00000001U, /* about anywhere */ 519 | EVBACKEND_POLL = 0x00000002U, /* !win */ 520 | EVBACKEND_EPOLL = 0x00000004U, /* linux */ 521 | EVBACKEND_KQUEUE = 0x00000008U, /* bsd */ 522 | EVBACKEND_DEVPOLL = 0x00000010U, /* solaris 8 */ /* NYI */ 523 | EVBACKEND_PORT = 0x00000020U, /* solaris 10 */ 524 | EVBACKEND_ALL = 0x0000003FU, /* all known backends */ 525 | EVBACKEND_MASK = 0x0000FFFFU /* all future backends */ 526 | }; 527 | 528 | #if EV_PROTOTYPES 529 | EV_API_DECL int ev_version_major (void) EV_THROW; 530 | EV_API_DECL int ev_version_minor (void) EV_THROW; 531 | 532 | EV_API_DECL unsigned int ev_supported_backends (void) EV_THROW; 533 | EV_API_DECL unsigned int ev_recommended_backends (void) EV_THROW; 534 | EV_API_DECL unsigned int ev_embeddable_backends (void) EV_THROW; 535 | 536 | EV_API_DECL ev_tstamp ev_time (void) EV_THROW; 537 | EV_API_DECL void ev_sleep (ev_tstamp delay) EV_THROW; /* sleep for a while */ 538 | 539 | /* Sets the allocation function to use, works like realloc. 540 | * It is used to allocate and free memory. 541 | * If it returns zero when memory needs to be allocated, the library might abort 542 | * or take some potentially destructive action. 543 | * The default is your system realloc function. 544 | */ 545 | EV_API_DECL void ev_set_allocator (void *(*cb)(void *ptr, long size) EV_THROW) EV_THROW; 546 | 547 | /* set the callback function to call on a 548 | * retryable syscall error 549 | * (such as failed select, poll, epoll_wait) 550 | */ 551 | EV_API_DECL void ev_set_syserr_cb (void (*cb)(const char *msg) EV_THROW) EV_THROW; 552 | 553 | #if EV_MULTIPLICITY 554 | 555 | /* the default loop is the only one that handles signals and child watchers */ 556 | /* you can call this as often as you like */ 557 | EV_API_DECL struct ev_loop *ev_default_loop (unsigned int flags EV_CPP (= 0)) EV_THROW; 558 | 559 | #ifdef EV_API_STATIC 560 | EV_API_DECL struct ev_loop *ev_default_loop_ptr; 561 | #endif 562 | 563 | EV_INLINE struct ev_loop * 564 | ev_default_loop_uc_ (void) EV_THROW 565 | { 566 | extern struct ev_loop *ev_default_loop_ptr; 567 | 568 | return ev_default_loop_ptr; 569 | } 570 | 571 | EV_INLINE int 572 | ev_is_default_loop (EV_P) EV_THROW 573 | { 574 | return EV_A == EV_DEFAULT_UC; 575 | } 576 | 577 | /* create and destroy alternative loops that don't handle signals */ 578 | EV_API_DECL struct ev_loop *ev_loop_new (unsigned int flags EV_CPP (= 0)) EV_THROW; 579 | 580 | EV_API_DECL ev_tstamp ev_now (EV_P) EV_THROW; /* time w.r.t. timers and the eventloop, updated after each poll */ 581 | 582 | #else 583 | 584 | EV_API_DECL int ev_default_loop (unsigned int flags EV_CPP (= 0)) EV_THROW; /* returns true when successful */ 585 | 586 | EV_API_DECL ev_tstamp ev_rt_now; 587 | 588 | EV_INLINE ev_tstamp 589 | ev_now (void) EV_THROW 590 | { 591 | return ev_rt_now; 592 | } 593 | 594 | /* looks weird, but ev_is_default_loop (EV_A) still works if this exists */ 595 | EV_INLINE int 596 | ev_is_default_loop (void) EV_THROW 597 | { 598 | return 1; 599 | } 600 | 601 | #endif /* multiplicity */ 602 | 603 | /* destroy event loops, also works for the default loop */ 604 | EV_API_DECL void ev_loop_destroy (EV_P); 605 | 606 | /* this needs to be called after fork, to duplicate the loop */ 607 | /* when you want to re-use it in the child */ 608 | /* you can call it in either the parent or the child */ 609 | /* you can actually call it at any time, anywhere :) */ 610 | EV_API_DECL void ev_loop_fork (EV_P) EV_THROW; 611 | 612 | EV_API_DECL unsigned int ev_backend (EV_P) EV_THROW; /* backend in use by loop */ 613 | 614 | EV_API_DECL void ev_now_update (EV_P) EV_THROW; /* update event loop time */ 615 | 616 | #if EV_WALK_ENABLE 617 | /* walk (almost) all watchers in the loop of a given type, invoking the */ 618 | /* callback on every such watcher. The callback might stop the watcher, */ 619 | /* but do nothing else with the loop */ 620 | EV_API_DECL void ev_walk (EV_P_ int types, void (*cb)(EV_P_ int type, void *w)) EV_THROW; 621 | #endif 622 | 623 | #endif /* prototypes */ 624 | 625 | /* ev_run flags values */ 626 | enum { 627 | EVRUN_NOWAIT = 1, /* do not block/wait */ 628 | EVRUN_ONCE = 2 /* block *once* only */ 629 | }; 630 | 631 | /* ev_break how values */ 632 | enum { 633 | EVBREAK_CANCEL = 0, /* undo unloop */ 634 | EVBREAK_ONE = 1, /* unloop once */ 635 | EVBREAK_ALL = 2 /* unloop all loops */ 636 | }; 637 | 638 | #if EV_PROTOTYPES 639 | EV_API_DECL int ev_run (EV_P_ int flags EV_CPP (= 0)); 640 | EV_API_DECL void ev_break (EV_P_ int how EV_CPP (= EVBREAK_ONE)) EV_THROW; /* break out of the loop */ 641 | 642 | /* 643 | * ref/unref can be used to add or remove a refcount on the mainloop. every watcher 644 | * keeps one reference. if you have a long-running watcher you never unregister that 645 | * should not keep ev_loop from running, unref() after starting, and ref() before stopping. 646 | */ 647 | EV_API_DECL void ev_ref (EV_P) EV_THROW; 648 | EV_API_DECL void ev_unref (EV_P) EV_THROW; 649 | 650 | /* 651 | * convenience function, wait for a single event, without registering an event watcher 652 | * if timeout is < 0, do wait indefinitely 653 | */ 654 | EV_API_DECL void ev_once (EV_P_ int fd, int events, ev_tstamp timeout, void (*cb)(int revents, void *arg), void *arg) EV_THROW; 655 | 656 | # if EV_FEATURE_API 657 | EV_API_DECL unsigned int ev_iteration (EV_P) EV_THROW; /* number of loop iterations */ 658 | EV_API_DECL unsigned int ev_depth (EV_P) EV_THROW; /* #ev_loop enters - #ev_loop leaves */ 659 | EV_API_DECL void ev_verify (EV_P) EV_THROW; /* abort if loop data corrupted */ 660 | 661 | EV_API_DECL void ev_set_io_collect_interval (EV_P_ ev_tstamp interval) EV_THROW; /* sleep at least this time, default 0 */ 662 | EV_API_DECL void ev_set_timeout_collect_interval (EV_P_ ev_tstamp interval) EV_THROW; /* sleep at least this time, default 0 */ 663 | 664 | /* advanced stuff for threading etc. support, see docs */ 665 | EV_API_DECL void ev_set_userdata (EV_P_ void *data) EV_THROW; 666 | EV_API_DECL void *ev_userdata (EV_P) EV_THROW; 667 | typedef void (*ev_loop_callback)(EV_P); 668 | EV_API_DECL void ev_set_invoke_pending_cb (EV_P_ ev_loop_callback invoke_pending_cb) EV_THROW; 669 | /* C++ doesn't allow the use of the ev_loop_callback typedef here, so we need to spell it out */ 670 | EV_API_DECL void ev_set_loop_release_cb (EV_P_ void (*release)(EV_P) EV_THROW, void (*acquire)(EV_P) EV_THROW) EV_THROW; 671 | 672 | EV_API_DECL unsigned int ev_pending_count (EV_P) EV_THROW; /* number of pending events, if any */ 673 | EV_API_DECL void ev_invoke_pending (EV_P); /* invoke all pending watchers */ 674 | 675 | /* 676 | * stop/start the timer handling. 677 | */ 678 | EV_API_DECL void ev_suspend (EV_P) EV_THROW; 679 | EV_API_DECL void ev_resume (EV_P) EV_THROW; 680 | #endif 681 | 682 | #endif 683 | 684 | /* these may evaluate ev multiple times, and the other arguments at most once */ 685 | /* either use ev_init + ev_TYPE_set, or the ev_TYPE_init macro, below, to first initialise a watcher */ 686 | #define ev_init(ev,cb_) do { \ 687 | ((ev_watcher *)(void *)(ev))->active = \ 688 | ((ev_watcher *)(void *)(ev))->pending = 0; \ 689 | ev_set_priority ((ev), 0); \ 690 | ev_set_cb ((ev), cb_); \ 691 | } while (0) 692 | 693 | #define ev_io_set(ev,fd_,events_) do { (ev)->fd = (fd_); (ev)->events = (events_) | EV__IOFDSET; } while (0) 694 | #define ev_timer_set(ev,after_,repeat_) do { ((ev_watcher_time *)(ev))->at = (after_); (ev)->repeat = (repeat_); } while (0) 695 | #define ev_periodic_set(ev,ofs_,ival_,rcb_) do { (ev)->offset = (ofs_); (ev)->interval = (ival_); (ev)->reschedule_cb = (rcb_); } while (0) 696 | #define ev_signal_set(ev,signum_) do { (ev)->signum = (signum_); } while (0) 697 | #define ev_child_set(ev,pid_,trace_) do { (ev)->pid = (pid_); (ev)->flags = !!(trace_); } while (0) 698 | #define ev_stat_set(ev,path_,interval_) do { (ev)->path = (path_); (ev)->interval = (interval_); (ev)->wd = -2; } while (0) 699 | #define ev_idle_set(ev) /* nop, yes, this is a serious in-joke */ 700 | #define ev_prepare_set(ev) /* nop, yes, this is a serious in-joke */ 701 | #define ev_check_set(ev) /* nop, yes, this is a serious in-joke */ 702 | #define ev_embed_set(ev,other_) do { (ev)->other = (other_); } while (0) 703 | #define ev_fork_set(ev) /* nop, yes, this is a serious in-joke */ 704 | #define ev_cleanup_set(ev) /* nop, yes, this is a serious in-joke */ 705 | #define ev_async_set(ev) /* nop, yes, this is a serious in-joke */ 706 | 707 | #define ev_io_init(ev,cb,fd,events) do { ev_init ((ev), (cb)); ev_io_set ((ev),(fd),(events)); } while (0) 708 | #define ev_timer_init(ev,cb,after,repeat) do { ev_init ((ev), (cb)); ev_timer_set ((ev),(after),(repeat)); } while (0) 709 | #define ev_periodic_init(ev,cb,ofs,ival,rcb) do { ev_init ((ev), (cb)); ev_periodic_set ((ev),(ofs),(ival),(rcb)); } while (0) 710 | #define ev_signal_init(ev,cb,signum) do { ev_init ((ev), (cb)); ev_signal_set ((ev), (signum)); } while (0) 711 | #define ev_child_init(ev,cb,pid,trace) do { ev_init ((ev), (cb)); ev_child_set ((ev),(pid),(trace)); } while (0) 712 | #define ev_stat_init(ev,cb,path,interval) do { ev_init ((ev), (cb)); ev_stat_set ((ev),(path),(interval)); } while (0) 713 | #define ev_idle_init(ev,cb) do { ev_init ((ev), (cb)); ev_idle_set ((ev)); } while (0) 714 | #define ev_prepare_init(ev,cb) do { ev_init ((ev), (cb)); ev_prepare_set ((ev)); } while (0) 715 | #define ev_check_init(ev,cb) do { ev_init ((ev), (cb)); ev_check_set ((ev)); } while (0) 716 | #define ev_embed_init(ev,cb,other) do { ev_init ((ev), (cb)); ev_embed_set ((ev),(other)); } while (0) 717 | #define ev_fork_init(ev,cb) do { ev_init ((ev), (cb)); ev_fork_set ((ev)); } while (0) 718 | #define ev_cleanup_init(ev,cb) do { ev_init ((ev), (cb)); ev_cleanup_set ((ev)); } while (0) 719 | #define ev_async_init(ev,cb) do { ev_init ((ev), (cb)); ev_async_set ((ev)); } while (0) 720 | 721 | #define ev_is_pending(ev) (0 + ((ev_watcher *)(void *)(ev))->pending) /* ro, true when watcher is waiting for callback invocation */ 722 | #define ev_is_active(ev) (0 + ((ev_watcher *)(void *)(ev))->active) /* ro, true when the watcher has been started */ 723 | 724 | #define ev_cb_(ev) (ev)->cb /* rw */ 725 | #define ev_cb(ev) (memmove (&ev_cb_ (ev), &((ev_watcher *)(ev))->cb, sizeof (ev_cb_ (ev))), (ev)->cb) 726 | 727 | #if EV_MINPRI == EV_MAXPRI 728 | # define ev_priority(ev) ((ev), EV_MINPRI) 729 | # define ev_set_priority(ev,pri) ((ev), (pri)) 730 | #else 731 | # define ev_priority(ev) (+(((ev_watcher *)(void *)(ev))->priority)) 732 | # define ev_set_priority(ev,pri) ( (ev_watcher *)(void *)(ev))->priority = (pri) 733 | #endif 734 | 735 | #define ev_periodic_at(ev) (+((ev_watcher_time *)(ev))->at) 736 | 737 | #ifndef ev_set_cb 738 | # define ev_set_cb(ev,cb_) (ev_cb_ (ev) = (cb_), memmove (&((ev_watcher *)(ev))->cb, &ev_cb_ (ev), sizeof (ev_cb_ (ev)))) 739 | #endif 740 | 741 | /* stopping (enabling, adding) a watcher does nothing if it is already running */ 742 | /* stopping (disabling, deleting) a watcher does nothing unless it's already running */ 743 | #if EV_PROTOTYPES 744 | 745 | /* feeds an event into a watcher as if the event actually occurred */ 746 | /* accepts any ev_watcher type */ 747 | EV_API_DECL void ev_feed_event (EV_P_ void *w, int revents) EV_THROW; 748 | EV_API_DECL void ev_feed_fd_event (EV_P_ int fd, int revents) EV_THROW; 749 | #if EV_SIGNAL_ENABLE 750 | EV_API_DECL void ev_feed_signal (int signum) EV_THROW; 751 | EV_API_DECL void ev_feed_signal_event (EV_P_ int signum) EV_THROW; 752 | #endif 753 | EV_API_DECL void ev_invoke (EV_P_ void *w, int revents); 754 | EV_API_DECL int ev_clear_pending (EV_P_ void *w) EV_THROW; 755 | 756 | EV_API_DECL void ev_io_start (EV_P_ ev_io *w) EV_THROW; 757 | EV_API_DECL void ev_io_stop (EV_P_ ev_io *w) EV_THROW; 758 | 759 | EV_API_DECL void ev_timer_start (EV_P_ ev_timer *w) EV_THROW; 760 | EV_API_DECL void ev_timer_stop (EV_P_ ev_timer *w) EV_THROW; 761 | /* stops if active and no repeat, restarts if active and repeating, starts if inactive and repeating */ 762 | EV_API_DECL void ev_timer_again (EV_P_ ev_timer *w) EV_THROW; 763 | /* return remaining time */ 764 | EV_API_DECL ev_tstamp ev_timer_remaining (EV_P_ ev_timer *w) EV_THROW; 765 | 766 | #if EV_PERIODIC_ENABLE 767 | EV_API_DECL void ev_periodic_start (EV_P_ ev_periodic *w) EV_THROW; 768 | EV_API_DECL void ev_periodic_stop (EV_P_ ev_periodic *w) EV_THROW; 769 | EV_API_DECL void ev_periodic_again (EV_P_ ev_periodic *w) EV_THROW; 770 | #endif 771 | 772 | /* only supported in the default loop */ 773 | #if EV_SIGNAL_ENABLE 774 | EV_API_DECL void ev_signal_start (EV_P_ ev_signal *w) EV_THROW; 775 | EV_API_DECL void ev_signal_stop (EV_P_ ev_signal *w) EV_THROW; 776 | #endif 777 | 778 | /* only supported in the default loop */ 779 | # if EV_CHILD_ENABLE 780 | EV_API_DECL void ev_child_start (EV_P_ ev_child *w) EV_THROW; 781 | EV_API_DECL void ev_child_stop (EV_P_ ev_child *w) EV_THROW; 782 | # endif 783 | 784 | # if EV_STAT_ENABLE 785 | EV_API_DECL void ev_stat_start (EV_P_ ev_stat *w) EV_THROW; 786 | EV_API_DECL void ev_stat_stop (EV_P_ ev_stat *w) EV_THROW; 787 | EV_API_DECL void ev_stat_stat (EV_P_ ev_stat *w) EV_THROW; 788 | # endif 789 | 790 | # if EV_IDLE_ENABLE 791 | EV_API_DECL void ev_idle_start (EV_P_ ev_idle *w) EV_THROW; 792 | EV_API_DECL void ev_idle_stop (EV_P_ ev_idle *w) EV_THROW; 793 | # endif 794 | 795 | #if EV_PREPARE_ENABLE 796 | EV_API_DECL void ev_prepare_start (EV_P_ ev_prepare *w) EV_THROW; 797 | EV_API_DECL void ev_prepare_stop (EV_P_ ev_prepare *w) EV_THROW; 798 | #endif 799 | 800 | #if EV_CHECK_ENABLE 801 | EV_API_DECL void ev_check_start (EV_P_ ev_check *w) EV_THROW; 802 | EV_API_DECL void ev_check_stop (EV_P_ ev_check *w) EV_THROW; 803 | #endif 804 | 805 | # if EV_FORK_ENABLE 806 | EV_API_DECL void ev_fork_start (EV_P_ ev_fork *w) EV_THROW; 807 | EV_API_DECL void ev_fork_stop (EV_P_ ev_fork *w) EV_THROW; 808 | # endif 809 | 810 | # if EV_CLEANUP_ENABLE 811 | EV_API_DECL void ev_cleanup_start (EV_P_ ev_cleanup *w) EV_THROW; 812 | EV_API_DECL void ev_cleanup_stop (EV_P_ ev_cleanup *w) EV_THROW; 813 | # endif 814 | 815 | # if EV_EMBED_ENABLE 816 | /* only supported when loop to be embedded is in fact embeddable */ 817 | EV_API_DECL void ev_embed_start (EV_P_ ev_embed *w) EV_THROW; 818 | EV_API_DECL void ev_embed_stop (EV_P_ ev_embed *w) EV_THROW; 819 | EV_API_DECL void ev_embed_sweep (EV_P_ ev_embed *w) EV_THROW; 820 | # endif 821 | 822 | # if EV_ASYNC_ENABLE 823 | EV_API_DECL void ev_async_start (EV_P_ ev_async *w) EV_THROW; 824 | EV_API_DECL void ev_async_stop (EV_P_ ev_async *w) EV_THROW; 825 | EV_API_DECL void ev_async_send (EV_P_ ev_async *w) EV_THROW; 826 | # endif 827 | 828 | #if EV_COMPAT3 829 | #define EVLOOP_NONBLOCK EVRUN_NOWAIT 830 | #define EVLOOP_ONESHOT EVRUN_ONCE 831 | #define EVUNLOOP_CANCEL EVBREAK_CANCEL 832 | #define EVUNLOOP_ONE EVBREAK_ONE 833 | #define EVUNLOOP_ALL EVBREAK_ALL 834 | #if EV_PROTOTYPES 835 | EV_INLINE void ev_loop (EV_P_ int flags) { ev_run (EV_A_ flags); } 836 | EV_INLINE void ev_unloop (EV_P_ int how ) { ev_break (EV_A_ how ); } 837 | EV_INLINE void ev_default_destroy (void) { ev_loop_destroy (EV_DEFAULT); } 838 | EV_INLINE void ev_default_fork (void) { ev_loop_fork (EV_DEFAULT); } 839 | #if EV_FEATURE_API 840 | EV_INLINE unsigned int ev_loop_count (EV_P) { return ev_iteration (EV_A); } 841 | EV_INLINE unsigned int ev_loop_depth (EV_P) { return ev_depth (EV_A); } 842 | EV_INLINE void ev_loop_verify (EV_P) { ev_verify (EV_A); } 843 | #endif 844 | #endif 845 | #else 846 | typedef struct ev_loop ev_loop; 847 | #endif 848 | 849 | #endif 850 | 851 | EV_CPP(}) 852 | 853 | #endif 854 | 855 | -------------------------------------------------------------------------------- /c__src/ev_epoll.c: -------------------------------------------------------------------------------- 1 | /* 2 | * libev epoll fd activity backend 3 | * 4 | * Copyright (c) 2007,2008,2009,2010,2011 Marc Alexander Lehmann 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without modifica- 8 | * tion, are permitted provided that the following conditions are met: 9 | * 10 | * 1. Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 18 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- 19 | * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 20 | * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- 21 | * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 23 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 24 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- 25 | * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 26 | * OF THE POSSIBILITY OF SUCH DAMAGE. 27 | * 28 | * Alternatively, the contents of this file may be used under the terms of 29 | * the GNU General Public License ("GPL") version 2 or any later version, 30 | * in which case the provisions of the GPL are applicable instead of 31 | * the above. If you wish to allow the use of your version of this file 32 | * only under the terms of the GPL and not to allow others to use your 33 | * version of this file under the BSD license, indicate your decision 34 | * by deleting the provisions above and replace them with the notice 35 | * and other provisions required by the GPL. If you do not delete the 36 | * provisions above, a recipient may use your version of this file under 37 | * either the BSD or the GPL. 38 | */ 39 | 40 | /* 41 | * general notes about epoll: 42 | * 43 | * a) epoll silently removes fds from the fd set. as nothing tells us 44 | * that an fd has been removed otherwise, we have to continually 45 | * "rearm" fds that we suspect *might* have changed (same 46 | * problem with kqueue, but much less costly there). 47 | * b) the fact that ADD != MOD creates a lot of extra syscalls due to a) 48 | * and seems not to have any advantage. 49 | * c) the inability to handle fork or file descriptors (think dup) 50 | * limits the applicability over poll, so this is not a generic 51 | * poll replacement. 52 | * d) epoll doesn't work the same as select with many file descriptors 53 | * (such as files). while not critical, no other advanced interface 54 | * seems to share this (rather non-unixy) limitation. 55 | * e) epoll claims to be embeddable, but in practise you never get 56 | * a ready event for the epoll fd (broken: <=2.6.26, working: >=2.6.32). 57 | * f) epoll_ctl returning EPERM means the fd is always ready. 58 | * 59 | * lots of "weird code" and complication handling in this file is due 60 | * to these design problems with epoll, as we try very hard to avoid 61 | * epoll_ctl syscalls for common usage patterns and handle the breakage 62 | * ensuing from receiving events for closed and otherwise long gone 63 | * file descriptors. 64 | */ 65 | 66 | #include 67 | 68 | #define EV_EMASK_EPERM 0x80 69 | 70 | static void 71 | epoll_modify (EV_P_ int fd, int oev, int nev) 72 | { 73 | struct epoll_event ev; 74 | unsigned char oldmask; 75 | 76 | /* 77 | * we handle EPOLL_CTL_DEL by ignoring it here 78 | * on the assumption that the fd is gone anyways 79 | * if that is wrong, we have to handle the spurious 80 | * event in epoll_poll. 81 | * if the fd is added again, we try to ADD it, and, if that 82 | * fails, we assume it still has the same eventmask. 83 | */ 84 | if (!nev) 85 | return; 86 | 87 | oldmask = anfds [fd].emask; 88 | anfds [fd].emask = nev; 89 | 90 | /* store the generation counter in the upper 32 bits, the fd in the lower 32 bits */ 91 | ev.data.u64 = (uint64_t)(uint32_t)fd 92 | | ((uint64_t)(uint32_t)++anfds [fd].egen << 32); 93 | ev.events = (nev & EV_READ ? EPOLLIN : 0) 94 | | (nev & EV_WRITE ? EPOLLOUT : 0); 95 | 96 | if (expect_true (!epoll_ctl (backend_fd, oev && oldmask != nev ? EPOLL_CTL_MOD : EPOLL_CTL_ADD, fd, &ev))) 97 | return; 98 | 99 | if (expect_true (errno == ENOENT)) 100 | { 101 | /* if ENOENT then the fd went away, so try to do the right thing */ 102 | if (!nev) 103 | goto dec_egen; 104 | 105 | if (!epoll_ctl (backend_fd, EPOLL_CTL_ADD, fd, &ev)) 106 | return; 107 | } 108 | else if (expect_true (errno == EEXIST)) 109 | { 110 | /* EEXIST means we ignored a previous DEL, but the fd is still active */ 111 | /* if the kernel mask is the same as the new mask, we assume it hasn't changed */ 112 | if (oldmask == nev) 113 | goto dec_egen; 114 | 115 | if (!epoll_ctl (backend_fd, EPOLL_CTL_MOD, fd, &ev)) 116 | return; 117 | } 118 | else if (expect_true (errno == EPERM)) 119 | { 120 | /* EPERM means the fd is always ready, but epoll is too snobbish */ 121 | /* to handle it, unlike select or poll. */ 122 | anfds [fd].emask = EV_EMASK_EPERM; 123 | 124 | /* add fd to epoll_eperms, if not already inside */ 125 | if (!(oldmask & EV_EMASK_EPERM)) 126 | { 127 | array_needsize (int, epoll_eperms, epoll_epermmax, epoll_epermcnt + 1, EMPTY2); 128 | epoll_eperms [epoll_epermcnt++] = fd; 129 | } 130 | 131 | return; 132 | } 133 | 134 | fd_kill (EV_A_ fd); 135 | 136 | dec_egen: 137 | /* we didn't successfully call epoll_ctl, so decrement the generation counter again */ 138 | --anfds [fd].egen; 139 | } 140 | 141 | static void 142 | epoll_poll (EV_P_ ev_tstamp timeout) 143 | { 144 | int i; 145 | int eventcnt; 146 | 147 | if (expect_false (epoll_epermcnt)) 148 | timeout = 0.; 149 | 150 | /* epoll wait times cannot be larger than (LONG_MAX - 999UL) / HZ msecs, which is below */ 151 | /* the default libev max wait time, however. */ 152 | EV_RELEASE_CB; 153 | eventcnt = epoll_wait (backend_fd, epoll_events, epoll_eventmax, timeout * 1e3); 154 | EV_ACQUIRE_CB; 155 | 156 | if (expect_false (eventcnt < 0)) 157 | { 158 | if (errno != EINTR) 159 | ev_syserr ("(libev) epoll_wait"); 160 | 161 | return; 162 | } 163 | 164 | for (i = 0; i < eventcnt; ++i) 165 | { 166 | struct epoll_event *ev = epoll_events + i; 167 | 168 | int fd = (uint32_t)ev->data.u64; /* mask out the lower 32 bits */ 169 | int want = anfds [fd].events; 170 | int got = (ev->events & (EPOLLOUT | EPOLLERR | EPOLLHUP) ? EV_WRITE : 0) 171 | | (ev->events & (EPOLLIN | EPOLLERR | EPOLLHUP) ? EV_READ : 0); 172 | 173 | /* 174 | * check for spurious notification. 175 | * this only finds spurious notifications on egen updates 176 | * other spurious notifications will be found by epoll_ctl, below 177 | * we assume that fd is always in range, as we never shrink the anfds array 178 | */ 179 | if (expect_false ((uint32_t)anfds [fd].egen != (uint32_t)(ev->data.u64 >> 32))) 180 | { 181 | /* recreate kernel state */ 182 | postfork = 1; 183 | continue; 184 | } 185 | 186 | if (expect_false (got & ~want)) 187 | { 188 | anfds [fd].emask = want; 189 | 190 | /* 191 | * we received an event but are not interested in it, try mod or del 192 | * this often happens because we optimistically do not unregister fds 193 | * when we are no longer interested in them, but also when we get spurious 194 | * notifications for fds from another process. this is partially handled 195 | * above with the gencounter check (== our fd is not the event fd), and 196 | * partially here, when epoll_ctl returns an error (== a child has the fd 197 | * but we closed it). 198 | */ 199 | ev->events = (want & EV_READ ? EPOLLIN : 0) 200 | | (want & EV_WRITE ? EPOLLOUT : 0); 201 | 202 | /* pre-2.6.9 kernels require a non-null pointer with EPOLL_CTL_DEL, */ 203 | /* which is fortunately easy to do for us. */ 204 | if (epoll_ctl (backend_fd, want ? EPOLL_CTL_MOD : EPOLL_CTL_DEL, fd, ev)) 205 | { 206 | postfork = 1; /* an error occurred, recreate kernel state */ 207 | continue; 208 | } 209 | } 210 | 211 | fd_event (EV_A_ fd, got); 212 | } 213 | 214 | /* if the receive array was full, increase its size */ 215 | if (expect_false (eventcnt == epoll_eventmax)) 216 | { 217 | ev_free (epoll_events); 218 | epoll_eventmax = array_nextsize (sizeof (struct epoll_event), epoll_eventmax, epoll_eventmax + 1); 219 | epoll_events = (struct epoll_event *)ev_malloc (sizeof (struct epoll_event) * epoll_eventmax); 220 | } 221 | 222 | /* now synthesize events for all fds where epoll fails, while select works... */ 223 | for (i = epoll_epermcnt; i--; ) 224 | { 225 | int fd = epoll_eperms [i]; 226 | unsigned char events = anfds [fd].events & (EV_READ | EV_WRITE); 227 | 228 | if (anfds [fd].emask & EV_EMASK_EPERM && events) 229 | fd_event (EV_A_ fd, events); 230 | else 231 | { 232 | epoll_eperms [i] = epoll_eperms [--epoll_epermcnt]; 233 | anfds [fd].emask = 0; 234 | } 235 | } 236 | } 237 | 238 | int inline_size 239 | epoll_init (EV_P_ int flags) 240 | { 241 | #ifdef EPOLL_CLOEXEC 242 | backend_fd = epoll_create1 (EPOLL_CLOEXEC); 243 | 244 | if (backend_fd < 0 && (errno == EINVAL || errno == ENOSYS)) 245 | #endif 246 | backend_fd = epoll_create (256); 247 | 248 | if (backend_fd < 0) 249 | return 0; 250 | 251 | fcntl (backend_fd, F_SETFD, FD_CLOEXEC); 252 | 253 | backend_mintime = 1e-3; /* epoll does sometimes return early, this is just to avoid the worst */ 254 | backend_modify = epoll_modify; 255 | backend_poll = epoll_poll; 256 | 257 | epoll_eventmax = 64; /* initial number of events receivable per poll */ 258 | epoll_events = (struct epoll_event *)ev_malloc (sizeof (struct epoll_event) * epoll_eventmax); 259 | 260 | return EVBACKEND_EPOLL; 261 | } 262 | 263 | void inline_size 264 | epoll_destroy (EV_P) 265 | { 266 | ev_free (epoll_events); 267 | array_free (epoll_eperm, EMPTY); 268 | } 269 | 270 | void inline_size 271 | epoll_fork (EV_P) 272 | { 273 | close (backend_fd); 274 | 275 | while ((backend_fd = epoll_create (256)) < 0) 276 | ev_syserr ("(libev) epoll_create"); 277 | 278 | fcntl (backend_fd, F_SETFD, FD_CLOEXEC); 279 | 280 | fd_rearm_all (EV_A); 281 | } 282 | 283 | -------------------------------------------------------------------------------- /c__src/ev_kqueue.c: -------------------------------------------------------------------------------- 1 | /* 2 | * libev kqueue backend 3 | * 4 | * Copyright (c) 2007,2008,2009,2010,2011,2012,2013 Marc Alexander Lehmann 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without modifica- 8 | * tion, are permitted provided that the following conditions are met: 9 | * 10 | * 1. Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 18 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- 19 | * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 20 | * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- 21 | * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 23 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 24 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- 25 | * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 26 | * OF THE POSSIBILITY OF SUCH DAMAGE. 27 | * 28 | * Alternatively, the contents of this file may be used under the terms of 29 | * the GNU General Public License ("GPL") version 2 or any later version, 30 | * in which case the provisions of the GPL are applicable instead of 31 | * the above. If you wish to allow the use of your version of this file 32 | * only under the terms of the GPL and not to allow others to use your 33 | * version of this file under the BSD license, indicate your decision 34 | * by deleting the provisions above and replace them with the notice 35 | * and other provisions required by the GPL. If you do not delete the 36 | * provisions above, a recipient may use your version of this file under 37 | * either the BSD or the GPL. 38 | */ 39 | 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | 46 | void inline_speed 47 | kqueue_change (EV_P_ int fd, int filter, int flags, int fflags) 48 | { 49 | ++kqueue_changecnt; 50 | array_needsize (struct kevent, kqueue_changes, kqueue_changemax, kqueue_changecnt, EMPTY2); 51 | 52 | EV_SET (&kqueue_changes [kqueue_changecnt - 1], fd, filter, flags, fflags, 0, 0); 53 | } 54 | 55 | /* OS X at least needs this */ 56 | #ifndef EV_ENABLE 57 | # define EV_ENABLE 0 58 | #endif 59 | #ifndef NOTE_EOF 60 | # define NOTE_EOF 0 61 | #endif 62 | 63 | static void 64 | kqueue_modify (EV_P_ int fd, int oev, int nev) 65 | { 66 | if (oev != nev) 67 | { 68 | if (oev & EV_READ) 69 | kqueue_change (EV_A_ fd, EVFILT_READ , EV_DELETE, 0); 70 | 71 | if (oev & EV_WRITE) 72 | kqueue_change (EV_A_ fd, EVFILT_WRITE, EV_DELETE, 0); 73 | } 74 | 75 | /* to detect close/reopen reliably, we have to re-add */ 76 | /* event requests even when oev == nev */ 77 | 78 | if (nev & EV_READ) 79 | kqueue_change (EV_A_ fd, EVFILT_READ , EV_ADD | EV_ENABLE, NOTE_EOF); 80 | 81 | if (nev & EV_WRITE) 82 | kqueue_change (EV_A_ fd, EVFILT_WRITE, EV_ADD | EV_ENABLE, NOTE_EOF); 83 | } 84 | 85 | static void 86 | kqueue_poll (EV_P_ ev_tstamp timeout) 87 | { 88 | int res, i; 89 | struct timespec ts; 90 | 91 | /* need to resize so there is enough space for errors */ 92 | if (kqueue_changecnt > kqueue_eventmax) 93 | { 94 | ev_free (kqueue_events); 95 | kqueue_eventmax = array_nextsize (sizeof (struct kevent), kqueue_eventmax, kqueue_changecnt); 96 | kqueue_events = (struct kevent *)ev_malloc (sizeof (struct kevent) * kqueue_eventmax); 97 | } 98 | 99 | EV_RELEASE_CB; 100 | EV_TS_SET (ts, timeout); 101 | res = kevent (backend_fd, kqueue_changes, kqueue_changecnt, kqueue_events, kqueue_eventmax, &ts); 102 | EV_ACQUIRE_CB; 103 | kqueue_changecnt = 0; 104 | 105 | if (expect_false (res < 0)) 106 | { 107 | if (errno != EINTR) 108 | ev_syserr ("(libev) kevent"); 109 | 110 | return; 111 | } 112 | 113 | for (i = 0; i < res; ++i) 114 | { 115 | int fd = kqueue_events [i].ident; 116 | 117 | if (expect_false (kqueue_events [i].flags & EV_ERROR)) 118 | { 119 | int err = kqueue_events [i].data; 120 | 121 | /* we are only interested in errors for fds that we are interested in :) */ 122 | if (anfds [fd].events) 123 | { 124 | if (err == ENOENT) /* resubmit changes on ENOENT */ 125 | kqueue_modify (EV_A_ fd, 0, anfds [fd].events); 126 | else if (err == EBADF) /* on EBADF, we re-check the fd */ 127 | { 128 | if (fd_valid (fd)) 129 | kqueue_modify (EV_A_ fd, 0, anfds [fd].events); 130 | else 131 | fd_kill (EV_A_ fd); 132 | } 133 | else /* on all other errors, we error out on the fd */ 134 | fd_kill (EV_A_ fd); 135 | } 136 | } 137 | else 138 | fd_event ( 139 | EV_A_ 140 | fd, 141 | kqueue_events [i].filter == EVFILT_READ ? EV_READ 142 | : kqueue_events [i].filter == EVFILT_WRITE ? EV_WRITE 143 | : 0 144 | ); 145 | } 146 | 147 | if (expect_false (res == kqueue_eventmax)) 148 | { 149 | ev_free (kqueue_events); 150 | kqueue_eventmax = array_nextsize (sizeof (struct kevent), kqueue_eventmax, kqueue_eventmax + 1); 151 | kqueue_events = (struct kevent *)ev_malloc (sizeof (struct kevent) * kqueue_eventmax); 152 | } 153 | } 154 | 155 | int inline_size 156 | kqueue_init (EV_P_ int flags) 157 | { 158 | /* initialize the kernel queue */ 159 | kqueue_fd_pid = getpid (); 160 | if ((backend_fd = kqueue ()) < 0) 161 | return 0; 162 | 163 | fcntl (backend_fd, F_SETFD, FD_CLOEXEC); /* not sure if necessary, hopefully doesn't hurt */ 164 | 165 | backend_mintime = 1e-9; /* apparently, they did the right thing in freebsd */ 166 | backend_modify = kqueue_modify; 167 | backend_poll = kqueue_poll; 168 | 169 | kqueue_eventmax = 64; /* initial number of events receivable per poll */ 170 | kqueue_events = (struct kevent *)ev_malloc (sizeof (struct kevent) * kqueue_eventmax); 171 | 172 | kqueue_changes = 0; 173 | kqueue_changemax = 0; 174 | kqueue_changecnt = 0; 175 | 176 | return EVBACKEND_KQUEUE; 177 | } 178 | 179 | void inline_size 180 | kqueue_destroy (EV_P) 181 | { 182 | ev_free (kqueue_events); 183 | ev_free (kqueue_changes); 184 | } 185 | 186 | void inline_size 187 | kqueue_fork (EV_P) 188 | { 189 | /* some BSD kernels don't just destroy the kqueue itself, 190 | * but also close the fd, which isn't documented, and 191 | * impossible to support properly. 192 | * we remember the pid of the kqueue call and only close 193 | * the fd if the pid is still the same. 194 | * this leaks fds on sane kernels, but BSD interfaces are 195 | * notoriously buggy and rarely get fixed. 196 | */ 197 | pid_t newpid = getpid (); 198 | 199 | if (newpid == kqueue_fd_pid) 200 | close (backend_fd); 201 | 202 | kqueue_fd_pid = newpid; 203 | while ((backend_fd = kqueue ()) < 0) 204 | ev_syserr ("(libev) kqueue"); 205 | 206 | fcntl (backend_fd, F_SETFD, FD_CLOEXEC); 207 | 208 | /* re-register interest in fds */ 209 | fd_rearm_all (EV_A); 210 | } 211 | 212 | /* sys/event.h defines EV_ERROR */ 213 | #undef EV_ERROR 214 | 215 | -------------------------------------------------------------------------------- /c__src/ev_poll.c: -------------------------------------------------------------------------------- 1 | /* 2 | * libev poll fd activity backend 3 | * 4 | * Copyright (c) 2007,2008,2009,2010,2011 Marc Alexander Lehmann 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without modifica- 8 | * tion, are permitted provided that the following conditions are met: 9 | * 10 | * 1. Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 18 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- 19 | * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 20 | * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- 21 | * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 23 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 24 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- 25 | * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 26 | * OF THE POSSIBILITY OF SUCH DAMAGE. 27 | * 28 | * Alternatively, the contents of this file may be used under the terms of 29 | * the GNU General Public License ("GPL") version 2 or any later version, 30 | * in which case the provisions of the GPL are applicable instead of 31 | * the above. If you wish to allow the use of your version of this file 32 | * only under the terms of the GPL and not to allow others to use your 33 | * version of this file under the BSD license, indicate your decision 34 | * by deleting the provisions above and replace them with the notice 35 | * and other provisions required by the GPL. If you do not delete the 36 | * provisions above, a recipient may use your version of this file under 37 | * either the BSD or the GPL. 38 | */ 39 | 40 | #include 41 | 42 | void inline_size 43 | pollidx_init (int *base, int count) 44 | { 45 | /* consider using memset (.., -1, ...), which is practically guaranteed 46 | * to work on all systems implementing poll */ 47 | while (count--) 48 | *base++ = -1; 49 | } 50 | 51 | static void 52 | poll_modify (EV_P_ int fd, int oev, int nev) 53 | { 54 | int idx; 55 | 56 | if (oev == nev) 57 | return; 58 | 59 | array_needsize (int, pollidxs, pollidxmax, fd + 1, pollidx_init); 60 | 61 | idx = pollidxs [fd]; 62 | 63 | if (idx < 0) /* need to allocate a new pollfd */ 64 | { 65 | pollidxs [fd] = idx = pollcnt++; 66 | array_needsize (struct pollfd, polls, pollmax, pollcnt, EMPTY2); 67 | polls [idx].fd = fd; 68 | } 69 | 70 | assert (polls [idx].fd == fd); 71 | 72 | if (nev) 73 | polls [idx].events = 74 | (nev & EV_READ ? POLLIN : 0) 75 | | (nev & EV_WRITE ? POLLOUT : 0); 76 | else /* remove pollfd */ 77 | { 78 | pollidxs [fd] = -1; 79 | 80 | if (expect_true (idx < --pollcnt)) 81 | { 82 | polls [idx] = polls [pollcnt]; 83 | pollidxs [polls [idx].fd] = idx; 84 | } 85 | } 86 | } 87 | 88 | static void 89 | poll_poll (EV_P_ ev_tstamp timeout) 90 | { 91 | struct pollfd *p; 92 | int res; 93 | 94 | EV_RELEASE_CB; 95 | res = poll (polls, pollcnt, timeout * 1e3); 96 | EV_ACQUIRE_CB; 97 | 98 | if (expect_false (res < 0)) 99 | { 100 | if (errno == EBADF) 101 | fd_ebadf (EV_A); 102 | else if (errno == ENOMEM && !syserr_cb) 103 | fd_enomem (EV_A); 104 | else if (errno != EINTR) 105 | ev_syserr ("(libev) poll"); 106 | } 107 | else 108 | for (p = polls; res; ++p) 109 | { 110 | assert (("libev: poll() returned illegal result, broken BSD kernel?", p < polls + pollcnt)); 111 | 112 | if (expect_false (p->revents)) /* this expect is debatable */ 113 | { 114 | --res; 115 | 116 | if (expect_false (p->revents & POLLNVAL)) 117 | fd_kill (EV_A_ p->fd); 118 | else 119 | fd_event ( 120 | EV_A_ 121 | p->fd, 122 | (p->revents & (POLLOUT | POLLERR | POLLHUP) ? EV_WRITE : 0) 123 | | (p->revents & (POLLIN | POLLERR | POLLHUP) ? EV_READ : 0) 124 | ); 125 | } 126 | } 127 | } 128 | 129 | int inline_size 130 | poll_init (EV_P_ int flags) 131 | { 132 | backend_mintime = 1e-3; 133 | backend_modify = poll_modify; 134 | backend_poll = poll_poll; 135 | 136 | pollidxs = 0; pollidxmax = 0; 137 | polls = 0; pollmax = 0; pollcnt = 0; 138 | 139 | return EVBACKEND_POLL; 140 | } 141 | 142 | void inline_size 143 | poll_destroy (EV_P) 144 | { 145 | ev_free (pollidxs); 146 | ev_free (polls); 147 | } 148 | 149 | -------------------------------------------------------------------------------- /c__src/ev_port.c: -------------------------------------------------------------------------------- 1 | /* 2 | * libev solaris event port backend 3 | * 4 | * Copyright (c) 2007,2008,2009,2010,2011 Marc Alexander Lehmann 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without modifica- 8 | * tion, are permitted provided that the following conditions are met: 9 | * 10 | * 1. Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 18 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- 19 | * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 20 | * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- 21 | * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 23 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 24 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- 25 | * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 26 | * OF THE POSSIBILITY OF SUCH DAMAGE. 27 | * 28 | * Alternatively, the contents of this file may be used under the terms of 29 | * the GNU General Public License ("GPL") version 2 or any later version, 30 | * in which case the provisions of the GPL are applicable instead of 31 | * the above. If you wish to allow the use of your version of this file 32 | * only under the terms of the GPL and not to allow others to use your 33 | * version of this file under the BSD license, indicate your decision 34 | * by deleting the provisions above and replace them with the notice 35 | * and other provisions required by the GPL. If you do not delete the 36 | * provisions above, a recipient may use your version of this file under 37 | * either the BSD or the GPL. 38 | */ 39 | 40 | /* useful reading: 41 | * 42 | * http://bugs.opensolaris.org/view_bug.do?bug_id=6268715 (random results) 43 | * http://bugs.opensolaris.org/view_bug.do?bug_id=6455223 (just totally broken) 44 | * http://bugs.opensolaris.org/view_bug.do?bug_id=6873782 (manpage ETIME) 45 | * http://bugs.opensolaris.org/view_bug.do?bug_id=6874410 (implementation ETIME) 46 | * http://www.mail-archive.com/networking-discuss@opensolaris.org/msg11898.html ETIME vs. nget 47 | * http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/lib/libc/port/gen/event_port.c (libc) 48 | * http://cvs.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/uts/common/fs/portfs/port.c#1325 (kernel) 49 | */ 50 | 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | #include 57 | 58 | void inline_speed 59 | port_associate_and_check (EV_P_ int fd, int ev) 60 | { 61 | if (0 > 62 | port_associate ( 63 | backend_fd, PORT_SOURCE_FD, fd, 64 | (ev & EV_READ ? POLLIN : 0) 65 | | (ev & EV_WRITE ? POLLOUT : 0), 66 | 0 67 | ) 68 | ) 69 | { 70 | if (errno == EBADFD) 71 | fd_kill (EV_A_ fd); 72 | else 73 | ev_syserr ("(libev) port_associate"); 74 | } 75 | } 76 | 77 | static void 78 | port_modify (EV_P_ int fd, int oev, int nev) 79 | { 80 | /* we need to reassociate no matter what, as closes are 81 | * once more silently being discarded. 82 | */ 83 | if (!nev) 84 | { 85 | if (oev) 86 | port_dissociate (backend_fd, PORT_SOURCE_FD, fd); 87 | } 88 | else 89 | port_associate_and_check (EV_A_ fd, nev); 90 | } 91 | 92 | static void 93 | port_poll (EV_P_ ev_tstamp timeout) 94 | { 95 | int res, i; 96 | struct timespec ts; 97 | uint_t nget = 1; 98 | 99 | /* we initialise this to something we will skip in the loop, as */ 100 | /* port_getn can return with nget unchanged, but no indication */ 101 | /* whether it was the original value or has been updated :/ */ 102 | port_events [0].portev_source = 0; 103 | 104 | EV_RELEASE_CB; 105 | EV_TS_SET (ts, timeout); 106 | res = port_getn (backend_fd, port_events, port_eventmax, &nget, &ts); 107 | EV_ACQUIRE_CB; 108 | 109 | /* port_getn may or may not set nget on error */ 110 | /* so we rely on port_events [0].portev_source not being updated */ 111 | if (res == -1 && errno != ETIME && errno != EINTR) 112 | ev_syserr ("(libev) port_getn (see http://bugs.opensolaris.org/view_bug.do?bug_id=6268715, try LIBEV_FLAGS=3 env variable)"); 113 | 114 | for (i = 0; i < nget; ++i) 115 | { 116 | if (port_events [i].portev_source == PORT_SOURCE_FD) 117 | { 118 | int fd = port_events [i].portev_object; 119 | 120 | fd_event ( 121 | EV_A_ 122 | fd, 123 | (port_events [i].portev_events & (POLLOUT | POLLERR | POLLHUP) ? EV_WRITE : 0) 124 | | (port_events [i].portev_events & (POLLIN | POLLERR | POLLHUP) ? EV_READ : 0) 125 | ); 126 | 127 | fd_change (EV_A_ fd, EV__IOFDSET); 128 | } 129 | } 130 | 131 | if (expect_false (nget == port_eventmax)) 132 | { 133 | ev_free (port_events); 134 | port_eventmax = array_nextsize (sizeof (port_event_t), port_eventmax, port_eventmax + 1); 135 | port_events = (port_event_t *)ev_malloc (sizeof (port_event_t) * port_eventmax); 136 | } 137 | } 138 | 139 | int inline_size 140 | port_init (EV_P_ int flags) 141 | { 142 | /* Initialize the kernel queue */ 143 | if ((backend_fd = port_create ()) < 0) 144 | return 0; 145 | 146 | assert (("libev: PORT_SOURCE_FD must not be zero", PORT_SOURCE_FD)); 147 | 148 | fcntl (backend_fd, F_SETFD, FD_CLOEXEC); /* not sure if necessary, hopefully doesn't hurt */ 149 | 150 | /* if my reading of the opensolaris kernel sources are correct, then 151 | * opensolaris does something very stupid: it checks if the time has already 152 | * elapsed and doesn't round up if that is the case,m otherwise it DOES round 153 | * up. Since we can't know what the case is, we need to guess by using a 154 | * "large enough" timeout. Normally, 1e-9 would be correct. 155 | */ 156 | backend_mintime = 1e-3; /* needed to compensate for port_getn returning early */ 157 | backend_modify = port_modify; 158 | backend_poll = port_poll; 159 | 160 | port_eventmax = 64; /* initial number of events receivable per poll */ 161 | port_events = (port_event_t *)ev_malloc (sizeof (port_event_t) * port_eventmax); 162 | 163 | return EVBACKEND_PORT; 164 | } 165 | 166 | void inline_size 167 | port_destroy (EV_P) 168 | { 169 | ev_free (port_events); 170 | } 171 | 172 | void inline_size 173 | port_fork (EV_P) 174 | { 175 | close (backend_fd); 176 | 177 | while ((backend_fd = port_create ()) < 0) 178 | ev_syserr ("(libev) port"); 179 | 180 | fcntl (backend_fd, F_SETFD, FD_CLOEXEC); 181 | 182 | /* re-register interest in fds */ 183 | fd_rearm_all (EV_A); 184 | } 185 | 186 | -------------------------------------------------------------------------------- /c__src/ev_select.c: -------------------------------------------------------------------------------- 1 | /* 2 | * libev select fd activity backend 3 | * 4 | * Copyright (c) 2007,2008,2009,2010,2011 Marc Alexander Lehmann 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without modifica- 8 | * tion, are permitted provided that the following conditions are met: 9 | * 10 | * 1. Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 18 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- 19 | * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 20 | * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- 21 | * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 23 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 24 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- 25 | * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 26 | * OF THE POSSIBILITY OF SUCH DAMAGE. 27 | * 28 | * Alternatively, the contents of this file may be used under the terms of 29 | * the GNU General Public License ("GPL") version 2 or any later version, 30 | * in which case the provisions of the GPL are applicable instead of 31 | * the above. If you wish to allow the use of your version of this file 32 | * only under the terms of the GPL and not to allow others to use your 33 | * version of this file under the BSD license, indicate your decision 34 | * by deleting the provisions above and replace them with the notice 35 | * and other provisions required by the GPL. If you do not delete the 36 | * provisions above, a recipient may use your version of this file under 37 | * either the BSD or the GPL. 38 | */ 39 | 40 | #ifndef _WIN32 41 | /* for unix systems */ 42 | # include 43 | # ifndef __hpux 44 | /* for REAL unix systems */ 45 | # include 46 | # endif 47 | #endif 48 | 49 | #ifndef EV_SELECT_USE_FD_SET 50 | # ifdef NFDBITS 51 | # define EV_SELECT_USE_FD_SET 0 52 | # else 53 | # define EV_SELECT_USE_FD_SET 1 54 | # endif 55 | #endif 56 | 57 | #if EV_SELECT_IS_WINSOCKET 58 | # undef EV_SELECT_USE_FD_SET 59 | # define EV_SELECT_USE_FD_SET 1 60 | # undef NFDBITS 61 | # define NFDBITS 0 62 | #endif 63 | 64 | #if !EV_SELECT_USE_FD_SET 65 | # define NFDBYTES (NFDBITS / 8) 66 | #endif 67 | 68 | #include 69 | 70 | static void 71 | select_modify (EV_P_ int fd, int oev, int nev) 72 | { 73 | if (oev == nev) 74 | return; 75 | 76 | { 77 | #if EV_SELECT_USE_FD_SET 78 | 79 | #if EV_SELECT_IS_WINSOCKET 80 | SOCKET handle = anfds [fd].handle; 81 | #else 82 | int handle = fd; 83 | #endif 84 | 85 | assert (("libev: fd >= FD_SETSIZE passed to fd_set-based select backend", fd < FD_SETSIZE)); 86 | 87 | /* FD_SET is broken on windows (it adds the fd to a set twice or more, 88 | * which eventually leads to overflows). Need to call it only on changes. 89 | */ 90 | #if EV_SELECT_IS_WINSOCKET 91 | if ((oev ^ nev) & EV_READ) 92 | #endif 93 | if (nev & EV_READ) 94 | FD_SET (handle, (fd_set *)vec_ri); 95 | else 96 | FD_CLR (handle, (fd_set *)vec_ri); 97 | 98 | #if EV_SELECT_IS_WINSOCKET 99 | if ((oev ^ nev) & EV_WRITE) 100 | #endif 101 | if (nev & EV_WRITE) 102 | FD_SET (handle, (fd_set *)vec_wi); 103 | else 104 | FD_CLR (handle, (fd_set *)vec_wi); 105 | 106 | #else 107 | 108 | int word = fd / NFDBITS; 109 | fd_mask mask = 1UL << (fd % NFDBITS); 110 | 111 | if (expect_false (vec_max <= word)) 112 | { 113 | int new_max = word + 1; 114 | 115 | vec_ri = ev_realloc (vec_ri, new_max * NFDBYTES); 116 | vec_ro = ev_realloc (vec_ro, new_max * NFDBYTES); /* could free/malloc */ 117 | vec_wi = ev_realloc (vec_wi, new_max * NFDBYTES); 118 | vec_wo = ev_realloc (vec_wo, new_max * NFDBYTES); /* could free/malloc */ 119 | #ifdef _WIN32 120 | vec_eo = ev_realloc (vec_eo, new_max * NFDBYTES); /* could free/malloc */ 121 | #endif 122 | 123 | for (; vec_max < new_max; ++vec_max) 124 | ((fd_mask *)vec_ri) [vec_max] = 125 | ((fd_mask *)vec_wi) [vec_max] = 0; 126 | } 127 | 128 | ((fd_mask *)vec_ri) [word] |= mask; 129 | if (!(nev & EV_READ)) 130 | ((fd_mask *)vec_ri) [word] &= ~mask; 131 | 132 | ((fd_mask *)vec_wi) [word] |= mask; 133 | if (!(nev & EV_WRITE)) 134 | ((fd_mask *)vec_wi) [word] &= ~mask; 135 | #endif 136 | } 137 | } 138 | 139 | static void 140 | select_poll (EV_P_ ev_tstamp timeout) 141 | { 142 | struct timeval tv; 143 | int res; 144 | int fd_setsize; 145 | 146 | EV_RELEASE_CB; 147 | EV_TV_SET (tv, timeout); 148 | 149 | #if EV_SELECT_USE_FD_SET 150 | fd_setsize = sizeof (fd_set); 151 | #else 152 | fd_setsize = vec_max * NFDBYTES; 153 | #endif 154 | 155 | memcpy (vec_ro, vec_ri, fd_setsize); 156 | memcpy (vec_wo, vec_wi, fd_setsize); 157 | 158 | #ifdef _WIN32 159 | /* pass in the write set as except set. 160 | * the idea behind this is to work around a windows bug that causes 161 | * errors to be reported as an exception and not by setting 162 | * the writable bit. this is so uncontrollably lame. 163 | */ 164 | memcpy (vec_eo, vec_wi, fd_setsize); 165 | res = select (vec_max * NFDBITS, (fd_set *)vec_ro, (fd_set *)vec_wo, (fd_set *)vec_eo, &tv); 166 | #elif EV_SELECT_USE_FD_SET 167 | fd_setsize = anfdmax < FD_SETSIZE ? anfdmax : FD_SETSIZE; 168 | res = select (fd_setsize, (fd_set *)vec_ro, (fd_set *)vec_wo, 0, &tv); 169 | #else 170 | res = select (vec_max * NFDBITS, (fd_set *)vec_ro, (fd_set *)vec_wo, 0, &tv); 171 | #endif 172 | EV_ACQUIRE_CB; 173 | 174 | if (expect_false (res < 0)) 175 | { 176 | #if EV_SELECT_IS_WINSOCKET 177 | errno = WSAGetLastError (); 178 | #endif 179 | #ifdef WSABASEERR 180 | /* on windows, select returns incompatible error codes, fix this */ 181 | if (errno >= WSABASEERR && errno < WSABASEERR + 1000) 182 | if (errno == WSAENOTSOCK) 183 | errno = EBADF; 184 | else 185 | errno -= WSABASEERR; 186 | #endif 187 | 188 | #ifdef _WIN32 189 | /* select on windows erroneously returns EINVAL when no fd sets have been 190 | * provided (this is documented). what microsoft doesn't tell you that this bug 191 | * exists even when the fd sets _are_ provided, so we have to check for this bug 192 | * here and emulate by sleeping manually. 193 | * we also get EINVAL when the timeout is invalid, but we ignore this case here 194 | * and assume that EINVAL always means: you have to wait manually. 195 | */ 196 | if (errno == EINVAL) 197 | { 198 | if (timeout) 199 | { 200 | unsigned long ms = timeout * 1e3; 201 | Sleep (ms ? ms : 1); 202 | } 203 | 204 | return; 205 | } 206 | #endif 207 | 208 | if (errno == EBADF) 209 | fd_ebadf (EV_A); 210 | else if (errno == ENOMEM && !syserr_cb) 211 | fd_enomem (EV_A); 212 | else if (errno != EINTR) 213 | ev_syserr ("(libev) select"); 214 | 215 | return; 216 | } 217 | 218 | #if EV_SELECT_USE_FD_SET 219 | 220 | { 221 | int fd; 222 | 223 | for (fd = 0; fd < anfdmax; ++fd) 224 | if (anfds [fd].events) 225 | { 226 | int events = 0; 227 | #if EV_SELECT_IS_WINSOCKET 228 | SOCKET handle = anfds [fd].handle; 229 | #else 230 | int handle = fd; 231 | #endif 232 | 233 | if (FD_ISSET (handle, (fd_set *)vec_ro)) events |= EV_READ; 234 | if (FD_ISSET (handle, (fd_set *)vec_wo)) events |= EV_WRITE; 235 | #ifdef _WIN32 236 | if (FD_ISSET (handle, (fd_set *)vec_eo)) events |= EV_WRITE; 237 | #endif 238 | 239 | if (expect_true (events)) 240 | fd_event (EV_A_ fd, events); 241 | } 242 | } 243 | 244 | #else 245 | 246 | { 247 | int word, bit; 248 | for (word = vec_max; word--; ) 249 | { 250 | fd_mask word_r = ((fd_mask *)vec_ro) [word]; 251 | fd_mask word_w = ((fd_mask *)vec_wo) [word]; 252 | #ifdef _WIN32 253 | word_w |= ((fd_mask *)vec_eo) [word]; 254 | #endif 255 | 256 | if (word_r || word_w) 257 | for (bit = NFDBITS; bit--; ) 258 | { 259 | fd_mask mask = 1UL << bit; 260 | int events = 0; 261 | 262 | events |= word_r & mask ? EV_READ : 0; 263 | events |= word_w & mask ? EV_WRITE : 0; 264 | 265 | if (expect_true (events)) 266 | fd_event (EV_A_ word * NFDBITS + bit, events); 267 | } 268 | } 269 | } 270 | 271 | #endif 272 | } 273 | 274 | int inline_size 275 | select_init (EV_P_ int flags) 276 | { 277 | backend_mintime = 1e-6; 278 | backend_modify = select_modify; 279 | backend_poll = select_poll; 280 | 281 | #if EV_SELECT_USE_FD_SET 282 | vec_ri = ev_malloc (sizeof (fd_set)); FD_ZERO ((fd_set *)vec_ri); 283 | vec_ro = ev_malloc (sizeof (fd_set)); 284 | vec_wi = ev_malloc (sizeof (fd_set)); FD_ZERO ((fd_set *)vec_wi); 285 | vec_wo = ev_malloc (sizeof (fd_set)); 286 | #ifdef _WIN32 287 | vec_eo = ev_malloc (sizeof (fd_set)); 288 | #endif 289 | #else 290 | vec_max = 0; 291 | vec_ri = 0; 292 | vec_ro = 0; 293 | vec_wi = 0; 294 | vec_wo = 0; 295 | #ifdef _WIN32 296 | vec_eo = 0; 297 | #endif 298 | #endif 299 | 300 | return EVBACKEND_SELECT; 301 | } 302 | 303 | void inline_size 304 | select_destroy (EV_P) 305 | { 306 | ev_free (vec_ri); 307 | ev_free (vec_ro); 308 | ev_free (vec_wi); 309 | ev_free (vec_wo); 310 | #ifdef _WIN32 311 | ev_free (vec_eo); 312 | #endif 313 | } 314 | 315 | -------------------------------------------------------------------------------- /c__src/ev_vars.h: -------------------------------------------------------------------------------- 1 | /* 2 | * loop member variable declarations 3 | * 4 | * Copyright (c) 2007,2008,2009,2010,2011,2012,2013 Marc Alexander Lehmann 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without modifica- 8 | * tion, are permitted provided that the following conditions are met: 9 | * 10 | * 1. Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 18 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- 19 | * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 20 | * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- 21 | * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 23 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 24 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- 25 | * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 26 | * OF THE POSSIBILITY OF SUCH DAMAGE. 27 | * 28 | * Alternatively, the contents of this file may be used under the terms of 29 | * the GNU General Public License ("GPL") version 2 or any later version, 30 | * in which case the provisions of the GPL are applicable instead of 31 | * the above. If you wish to allow the use of your version of this file 32 | * only under the terms of the GPL and not to allow others to use your 33 | * version of this file under the BSD license, indicate your decision 34 | * by deleting the provisions above and replace them with the notice 35 | * and other provisions required by the GPL. If you do not delete the 36 | * provisions above, a recipient may use your version of this file under 37 | * either the BSD or the GPL. 38 | */ 39 | 40 | #define VARx(type,name) VAR(name, type name) 41 | 42 | VARx(ev_tstamp, now_floor) /* last time we refreshed rt_time */ 43 | VARx(ev_tstamp, mn_now) /* monotonic clock "now" */ 44 | VARx(ev_tstamp, rtmn_diff) /* difference realtime - monotonic time */ 45 | 46 | /* for reverse feeding of events */ 47 | VARx(W *, rfeeds) 48 | VARx(int, rfeedmax) 49 | VARx(int, rfeedcnt) 50 | 51 | VAR (pendings, ANPENDING *pendings [NUMPRI]) 52 | VAR (pendingmax, int pendingmax [NUMPRI]) 53 | VAR (pendingcnt, int pendingcnt [NUMPRI]) 54 | VARx(int, pendingpri) /* highest priority currently pending */ 55 | VARx(ev_prepare, pending_w) /* dummy pending watcher */ 56 | 57 | VARx(ev_tstamp, io_blocktime) 58 | VARx(ev_tstamp, timeout_blocktime) 59 | 60 | VARx(int, backend) 61 | VARx(int, activecnt) /* total number of active events ("refcount") */ 62 | VARx(EV_ATOMIC_T, loop_done) /* signal by ev_break */ 63 | 64 | VARx(int, backend_fd) 65 | VARx(ev_tstamp, backend_mintime) /* assumed typical timer resolution */ 66 | VAR (backend_modify, void (*backend_modify)(EV_P_ int fd, int oev, int nev)) 67 | VAR (backend_poll , void (*backend_poll)(EV_P_ ev_tstamp timeout)) 68 | 69 | VARx(ANFD *, anfds) 70 | VARx(int, anfdmax) 71 | 72 | VAR (evpipe, int evpipe [2]) 73 | VARx(ev_io, pipe_w) 74 | VARx(EV_ATOMIC_T, pipe_write_wanted) 75 | VARx(EV_ATOMIC_T, pipe_write_skipped) 76 | 77 | #if !defined(_WIN32) || EV_GENWRAP 78 | VARx(pid_t, curpid) 79 | #endif 80 | 81 | VARx(char, postfork) /* true if we need to recreate kernel state after fork */ 82 | 83 | #if EV_USE_SELECT || EV_GENWRAP 84 | VARx(void *, vec_ri) 85 | VARx(void *, vec_ro) 86 | VARx(void *, vec_wi) 87 | VARx(void *, vec_wo) 88 | #if defined(_WIN32) || EV_GENWRAP 89 | VARx(void *, vec_eo) 90 | #endif 91 | VARx(int, vec_max) 92 | #endif 93 | 94 | #if EV_USE_POLL || EV_GENWRAP 95 | VARx(struct pollfd *, polls) 96 | VARx(int, pollmax) 97 | VARx(int, pollcnt) 98 | VARx(int *, pollidxs) /* maps fds into structure indices */ 99 | VARx(int, pollidxmax) 100 | #endif 101 | 102 | #if EV_USE_EPOLL || EV_GENWRAP 103 | VARx(struct epoll_event *, epoll_events) 104 | VARx(int, epoll_eventmax) 105 | VARx(int *, epoll_eperms) 106 | VARx(int, epoll_epermcnt) 107 | VARx(int, epoll_epermmax) 108 | #endif 109 | 110 | #if EV_USE_KQUEUE || EV_GENWRAP 111 | VARx(pid_t, kqueue_fd_pid) 112 | VARx(struct kevent *, kqueue_changes) 113 | VARx(int, kqueue_changemax) 114 | VARx(int, kqueue_changecnt) 115 | VARx(struct kevent *, kqueue_events) 116 | VARx(int, kqueue_eventmax) 117 | #endif 118 | 119 | #if EV_USE_PORT || EV_GENWRAP 120 | VARx(struct port_event *, port_events) 121 | VARx(int, port_eventmax) 122 | #endif 123 | 124 | #if EV_USE_IOCP || EV_GENWRAP 125 | VARx(HANDLE, iocp) 126 | #endif 127 | 128 | VARx(int *, fdchanges) 129 | VARx(int, fdchangemax) 130 | VARx(int, fdchangecnt) 131 | 132 | VARx(ANHE *, timers) 133 | VARx(int, timermax) 134 | VARx(int, timercnt) 135 | 136 | #if EV_PERIODIC_ENABLE || EV_GENWRAP 137 | VARx(ANHE *, periodics) 138 | VARx(int, periodicmax) 139 | VARx(int, periodiccnt) 140 | #endif 141 | 142 | #if EV_IDLE_ENABLE || EV_GENWRAP 143 | VAR (idles, ev_idle **idles [NUMPRI]) 144 | VAR (idlemax, int idlemax [NUMPRI]) 145 | VAR (idlecnt, int idlecnt [NUMPRI]) 146 | #endif 147 | VARx(int, idleall) /* total number */ 148 | 149 | VARx(struct ev_prepare **, prepares) 150 | VARx(int, preparemax) 151 | VARx(int, preparecnt) 152 | 153 | VARx(struct ev_check **, checks) 154 | VARx(int, checkmax) 155 | VARx(int, checkcnt) 156 | 157 | #if EV_FORK_ENABLE || EV_GENWRAP 158 | VARx(struct ev_fork **, forks) 159 | VARx(int, forkmax) 160 | VARx(int, forkcnt) 161 | #endif 162 | 163 | #if EV_CLEANUP_ENABLE || EV_GENWRAP 164 | VARx(struct ev_cleanup **, cleanups) 165 | VARx(int, cleanupmax) 166 | VARx(int, cleanupcnt) 167 | #endif 168 | 169 | #if EV_ASYNC_ENABLE || EV_GENWRAP 170 | VARx(EV_ATOMIC_T, async_pending) 171 | VARx(struct ev_async **, asyncs) 172 | VARx(int, asyncmax) 173 | VARx(int, asynccnt) 174 | #endif 175 | 176 | #if EV_USE_INOTIFY || EV_GENWRAP 177 | VARx(int, fs_fd) 178 | VARx(ev_io, fs_w) 179 | VARx(char, fs_2625) /* whether we are running in linux 2.6.25 or newer */ 180 | VAR (fs_hash, ANFS fs_hash [EV_INOTIFY_HASHSIZE]) 181 | #endif 182 | 183 | VARx(EV_ATOMIC_T, sig_pending) 184 | #if EV_USE_SIGNALFD || EV_GENWRAP 185 | VARx(int, sigfd) 186 | VARx(ev_io, sigfd_w) 187 | VARx(sigset_t, sigfd_set) 188 | #endif 189 | 190 | VARx(unsigned int, origflags) /* original loop flags */ 191 | 192 | #if EV_FEATURE_API || EV_GENWRAP 193 | VARx(unsigned int, loop_count) /* total number of loop iterations/blocks */ 194 | VARx(unsigned int, loop_depth) /* #ev_run enters - #ev_run leaves */ 195 | 196 | VARx(void *, userdata) 197 | /* C++ doesn't support the ev_loop_callback typedef here. stinks. */ 198 | VAR (release_cb, void (*release_cb)(EV_P) EV_THROW) 199 | VAR (acquire_cb, void (*acquire_cb)(EV_P) EV_THROW) 200 | VAR (invoke_cb , ev_loop_callback invoke_cb) 201 | #endif 202 | 203 | #undef VARx 204 | 205 | -------------------------------------------------------------------------------- /c__src/ev_win32.c: -------------------------------------------------------------------------------- 1 | /* 2 | * libev win32 compatibility cruft (_not_ a backend) 3 | * 4 | * Copyright (c) 2007,2008,2009 Marc Alexander Lehmann 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without modifica- 8 | * tion, are permitted provided that the following conditions are met: 9 | * 10 | * 1. Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 18 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- 19 | * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 20 | * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- 21 | * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 23 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 24 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- 25 | * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 26 | * OF THE POSSIBILITY OF SUCH DAMAGE. 27 | * 28 | * Alternatively, the contents of this file may be used under the terms of 29 | * the GNU General Public License ("GPL") version 2 or any later version, 30 | * in which case the provisions of the GPL are applicable instead of 31 | * the above. If you wish to allow the use of your version of this file 32 | * only under the terms of the GPL and not to allow others to use your 33 | * version of this file under the BSD license, indicate your decision 34 | * by deleting the provisions above and replace them with the notice 35 | * and other provisions required by the GPL. If you do not delete the 36 | * provisions above, a recipient may use your version of this file under 37 | * either the BSD or the GPL. 38 | */ 39 | 40 | #ifdef _WIN32 41 | 42 | /* timeb.h is actually xsi legacy functionality */ 43 | #include 44 | 45 | /* note: the comment below could not be substantiated, but what would I care */ 46 | /* MSDN says this is required to handle SIGFPE */ 47 | /* my wild guess would be that using something floating-pointy is required */ 48 | /* for the crt to do something about it */ 49 | volatile double SIGFPE_REQ = 0.0f; 50 | 51 | static SOCKET 52 | ev_tcp_socket (void) 53 | { 54 | #if EV_USE_WSASOCKET 55 | return WSASocket (AF_INET, SOCK_STREAM, 0, 0, 0, 0); 56 | #else 57 | return socket (AF_INET, SOCK_STREAM, 0); 58 | #endif 59 | } 60 | 61 | /* oh, the humanity! */ 62 | static int 63 | ev_pipe (int filedes [2]) 64 | { 65 | struct sockaddr_in addr = { 0 }; 66 | int addr_size = sizeof (addr); 67 | struct sockaddr_in adr2; 68 | int adr2_size = sizeof (adr2); 69 | SOCKET listener; 70 | SOCKET sock [2] = { -1, -1 }; 71 | 72 | if ((listener = ev_tcp_socket ()) == INVALID_SOCKET) 73 | return -1; 74 | 75 | addr.sin_family = AF_INET; 76 | addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK); 77 | addr.sin_port = 0; 78 | 79 | if (bind (listener, (struct sockaddr *)&addr, addr_size)) 80 | goto fail; 81 | 82 | if (getsockname (listener, (struct sockaddr *)&addr, &addr_size)) 83 | goto fail; 84 | 85 | if (listen (listener, 1)) 86 | goto fail; 87 | 88 | if ((sock [0] = ev_tcp_socket ()) == INVALID_SOCKET) 89 | goto fail; 90 | 91 | if (connect (sock [0], (struct sockaddr *)&addr, addr_size)) 92 | goto fail; 93 | 94 | if ((sock [1] = accept (listener, 0, 0)) < 0) 95 | goto fail; 96 | 97 | /* windows vista returns fantasy port numbers for sockets: 98 | * example for two interconnected tcp sockets: 99 | * 100 | * (Socket::unpack_sockaddr_in getsockname $sock0)[0] == 53364 101 | * (Socket::unpack_sockaddr_in getpeername $sock0)[0] == 53363 102 | * (Socket::unpack_sockaddr_in getsockname $sock1)[0] == 53363 103 | * (Socket::unpack_sockaddr_in getpeername $sock1)[0] == 53365 104 | * 105 | * wow! tridirectional sockets! 106 | * 107 | * this way of checking ports seems to work: 108 | */ 109 | if (getpeername (sock [0], (struct sockaddr *)&addr, &addr_size)) 110 | goto fail; 111 | 112 | if (getsockname (sock [1], (struct sockaddr *)&adr2, &adr2_size)) 113 | goto fail; 114 | 115 | errno = WSAEINVAL; 116 | if (addr_size != adr2_size 117 | || addr.sin_addr.s_addr != adr2.sin_addr.s_addr /* just to be sure, I mean, it's windows */ 118 | || addr.sin_port != adr2.sin_port) 119 | goto fail; 120 | 121 | closesocket (listener); 122 | 123 | #if EV_SELECT_IS_WINSOCKET 124 | filedes [0] = EV_WIN32_HANDLE_TO_FD (sock [0]); 125 | filedes [1] = EV_WIN32_HANDLE_TO_FD (sock [1]); 126 | #else 127 | /* when select isn't winsocket, we also expect socket, connect, accept etc. 128 | * to work on fds */ 129 | filedes [0] = sock [0]; 130 | filedes [1] = sock [1]; 131 | #endif 132 | 133 | return 0; 134 | 135 | fail: 136 | closesocket (listener); 137 | 138 | if (sock [0] != INVALID_SOCKET) closesocket (sock [0]); 139 | if (sock [1] != INVALID_SOCKET) closesocket (sock [1]); 140 | 141 | return -1; 142 | } 143 | 144 | #undef pipe 145 | #define pipe(filedes) ev_pipe (filedes) 146 | 147 | #define EV_HAVE_EV_TIME 1 148 | ev_tstamp 149 | ev_time (void) 150 | { 151 | FILETIME ft; 152 | ULARGE_INTEGER ui; 153 | 154 | GetSystemTimeAsFileTime (&ft); 155 | ui.u.LowPart = ft.dwLowDateTime; 156 | ui.u.HighPart = ft.dwHighDateTime; 157 | 158 | /* msvc cannot convert ulonglong to double... yes, it is that sucky */ 159 | return (LONGLONG)(ui.QuadPart - 116444736000000000) * 1e-7; 160 | } 161 | 162 | #endif 163 | 164 | -------------------------------------------------------------------------------- /c__src/ev_wrap.h: -------------------------------------------------------------------------------- 1 | /* DO NOT EDIT, automatically generated by update_ev_wrap */ 2 | #ifndef EV_WRAP_H 3 | #define EV_WRAP_H 4 | #define acquire_cb ((loop)->acquire_cb) 5 | #define activecnt ((loop)->activecnt) 6 | #define anfdmax ((loop)->anfdmax) 7 | #define anfds ((loop)->anfds) 8 | #define async_pending ((loop)->async_pending) 9 | #define asynccnt ((loop)->asynccnt) 10 | #define asyncmax ((loop)->asyncmax) 11 | #define asyncs ((loop)->asyncs) 12 | #define backend ((loop)->backend) 13 | #define backend_fd ((loop)->backend_fd) 14 | #define backend_mintime ((loop)->backend_mintime) 15 | #define backend_modify ((loop)->backend_modify) 16 | #define backend_poll ((loop)->backend_poll) 17 | #define checkcnt ((loop)->checkcnt) 18 | #define checkmax ((loop)->checkmax) 19 | #define checks ((loop)->checks) 20 | #define cleanupcnt ((loop)->cleanupcnt) 21 | #define cleanupmax ((loop)->cleanupmax) 22 | #define cleanups ((loop)->cleanups) 23 | #define curpid ((loop)->curpid) 24 | #define epoll_epermcnt ((loop)->epoll_epermcnt) 25 | #define epoll_epermmax ((loop)->epoll_epermmax) 26 | #define epoll_eperms ((loop)->epoll_eperms) 27 | #define epoll_eventmax ((loop)->epoll_eventmax) 28 | #define epoll_events ((loop)->epoll_events) 29 | #define evpipe ((loop)->evpipe) 30 | #define fdchangecnt ((loop)->fdchangecnt) 31 | #define fdchangemax ((loop)->fdchangemax) 32 | #define fdchanges ((loop)->fdchanges) 33 | #define forkcnt ((loop)->forkcnt) 34 | #define forkmax ((loop)->forkmax) 35 | #define forks ((loop)->forks) 36 | #define fs_2625 ((loop)->fs_2625) 37 | #define fs_fd ((loop)->fs_fd) 38 | #define fs_hash ((loop)->fs_hash) 39 | #define fs_w ((loop)->fs_w) 40 | #define idleall ((loop)->idleall) 41 | #define idlecnt ((loop)->idlecnt) 42 | #define idlemax ((loop)->idlemax) 43 | #define idles ((loop)->idles) 44 | #define invoke_cb ((loop)->invoke_cb) 45 | #define io_blocktime ((loop)->io_blocktime) 46 | #define iocp ((loop)->iocp) 47 | #define kqueue_changecnt ((loop)->kqueue_changecnt) 48 | #define kqueue_changemax ((loop)->kqueue_changemax) 49 | #define kqueue_changes ((loop)->kqueue_changes) 50 | #define kqueue_eventmax ((loop)->kqueue_eventmax) 51 | #define kqueue_events ((loop)->kqueue_events) 52 | #define kqueue_fd_pid ((loop)->kqueue_fd_pid) 53 | #define loop_count ((loop)->loop_count) 54 | #define loop_depth ((loop)->loop_depth) 55 | #define loop_done ((loop)->loop_done) 56 | #define mn_now ((loop)->mn_now) 57 | #define now_floor ((loop)->now_floor) 58 | #define origflags ((loop)->origflags) 59 | #define pending_w ((loop)->pending_w) 60 | #define pendingcnt ((loop)->pendingcnt) 61 | #define pendingmax ((loop)->pendingmax) 62 | #define pendingpri ((loop)->pendingpri) 63 | #define pendings ((loop)->pendings) 64 | #define periodiccnt ((loop)->periodiccnt) 65 | #define periodicmax ((loop)->periodicmax) 66 | #define periodics ((loop)->periodics) 67 | #define pipe_w ((loop)->pipe_w) 68 | #define pipe_write_skipped ((loop)->pipe_write_skipped) 69 | #define pipe_write_wanted ((loop)->pipe_write_wanted) 70 | #define pollcnt ((loop)->pollcnt) 71 | #define pollidxmax ((loop)->pollidxmax) 72 | #define pollidxs ((loop)->pollidxs) 73 | #define pollmax ((loop)->pollmax) 74 | #define polls ((loop)->polls) 75 | #define port_eventmax ((loop)->port_eventmax) 76 | #define port_events ((loop)->port_events) 77 | #define postfork ((loop)->postfork) 78 | #define preparecnt ((loop)->preparecnt) 79 | #define preparemax ((loop)->preparemax) 80 | #define prepares ((loop)->prepares) 81 | #define release_cb ((loop)->release_cb) 82 | #define rfeedcnt ((loop)->rfeedcnt) 83 | #define rfeedmax ((loop)->rfeedmax) 84 | #define rfeeds ((loop)->rfeeds) 85 | #define rtmn_diff ((loop)->rtmn_diff) 86 | #define sig_pending ((loop)->sig_pending) 87 | #define sigfd ((loop)->sigfd) 88 | #define sigfd_set ((loop)->sigfd_set) 89 | #define sigfd_w ((loop)->sigfd_w) 90 | #define timeout_blocktime ((loop)->timeout_blocktime) 91 | #define timercnt ((loop)->timercnt) 92 | #define timermax ((loop)->timermax) 93 | #define timers ((loop)->timers) 94 | #define userdata ((loop)->userdata) 95 | #define vec_eo ((loop)->vec_eo) 96 | #define vec_max ((loop)->vec_max) 97 | #define vec_ri ((loop)->vec_ri) 98 | #define vec_ro ((loop)->vec_ro) 99 | #define vec_wi ((loop)->vec_wi) 100 | #define vec_wo ((loop)->vec_wo) 101 | #else 102 | #undef EV_WRAP_H 103 | #undef acquire_cb 104 | #undef activecnt 105 | #undef anfdmax 106 | #undef anfds 107 | #undef async_pending 108 | #undef asynccnt 109 | #undef asyncmax 110 | #undef asyncs 111 | #undef backend 112 | #undef backend_fd 113 | #undef backend_mintime 114 | #undef backend_modify 115 | #undef backend_poll 116 | #undef checkcnt 117 | #undef checkmax 118 | #undef checks 119 | #undef cleanupcnt 120 | #undef cleanupmax 121 | #undef cleanups 122 | #undef curpid 123 | #undef epoll_epermcnt 124 | #undef epoll_epermmax 125 | #undef epoll_eperms 126 | #undef epoll_eventmax 127 | #undef epoll_events 128 | #undef evpipe 129 | #undef fdchangecnt 130 | #undef fdchangemax 131 | #undef fdchanges 132 | #undef forkcnt 133 | #undef forkmax 134 | #undef forks 135 | #undef fs_2625 136 | #undef fs_fd 137 | #undef fs_hash 138 | #undef fs_w 139 | #undef idleall 140 | #undef idlecnt 141 | #undef idlemax 142 | #undef idles 143 | #undef invoke_cb 144 | #undef io_blocktime 145 | #undef iocp 146 | #undef kqueue_changecnt 147 | #undef kqueue_changemax 148 | #undef kqueue_changes 149 | #undef kqueue_eventmax 150 | #undef kqueue_events 151 | #undef kqueue_fd_pid 152 | #undef loop_count 153 | #undef loop_depth 154 | #undef loop_done 155 | #undef mn_now 156 | #undef now_floor 157 | #undef origflags 158 | #undef pending_w 159 | #undef pendingcnt 160 | #undef pendingmax 161 | #undef pendingpri 162 | #undef pendings 163 | #undef periodiccnt 164 | #undef periodicmax 165 | #undef periodics 166 | #undef pipe_w 167 | #undef pipe_write_skipped 168 | #undef pipe_write_wanted 169 | #undef pollcnt 170 | #undef pollidxmax 171 | #undef pollidxs 172 | #undef pollmax 173 | #undef polls 174 | #undef port_eventmax 175 | #undef port_events 176 | #undef postfork 177 | #undef preparecnt 178 | #undef preparemax 179 | #undef prepares 180 | #undef release_cb 181 | #undef rfeedcnt 182 | #undef rfeedmax 183 | #undef rfeeds 184 | #undef rtmn_diff 185 | #undef sig_pending 186 | #undef sigfd 187 | #undef sigfd_set 188 | #undef sigfd_w 189 | #undef timeout_blocktime 190 | #undef timercnt 191 | #undef timermax 192 | #undef timers 193 | #undef userdata 194 | #undef vec_eo 195 | #undef vec_max 196 | #undef vec_ri 197 | #undef vec_ro 198 | #undef vec_wi 199 | #undef vec_wo 200 | #endif 201 | -------------------------------------------------------------------------------- /images/gen_socket_pollet_binding.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alibaba/erlang_multi_pollset/4c65ecde0dec4d0edc16049fce594769b2cf752e/images/gen_socket_pollet_binding.PNG -------------------------------------------------------------------------------- /images/gen_socket_pollset.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alibaba/erlang_multi_pollset/4c65ecde0dec4d0edc16049fce594769b2cf752e/images/gen_socket_pollset.PNG -------------------------------------------------------------------------------- /images/gen_tcp_pollset.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alibaba/erlang_multi_pollset/4c65ecde0dec4d0edc16049fce594769b2cf752e/images/gen_tcp_pollset.PNG -------------------------------------------------------------------------------- /rebar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alibaba/erlang_multi_pollset/4c65ecde0dec4d0edc16049fce594769b2cf752e/rebar -------------------------------------------------------------------------------- /rebar.config: -------------------------------------------------------------------------------- 1 | {erl_opts, [ 2 | %%warnings_as_errors, 3 | no_debug_info, 4 | {d, 'DEBUG'}, 5 | {src_dirs, ["src"]} 6 | ]}. 7 | 8 | {deps_dir, "deps"}. 9 | {sub_dirs, ["rel"]}. 10 | 11 | {deps,[]}. 12 | 13 | {pre_hooks, [{compile, "./build.sh"}]}. 14 | 15 | %%{cover_enabled,true}. 16 | %%{cover_print_enabled,true}. 17 | {cover_exclude,[]}. 18 | {xref_checks,[undefined_function_calls]}. 19 | {eunit_opts,[verbose,{report,{eunit_surefire,[{dir,"."}]}}]}. 20 | -------------------------------------------------------------------------------- /src/gen_socket.app.src: -------------------------------------------------------------------------------- 1 | {application,gen_socket, 2 | [{description,[]}, 3 | {vsn,"1.0.3"}, 4 | {modules,[]}, 5 | {registered,[]}, 6 | {applications,[kernel,stdlib]}, 7 | {mod,{gen_socket_app,[]}}, 8 | {env,[{bind_scheduler,true},{bind_socket_policy,1}]}]}. 9 | -------------------------------------------------------------------------------- /src/gen_socket.erl: -------------------------------------------------------------------------------- 1 | -module(gen_socket). 2 | -export([connect/3, connect/4, listen/2, setopts/2, accept/1, accept/2, 3 | send/2, recv/2, recv/3, controlling_process/2, getstat/1, getstat/2, 4 | socket_info/2, peername/1, sockname/1, getopts/2, close/1, 5 | dup_socket/1]). 6 | 7 | -include("gen_socket.hrl"). 8 | 9 | connect(Address, Port, Options) -> 10 | connect(Address, Port, Options, infinity). 11 | 12 | connect(Address, Port, Options, Timeout) -> 13 | {ok, IPAddress} = to_ip_address(Address), 14 | {PostOptions, PreOptions} = partition_options(Options, [active]), 15 | case create_socket(Options) of 16 | {ok, Socket} -> 17 | case do_connect_1(IPAddress, Port, PreOptions, Timeout, Socket) of 18 | ok -> case setopts(Socket, PostOptions) of 19 | ok -> {ok, Socket}; 20 | Error -> raw_socket:close(Socket), Error 21 | end; 22 | Error -> raw_socket:close(Socket), Error 23 | end; 24 | Error -> Error 25 | end. 26 | 27 | do_connect_1(IPAddress, Port, _Options, Timeout, Socket) -> 28 | Address = inet:ntoa(IPAddress), 29 | case raw_socket:connect(Socket, Address, Port) of 30 | {error, einprogress} -> 31 | wait_for_connect_finish(Timeout, Socket); 32 | Error -> 33 | Error 34 | end. 35 | 36 | wait_for_connect_finish(Timeout, Socket) -> 37 | case raw_socket:enable_events(Socket, [readable, writable], self()) of 38 | ok -> wait_for_connect_events(Timeout, Socket); 39 | Error -> flush_all_events(Socket), 40 | Error 41 | end. 42 | 43 | wait_for_connect_events(Timeout, Socket) -> 44 | receive 45 | {socket_event, _, Socket} -> 46 | flush_all_events(Socket), 47 | case raw_socket:getsockopt(Socket, errorcode) of 48 | {ok, 0, _} -> ok; 49 | {ok, _Code, Err} -> {error, Err}; 50 | Error -> Error 51 | end 52 | after Timeout -> 53 | flush_all_events(Socket), 54 | {error, etimedout} 55 | end. 56 | 57 | is_member(Key, List) -> 58 | lists:any(fun(K) -> K == Key end, List). 59 | 60 | partition_options(Options, Keys) -> 61 | lists:partition( 62 | fun(T) when is_tuple(T) -> 63 | is_member(element(1, T), Keys); 64 | (_T) -> 65 | false 66 | end, Options). 67 | 68 | controlling_process(Socket, Pid) -> 69 | raw_socket:controlling_process(Socket, Pid). 70 | 71 | listen(Port, Options) -> 72 | case create_socket(Options) of 73 | {ok, Socket} -> 74 | case do_listen_1(Socket, Port, Options) of 75 | ok -> {ok, Socket}; 76 | Error -> raw_socket:close(Socket), Error 77 | end; 78 | Error -> 79 | Error 80 | end. 81 | 82 | do_listen_1(Socket, Port, Options) -> 83 | Address0 = proplists:get_value(ip, Options, {0,0,0,0}), 84 | {ok, Address1} = to_ip_address(Address0), 85 | Address2 = inet:ntoa(Address1), 86 | Backlog = proplists:get_value(backlog, Options, 128), 87 | case raw_socket:bind(Socket, Address2, Port) of 88 | ok -> raw_socket:listen(Socket, Backlog); 89 | Error -> Error 90 | end. 91 | 92 | create_socket(Options) -> 93 | %_Family = get_family(Options), 94 | case raw_socket:socket() of 95 | {ok, Socket} -> 96 | case setopts(Socket, Options) of 97 | ok -> {ok, Socket}; 98 | Error -> raw_socket:close(Socket), Error 99 | end; 100 | Error -> Error 101 | end. 102 | 103 | setopts(_Socket, []) -> 104 | ok; 105 | setopts(Socket, [{active, false} | T]) -> 106 | case raw_socket:setsockopt(Socket, {active, ?ACTIVE_FALSE}) of 107 | ok -> setopts(Socket, T); 108 | Error -> Error 109 | end; 110 | setopts(Socket, [{active, true} | T]) -> 111 | case raw_socket:setsockopt(Socket, {active, ?ACTIVE_TRUE}) of 112 | ok -> setopts(Socket, T); 113 | Error -> Error 114 | end; 115 | setopts(Socket, [{active, once} | T]) -> 116 | case raw_socket:setsockopt(Socket, {active, ?ACTIVE_ONCE}) of 117 | ok -> setopts(Socket, T); 118 | Error -> Error 119 | end; 120 | setopts(Socket, [{active, _} = H | T]) -> 121 | case raw_socket:setsockopt(Socket, H) of 122 | ok -> setopts(Socket, T); 123 | Error -> Error 124 | end; 125 | setopts(Socket, [{nodelay, _} = H | T]) -> 126 | case raw_socket:setsockopt(Socket, H) of 127 | ok -> setopts(Socket, T); 128 | Error -> Error 129 | end; 130 | setopts(Socket, [{reuseaddr, _} = H | T]) -> 131 | case raw_socket:setsockopt(Socket, H) of 132 | ok -> setopts(Socket, T); 133 | Error -> Error 134 | end; 135 | setopts(Socket, [{keepalive, _} = H | T]) -> 136 | case raw_socket:setsockopt(Socket, H) of 137 | ok -> setopts(Socket, T); 138 | Error -> Error 139 | end; 140 | setopts(Socket, [{recbuf, _} = H | T]) -> 141 | case raw_socket:setsockopt(Socket, H) of 142 | ok -> setopts(Socket, T); 143 | Error -> Error 144 | end; 145 | setopts(Socket, [{sndbuf, _} = H | T]) -> 146 | case raw_socket:setsockopt(Socket, H) of 147 | ok -> setopts(Socket, T); 148 | Error -> Error 149 | end; 150 | setopts(Socket, [{send_timeout, Timo} | T]) -> 151 | Timeout = case Timo of 152 | 0 -> infinity; 153 | _ -> Timo 154 | end, 155 | case raw_socket:setsockopt(Socket, {send_timeout, Timeout}) of 156 | ok -> setopts(Socket, T); 157 | Error -> Error 158 | end; 159 | setopts(Socket, [{backlog, _} | T]) -> 160 | setopts(Socket, T); 161 | setopts(Socket, [{ip, _} | T]) -> 162 | setopts(Socket, T); 163 | setopts(_Socket, [_|_]) -> 164 | %setopts(Socket, T). 165 | {error, unknown_option}. 166 | 167 | getopts(Socket, Opts) -> 168 | try internal_getopts(Socket, Opts) of 169 | Result -> {ok, Result} 170 | catch 171 | Error -> {error, Error} 172 | end. 173 | 174 | internal_getopts(_Socket, []) -> 175 | []; 176 | internal_getopts(Socket, [nodelay = H | T]) -> 177 | V = case raw_socket:getsockopt(Socket, H) of 178 | {ok, 1} -> true; 179 | {ok, 0} -> false; 180 | Error -> throw(Error) 181 | end, 182 | [{nodelay, V} | internal_getopts(Socket, T)]; 183 | internal_getopts(Socket, [reuseaddr = H | T]) -> 184 | V = case raw_socket:getsockopt(Socket, H) of 185 | {ok, 1} -> true; 186 | {ok, 0} -> false; 187 | Error -> throw(Error) 188 | end, 189 | [{reuseaddr, V} | internal_getopts(Socket, T)]; 190 | internal_getopts(Socket, [errorcode = H | T]) -> 191 | V = case raw_socket:getsockopt(Socket, H) of 192 | {ok, N} -> N; 193 | Error -> throw(Error) 194 | end, 195 | [{errorcode, V} | internal_getopts(Socket, T)]; 196 | internal_getopts(Socket, [ionread = H | T]) -> 197 | V = case raw_socket:getsockopt(Socket, H) of 198 | {ok, N} -> N; 199 | Error -> throw(Error) 200 | end, 201 | [{ionread, V} | internal_getopts(Socket, T)]; 202 | internal_getopts(Socket, [active = H | T]) -> 203 | V = case raw_socket:getsockopt(Socket, H) of 204 | {ok, N} -> 205 | case N of 206 | ?ACTIVE_FALSE -> false; 207 | ?ACTIVE_TRUE -> true; 208 | ?ACTIVE_ONCE -> once; 209 | N when N>0 -> N; 210 | _ -> throw(N) 211 | end; 212 | Error -> throw(Error) 213 | end, 214 | [{active, V} | internal_getopts(Socket, T)]. 215 | 216 | to_ip_address(Address) when is_atom(Address) -> 217 | IPAddress = atom_to_list(Address), 218 | to_ip_address(IPAddress); 219 | to_ip_address(Address) when is_list(Address) -> 220 | case inet:getaddrs(Address, inet) of 221 | {ok, [H|_]} -> {ok, H}; 222 | Error -> Error 223 | end; 224 | to_ip_address({_,_,_,_} = Address) -> 225 | {ok, Address}. 226 | 227 | accept(Socket) -> 228 | accept(Socket, infinity). 229 | 230 | accept(Socket, Timeout) -> 231 | case raw_socket:accept(Socket) of 232 | {ok, NewSocket} -> {ok, NewSocket}; 233 | {error, eagain} -> 234 | case wait_for_readable(Timeout, Socket) of 235 | ok -> accept(Socket, Timeout); 236 | Error -> Error 237 | end; 238 | Error -> Error 239 | end. 240 | 241 | send(Socket, Bin) when is_binary(Bin)-> 242 | send(Socket, Bin, 0, size(Bin)); 243 | send(Socket, DataList) when is_list(DataList) -> 244 | {IoList, Size} = raw_socket:iolist_to_binary_list(DataList), 245 | sendv(Socket, IoList, 0, Size). 246 | 247 | %% send(Socket, DataList) when is_list(DataList) -> 248 | %% IoList1 = lists:flatten(DataList), 249 | %% {IoList2, Size} = convert_to_iolist(IoList1, {[], 0}), 250 | %% sendv(Socket, IoList2, 0, Size). 251 | 252 | %% convert_to_iolist([], {L, Size}) -> 253 | %% {lists:reverse(L), Size}; 254 | %% convert_to_iolist([H|_] = IoList, {L, Size}) when is_integer(H) andalso H >=0 255 | %% andalso H =< 255 -> 256 | %% {String, Remain} = get_string(IoList, []), 257 | %% Bin = list_to_binary(String), 258 | %% convert_to_iolist(Remain, {[Bin | L], Size + size(Bin)}); 259 | %% convert_to_iolist([H|T], {L, Size}) when is_binary(H) -> 260 | %% convert_to_iolist(T, {[H|L], Size + size(H)}); 261 | %% % may support other types ? 262 | %% convert_to_iolist(_, _) -> 263 | %% error(badarg). 264 | 265 | %% get_string([], Acc) -> 266 | %% {lists:reverse(Acc), []}; 267 | %% get_string([H|T], Acc) when is_integer(H) andalso H >= 0 andalso H =< 255 -> 268 | %% NewAcc = [H|Acc], 269 | %% get_string(T, NewAcc); 270 | %% get_string([H|_], _Acc) when is_integer(H) -> 271 | %% error(badarg); 272 | %% get_string(IoList, Acc) -> 273 | %% {lists:reverse(Acc), IoList}. 274 | 275 | send(_Socket, _Bin, End, End) -> 276 | ok; 277 | send(Socket, Bin, Off, End) -> 278 | case raw_socket:send(Socket, Bin, Off, End-Off) of 279 | {ok, Off2, _} -> 280 | send(Socket, Bin, Off2, End); 281 | {error, eagain} -> 282 | Timeout = get_send_timeout(Socket), 283 | case wait_for_writable(Timeout, Socket) of 284 | ok -> send(Socket, Bin, Off, End); 285 | Error -> Error 286 | end; 287 | Error -> Error 288 | end. 289 | 290 | sendv(_Socket, _IoList, End, End) -> 291 | ok; 292 | sendv(Socket, IoList, Off, End) -> 293 | case raw_socket:sendv(Socket, IoList, Off) of 294 | {ok, Off2, _} -> 295 | sendv(Socket, IoList, Off2, End); 296 | {error, eagain} -> 297 | Timeout = get_send_timeout(Socket), 298 | case wait_for_writable(Timeout, Socket) of 299 | ok -> sendv(Socket, IoList, Off, End); 300 | Error -> Error 301 | end; 302 | Error -> Error 303 | end. 304 | 305 | recv(Socket, Length) when is_integer(Length), Length >= 0 -> 306 | recv(Socket, Length, infinity). 307 | 308 | recv(Socket, 0, Timeout) -> 309 | case raw_socket:getsockopt(Socket, ionread) of 310 | {ok, 0} -> 311 | case wait_for_readable(Timeout, Socket) of 312 | ok -> 313 | case raw_socket:getsockopt(Socket, ionread) of 314 | {ok, 0} -> % socket closed ? 315 | recv(Socket, 100, Timeout); 316 | {ok, Count0} when Count0 > ?TCP_MAX_PACKET_SIZE-> 317 | recv(Socket, ?TCP_MAX_PACKET_SIZE, Timeout); 318 | {ok, Count1} -> 319 | recv(Socket, Count1, Timeout); 320 | Error -> Error 321 | end; 322 | Error -> Error 323 | end; 324 | {ok, Count0} when Count0 > ?TCP_MAX_PACKET_SIZE-> 325 | recv(Socket, ?TCP_MAX_PACKET_SIZE, Timeout); 326 | {ok, Count1} -> 327 | recv(Socket, Count1, Timeout); 328 | Error -> 329 | Error 330 | end; 331 | 332 | recv(Socket, Length, Timeout) when is_integer(Length), Length >= 0-> 333 | case raw_socket:alloc_buf(Length, Length) of 334 | {ok, Buf} -> recv_buf(Socket, Buf, Timeout); 335 | Error -> Error 336 | end. 337 | 338 | recv_buf(Socket, Buf, Timeout) -> 339 | case raw_socket:recv_buf(Socket, Buf) of 340 | ok -> 341 | raw_socket:convert_buf_to_binary(Buf); 342 | {error, eagain} -> 343 | case wait_for_readable(Timeout, Socket) of 344 | ok -> 345 | recv_buf(Socket, Buf, Timeout); 346 | Error -> Error 347 | end; 348 | Error -> Error 349 | end. 350 | 351 | 352 | wait_for_readable(Timeout, Socket) -> 353 | case raw_socket:enable_events(Socket, [readable], self()) of 354 | ok -> wait_for_event_readable(Timeout, Socket); 355 | Error -> Error 356 | end. 357 | wait_for_event_readable(Timeout, Socket) -> 358 | receive 359 | {socket_event, readable, Socket} -> 360 | flush_event(Socket, readable) 361 | after Timeout -> 362 | flush_event(Socket, readable), 363 | {error, etimedout} 364 | end. 365 | 366 | 367 | wait_for_writable(Timeout, Socket) -> 368 | case raw_socket:enable_events(Socket, [writable], self()) of 369 | ok -> wait_for_event_writable(Timeout, Socket); 370 | Error -> Error 371 | end. 372 | wait_for_event_writable(Timeout, Socket) -> 373 | receive 374 | {socket_event, writable, Socket} -> 375 | flush_event(Socket, writable); 376 | {tcp_error, Socket, Reason} -> 377 | {error, Reason}; 378 | {tcp_closed, Socket} -> 379 | {error, closed} 380 | after Timeout -> 381 | flush_event(Socket, writable), 382 | {error, etimedout} 383 | end. 384 | 385 | flush_all_events(Socket) -> 386 | case raw_socket:disable_events(Socket, [readable, writable]) of 387 | ok -> do_flush_all_events(Socket); 388 | Error -> Error 389 | end. 390 | 391 | do_flush_all_events(Socket) -> 392 | receive 393 | {socket_event, _, Socket} -> do_flush_all_events(Socket) 394 | after 0 -> 395 | ok 396 | end. 397 | 398 | flush_event(Socket, Event) -> 399 | case raw_socket:disable_events(Socket, [Event]) of 400 | ok -> do_flush_event(Socket, Event); 401 | Error -> Error 402 | end. 403 | 404 | do_flush_event(Socket, Event) -> 405 | receive 406 | {socket_event, Event, Socket} -> do_flush_event(Socket, Event) 407 | after 0 -> 408 | ok 409 | end. 410 | 411 | close(Socket) -> 412 | raw_socket:close(Socket). 413 | 414 | socket_info(Socket, Opt) -> 415 | raw_socket:socket_info(Socket, Opt). 416 | 417 | peername(Socket) -> 418 | case raw_socket:peername(Socket) of 419 | {ok, Bin} -> 420 | [F, P1,P0 | Addr] = erlang:binary_to_list(Bin), 421 | {IP, _} = get_ip(F, Addr), 422 | {ok, { IP, ?u16(P1, P0) }}; 423 | Error -> Error 424 | end. 425 | 426 | sockname(Socket) -> 427 | case raw_socket:sockname(Socket) of 428 | {ok, Bin} -> 429 | [F, P1,P0 | Addr] = erlang:binary_to_list(Bin), 430 | {IP, _} = get_ip(F, Addr), 431 | {ok, { IP, ?u16(P1, P0) }}; 432 | Error -> Error 433 | end. 434 | 435 | getstat(Socket) -> 436 | getstat(Socket, 437 | lists:map(fun(T) -> element(1, T) end, stats())). 438 | 439 | getstat(Socket, L) -> 440 | case encode_stats(L) of 441 | {ok, Bytes} -> 442 | try raw_socket:getstat(Socket, Bytes) of 443 | {ok, Data} -> {ok, decode_stats(Data)} 444 | catch 445 | E -> {error, E} 446 | end; 447 | Error -> Error 448 | end. 449 | 450 | decode_stats(L) -> 451 | lists:map(fun({StatIdx, StatValue})-> 452 | {element(1, lists:keyfind(StatIdx, 2, stats())), StatValue} 453 | end, L). 454 | 455 | encode_stats(L) -> 456 | try enc_stats(lists:usort(L)) of 457 | Result -> 458 | {ok, Result} 459 | catch 460 | Error -> 461 | {error, Error} 462 | end. 463 | 464 | enc_stats(L) -> 465 | lists:map(fun(X) -> 466 | case lists:keyfind(X, 1, stats()) of 467 | false -> throw(einval); 468 | T -> element(2, T) 469 | end 470 | end, L). 471 | 472 | stats() -> 473 | [ 474 | {recv_cnt, ?GEN_SOCKET_STAT_RECV_CNT}, 475 | {recv_max, ?GEN_SOCKET_STAT_RECV_MAX}, 476 | {recv_avg, ?GEN_SOCKET_STAT_RECV_AVG}, 477 | {recv_dvi, ?GEN_SOCKET_STAT_RECV_DVI}, 478 | {recv_oct, ?GEN_SOCKET_STAT_RECV_OCT}, 479 | {send_cnt, ?GEN_SOCKET_STAT_SEND_CNT}, 480 | {send_max, ?GEN_SOCKET_STAT_SEND_MAX}, 481 | {send_avg, ?GEN_SOCKET_STAT_SEND_AVG}, 482 | {send_pend, ?GEN_SOCKET_STAT_SEND_PEND}, 483 | {send_oct, ?GEN_SOCKET_STAT_SEND_OCT} 484 | ]. 485 | 486 | get_ip(?INET_AF_INET, Addr) -> 487 | get_ip4(Addr). 488 | get_ip4([A,B,C,D | T]) -> {{A,B,C,D},T}. 489 | 490 | 491 | dup_socket(S) -> 492 | raw_socket:dup_socket(S). 493 | 494 | get_send_timeout(Socket) -> 495 | {ok, Timeout} = raw_socket:getsockopt(Socket, send_timeout), 496 | case Timeout of 497 | 0 -> infinity; 498 | _ -> Timeout 499 | end. 500 | -------------------------------------------------------------------------------- /src/gen_socket.hrl: -------------------------------------------------------------------------------- 1 | %%---------------------------------------------------------------------------- 2 | %% Interface constants. 3 | %% 4 | %% This section must be "identical" to the corresponding in raw_socket.cpp 5 | %% 6 | 7 | -define(u16(X1,X0), 8 | (((X1) bsl 8) bor (X0))). 9 | 10 | %% family codes to open 11 | -define(INET_AF_INET, 1). 12 | -define(INET_AF_INET6, 2). 13 | -define(INET_AF_ANY, 3). % Fake for ANY in any address family 14 | -define(INET_AF_LOOPBACK, 4). % Fake for LOOPBACK in any address family 15 | 16 | -define(ACTIVE_FALSE, 0). 17 | -define(ACTIVE_TRUE, -1). 18 | -define(ACTIVE_ONCE, -2). 19 | 20 | -define(GEN_SOCKET_STAT_RECV_CNT, 0). 21 | -define(GEN_SOCKET_STAT_RECV_MAX, 1). 22 | -define(GEN_SOCKET_STAT_RECV_AVG, 2). 23 | -define(GEN_SOCKET_STAT_RECV_DVI, 3). 24 | -define(GEN_SOCKET_STAT_RECV_OCT, 4). 25 | -define(GEN_SOCKET_STAT_SEND_CNT, 5). 26 | -define(GEN_SOCKET_STAT_SEND_MAX, 6). 27 | -define(GEN_SOCKET_STAT_SEND_AVG, 7). 28 | -define(GEN_SOCKET_STAT_SEND_PEND, 8). 29 | -define(GEN_SOCKET_STAT_SEND_OCT, 9). 30 | 31 | -define(TCP_MAX_PACKET_SIZE, 16#4000000). 32 | -------------------------------------------------------------------------------- /src/gen_socket_app.erl: -------------------------------------------------------------------------------- 1 | -module(gen_socket_app). 2 | 3 | -behaviour(application). 4 | 5 | %% Application callbacks 6 | -export([start/2, stop/1, start/0]). 7 | 8 | %% =================================================================== 9 | %% Application callbacks 10 | %% =================================================================== 11 | 12 | start(_StartType, _StartArgs) -> 13 | gen_socket_sup:start_link(). 14 | 15 | stop(_State) -> 16 | ok. 17 | 18 | %% =================== 19 | start() -> 20 | application:ensure_started(gen_socket). 21 | -------------------------------------------------------------------------------- /src/gen_socket_poller.erl: -------------------------------------------------------------------------------- 1 | -module(gen_socket_poller). 2 | -behaviour(gen_server). 3 | -export([start_link/1, poller/1]). 4 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2, 5 | terminate/2, code_change/3]). 6 | 7 | -define(SPINCOUNT, 10). 8 | -define(INVOKE_MAXCNT, 0). 9 | 10 | -record(state, {poller=undefined}). 11 | 12 | start_link(Arg) -> 13 | gen_server:start_link(?MODULE, Arg, []). 14 | 15 | poller(To) -> 16 | gen_server:call(To, poller). 17 | 18 | init({Id, BindSched}) -> 19 | process_flag(trap_exit, false), 20 | Poller = spawn_link(fun() -> poller(Id, BindSched) end), 21 | State = #state{poller = Poller}, 22 | {ok, State}. 23 | 24 | handle_call(poller, _From, State) -> 25 | {reply, State#state.poller, State}. 26 | handle_cast(stop, State) -> 27 | {stop, normal, State}. 28 | handle_info(_Info, State) -> 29 | {noreply, State}. 30 | terminate(_Reason, _State) -> 31 | ok. 32 | code_change(_OldVsn, State, _Extra) -> 33 | {ok, State}. 34 | 35 | poller(Id, BindSched) -> 36 | case BindSched of 37 | true -> process_flag(scheduler, Id+1); 38 | _ -> ok 39 | end, 40 | {ok, PsMon} = raw_socket:alloc_psmon(Id), 41 | poller_loop(Id, PsMon, ?SPINCOUNT). 42 | 43 | poller_loop(Id, PsMon, 0) -> 44 | raw_socket:enable_psmon(PsMon), 45 | receive 46 | _Msg -> 47 | raw_socket:disable_psmon(PsMon), 48 | drain_message(), 49 | poller_loop(Id, PsMon, ?SPINCOUNT) 50 | %% after 300 -> 51 | %% raw_socket:disable_psmon(PsMon), 52 | %% drain_message(), 53 | %% poller_loop(Id, PsMon, 1) 54 | end; 55 | poller_loop(Id, PsMon, SpinCount) -> 56 | {ok, Num} = raw_socket:evrun(Id, ?INVOKE_MAXCNT), 57 | case Num of 58 | 0 -> erlang:yield(), poller_loop(Id, PsMon, SpinCount - 1); 59 | _ -> erlang:yield(), poller_loop(Id, PsMon, ?SPINCOUNT) 60 | end. 61 | 62 | drain_message() -> 63 | receive 64 | _Msg -> drain_message() 65 | after 0 -> 66 | ok 67 | end. 68 | -------------------------------------------------------------------------------- /src/gen_socket_sup.erl: -------------------------------------------------------------------------------- 1 | 2 | -module(gen_socket_sup). 3 | 4 | -behaviour(supervisor). 5 | 6 | %% API 7 | -export([start_link/0]). 8 | 9 | %% Supervisor callbacks 10 | -export([init/1]). 11 | 12 | %% Helper macro for declaring children of supervisor 13 | -define(CHILD(I, M, Type, Arg), {I, {M, start_link, [Arg]}, permanent, 5000, Type, [M]}). 14 | 15 | %% =================================================================== 16 | %% API functions 17 | %% =================================================================== 18 | 19 | start_link() -> 20 | supervisor:start_link({local, ?MODULE}, ?MODULE, []). 21 | 22 | %% =================================================================== 23 | %% Supervisor callbacks 24 | %% =================================================================== 25 | 26 | init([]) -> 27 | set_scheduler_ids(), 28 | BindPoller = application:get_env(gen_socket, bind_scheduler, true), 29 | BindSocketPolicy = application:get_env(gen_socket, bind_socket_policy, 1), 30 | io:format("BindPoller=~p, BindSocketPolicy=~p~n", [BindPoller, BindSocketPolicy]), 31 | N = raw_socket:total_pollsets(), 32 | raw_socket:set_bind_policy(BindSocketPolicy), 33 | Children = [?CHILD(child_name(Id), gen_socket_poller, worker, {Id, BindPoller}) 34 | || Id <- lists:seq(0, N-1)], 35 | {ok, { {one_for_one, 5, 10}, Children} }. 36 | 37 | child_name(Id) -> 38 | list_to_atom("gen_socket_poller" ++ integer_to_list(Id)). 39 | 40 | set_scheduler_ids() -> 41 | OldFlag = process_flag(trap_exit, true), 42 | Pid = spawn_link(fun set_sched_id/0), 43 | receive 44 | {'EXIT', Pid, Reason} -> Reason = normal 45 | end, 46 | process_flag(trap_exit, OldFlag). 47 | 48 | set_sched_id() -> 49 | Scheds = erlang:system_info(schedulers), 50 | set_sched_id(Scheds). 51 | set_sched_id(0) -> 52 | ok; 53 | set_sched_id(N) -> 54 | process_flag(scheduler, N), 55 | erlang:yield(), 56 | ok = raw_socket:set_scheduler_id(N), 57 | set_sched_id(N-1). 58 | -------------------------------------------------------------------------------- /src/raw_socket.erl: -------------------------------------------------------------------------------- 1 | -module(raw_socket). 2 | 3 | -ifdef(TEST). 4 | -include_lib("eunit/include/eunit.hrl"). 5 | -endif. 6 | 7 | -export([init/0, socket/0, bind/3, listen/2, close/1, 8 | enable_events/3, disable_events/2, 9 | recv/1, 10 | send/4, 11 | accept/1, 12 | connect/3, 13 | setsockopt/2, 14 | getsockopt/2, 15 | shutdown/2, 16 | controlling_process/2, 17 | alloc_buf/2, 18 | realloc_buf/3, 19 | free_buf/1, 20 | get_buf_info/1, 21 | get_buf_data/3, 22 | recv_buf/2, 23 | convert_buf_to_binary/1, 24 | alloc_psmon/1, 25 | enable_psmon/1, 26 | disable_psmon/1, 27 | total_pollsets/0, 28 | evrun/2, 29 | bind_pollset/2, 30 | getstat/2, 31 | socket_info/2, 32 | peername/1, 33 | sockname/1, 34 | set_scheduler_id/1, 35 | dup_socket/1, 36 | sendv/3, 37 | ev_info/1, 38 | socketonline_info/0, 39 | set_bind_policy/1, 40 | iolist_to_binary_list/1 41 | ]). 42 | -on_load(init/0). 43 | 44 | -define(NOT_LOAD, erlang:nif_error(nif_not_loaded)). 45 | 46 | init() -> 47 | Cmd = priv_dir() ++ "/raw_socket", 48 | erlang:load_nif(Cmd, 0). 49 | 50 | socket() -> ?NOT_LOAD. 51 | 52 | bind(_Sock, _Ip, _Port) -> ?NOT_LOAD. 53 | 54 | listen(_Sock, _Backlog) -> ?NOT_LOAD. 55 | 56 | close(_Sock) -> ?NOT_LOAD. 57 | 58 | enable_events(_Sock, _Events, _Pid) -> ?NOT_LOAD. 59 | 60 | disable_events(_Sock, _Events) -> ?NOT_LOAD. 61 | 62 | accept(_Sock) -> ?NOT_LOAD. 63 | 64 | connect(_Sock, _Ip, _Port) -> ?NOT_LOAD. 65 | 66 | setsockopt(_Sock, _Opt) -> ?NOT_LOAD. 67 | 68 | getsockopt(_Sock, _Opt) -> ?NOT_LOAD. 69 | 70 | recv(_Sock) -> ?NOT_LOAD. 71 | 72 | send(_Sock, _Bin, _Off, _Len) -> ?NOT_LOAD. 73 | 74 | shutdown(_Sock, _Flags) -> ?NOT_LOAD. 75 | 76 | controlling_process(_Sock, _NewOwer) -> ?NOT_LOAD. 77 | 78 | alloc_buf(_Cap, _Size) -> ?NOT_LOAD. 79 | 80 | realloc_buf(_BufObj, _Cap, _Size) -> ?NOT_LOAD. 81 | 82 | free_buf(_BufObj) -> ?NOT_LOAD. 83 | 84 | get_buf_info(_BufObj) -> ?NOT_LOAD. 85 | 86 | get_buf_data(_BufObj, _Off, _Len) -> ?NOT_LOAD. 87 | 88 | recv_buf(_Sock, _BufObj) -> ?NOT_LOAD. 89 | 90 | convert_buf_to_binary(_BufObj) -> ?NOT_LOAD. 91 | 92 | alloc_psmon(_PsId) -> ?NOT_LOAD. 93 | 94 | enable_psmon(_PsRes) -> ?NOT_LOAD. 95 | 96 | disable_psmon(_PsRes) -> ?NOT_LOAD. 97 | 98 | total_pollsets() -> ?NOT_LOAD. 99 | 100 | evrun(_PsId, _MaxInvoking) -> ?NOT_LOAD. 101 | 102 | bind_pollset(_Sock, _Pollset) -> ?NOT_LOAD. 103 | 104 | getstat(_Sock, _Bytes) -> ?NOT_LOAD. 105 | 106 | socket_info(_Sock, _Opt) -> ?NOT_LOAD. 107 | 108 | peername(_Sockete) -> ?NOT_LOAD. 109 | 110 | sockname(_Sockete) -> ?NOT_LOAD. 111 | 112 | set_scheduler_id(_SchedId) -> ?NOT_LOAD. 113 | 114 | dup_socket(_Sock) -> ?NOT_LOAD. 115 | 116 | sendv(_Sock, _IoList, _Off) -> ?NOT_LOAD. 117 | 118 | ev_info(_Sock) -> ?NOT_LOAD. 119 | 120 | socketonline_info() -> ?NOT_LOAD. 121 | 122 | set_bind_policy(_Policy) -> ?NOT_LOAD. 123 | 124 | iolist_to_binary_list(_IOList) -> ?NOT_LOAD. 125 | 126 | priv_dir() -> 127 | case code:priv_dir(acs) of 128 | {error, bad_name} -> 129 | case code:which(?MODULE) of 130 | File when not is_list(File) -> "../priv"; 131 | File -> filename:join([filename:dirname(File),"../priv"]) 132 | end; 133 | F -> F 134 | end. 135 | -------------------------------------------------------------------------------- /test/gensocket_test.erl: -------------------------------------------------------------------------------- 1 | -module(gensocket_test). 2 | -compile(export_all). 3 | 4 | -ifdef(TEST). 5 | -include_lib("eunit/include/eunit.hrl"). 6 | -define(PROXY_PORT, 8009). 7 | -define(SERVER_PORT, 8010). 8 | -define(IP, "127.0.0.1"). 9 | -define(OPTS, [{backlog, 128}, {reuseaddr, true}, {nodelay, true}]). 10 | -define(MSG, <<"abc">>). 11 | -define(INTEGER, 88). 12 | -define(ACTIVEN, 43). 13 | 14 | init_test() -> 15 | application:start(gen_socket). 16 | 17 | basic_socket_operation_test_() -> 18 | {timeout, 60, ?_test(basic_socket_operation_test__())}. 19 | 20 | basic_socket_operation_test__() -> 21 | PollSetSocketInfo = raw_socket:socketonline_info(), 22 | ?debugFmt("==================== basic_socket_operation_test begin ===================================", []), 23 | LSocket = listen(?SERVER_PORT), 24 | {ServerSocket, ClientSocket, ClientPid} = get_socket_pair(LSocket), 25 | ?assertNotMatch(ServerSocket, ClientSocket), 26 | 27 | %% Test setopts(), getopts() 28 | Opts1 = [{reuseaddr, false}, {nodelay, false}], 29 | ?assertMatch(ok, gen_socket:setopts(LSocket, Opts1)), 30 | ?assertMatch({ok, Opts1}, gen_socket:getopts(LSocket, [reuseaddr, nodelay])), 31 | 32 | Opts2 = [{reuseaddr, true}, {nodelay, false}], 33 | ?assertMatch(ok, gen_socket:setopts(LSocket, Opts2)), 34 | ?assertMatch({ok, Opts2}, gen_socket:getopts(LSocket, [reuseaddr, nodelay])), 35 | 36 | 37 | %% Test peername(), sockname() 38 | PeerNameServer = gen_socket:peername(ServerSocket), 39 | SockNameServer = gen_socket:sockname(ServerSocket), 40 | PeernameClient = gen_socket:peername(ClientSocket), 41 | SocknameClient = gen_socket:sockname(ClientSocket), 42 | 43 | %%?debugFmt("peer_server: ~p, sock_server: ~p, peer_client: ~p, sock_client: ~p, ~n", [PeerNameServer, SockNameServer, PeernameClient, SocknameClient]), 44 | 45 | ?assertMatch(PeerNameServer, SocknameClient), 46 | ?assertMatch(SockNameServer, PeernameClient), 47 | 48 | %% Test send(), recv(), controlling_process() 49 | ?assertMatch(ok, gen_socket:send(ClientSocket, [[[?INTEGER]], "abc", [<<>>], [?MSG , <<>>, ?MSG]])), 50 | ?assertMatch(ok, gen_socket:send(ClientSocket, ?MSG)), 51 | ?assertMatch(ok, gen_socket:send(ClientSocket, ?MSG)), 52 | 53 | ?debugFmt("basic_socket_operation_test() send ok~n", []), 54 | 55 | %% Test {error,enomem} 56 | catch gen_socket:recv(ServerSocket, -1), 57 | ?assertMatch({error,enomem}, gen_socket:recv(ServerSocket, 16#4000000 + 1)), 58 | case gen_socket:recv(ServerSocket, 1) of 59 | {ok, Bin0} -> 60 | ?assertMatch(Bin0, <>), 61 | ok; 62 | Error0 -> 63 | ?assert(false) 64 | end, 65 | 66 | case gen_socket:recv(ServerSocket, erlang:size(?MSG)) of 67 | {ok, Bin1} -> 68 | ?assertMatch(Bin1, ?MSG), 69 | ok; 70 | Error1 -> 71 | ?assert(false) 72 | end, 73 | 74 | case gen_socket:recv(ServerSocket, erlang:size(?MSG)) of 75 | {ok, Bin2} -> 76 | ?assertMatch(Bin2, ?MSG), 77 | ok; 78 | Error2 -> 79 | ?assert(false) 80 | end, 81 | 82 | case gen_socket:recv(ServerSocket, erlang:size(?MSG)) of 83 | {ok, Bin3} -> 84 | ?assertMatch(Bin3, ?MSG), 85 | ok; 86 | Error3 -> 87 | ?assert(false) 88 | end, 89 | 90 | ?assertMatch({ok, <<"abcabc">>}, gen_socket:recv(ServerSocket, 0)), 91 | ?debugFmt("pollset socket count info:~p~n", [raw_socket:socketonline_info()]), 92 | ?debugFmt("ClientSocket ev_info:~p~n", [raw_socket:ev_info(ClientSocket)]), 93 | ?debugFmt("ClientSocket bind_id:~p~n", [raw_socket:socket_info(LSocket, bind_id)]), 94 | ?debugFmt("ClientSocket bind_id:~p~n", [raw_socket:socket_info(ServerSocket, bind_id)]), 95 | ?debugFmt("ClientSocket bind_id:~p~n", [raw_socket:socket_info(ClientSocket, bind_id)]), 96 | 97 | ?assertMatch({bind_id, 24}, raw_socket:socket_info(LSocket, bind_id)), 98 | ?assertMatch({bind_id, 23}, raw_socket:socket_info(ClientSocket, bind_id)), 99 | ?assertMatch({bind_id, 22}, raw_socket:socket_info(ServerSocket, bind_id)), 100 | 101 | %% Test getstat() 102 | ClientStats = [{recv_avg,0}, 103 | {recv_cnt,0}, 104 | {recv_dvi,0}, 105 | {recv_max,0}, 106 | {recv_oct,0}, 107 | {send_avg,5}, %% (3 + 3 + 3 + 1) / 2 = 5 108 | {send_cnt,2}, 109 | {send_max,7}, 110 | {send_oct,10}, 111 | {send_pend,0}], 112 | 113 | ServerStats = [{recv_avg,2}, 114 | {recv_cnt,4}, 115 | {recv_dvi,0}, 116 | {recv_max,3}, 117 | {recv_oct,10}, 118 | {send_avg,0}, 119 | {send_cnt,0}, 120 | {send_max,0}, 121 | {send_oct,0}, 122 | {send_pend,0}], 123 | 124 | ?debugFmt("client:~n ~p~n", [gen_socket:getstat(ClientSocket)]), 125 | ?debugFmt("server:~n ~p~n", [gen_socket:getstat(ServerSocket)]), 126 | 127 | %%?assertMatch({ok, ClientStats}, gen_socket:getstat(ClientSocket)), 128 | %%?assertMatch({ok, ServerStats}, gen_socket:getstat(ServerSocket)), 129 | %%?debugFmt("getstat~p~n", [gen_socket:getstat(ServerSocket)]), 130 | 131 | %% Test close() 132 | ?assertMatch(ok, gen_socket:close(ServerSocket)), 133 | ?assertMatch(ok, gen_socket:close(ClientSocket)), 134 | ?assertMatch(ok, gen_socket:close(LSocket)), 135 | 136 | ?assertMatch({error, closed}, gen_socket:send(ClientSocket, ?MSG)), 137 | ?assertMatch({error, closed}, gen_socket:send(ClientSocket, [?MSG])), 138 | 139 | ?assertMatch(PollSetSocketInfo, raw_socket:socketonline_info()). 140 | 141 | listen(Port) -> 142 | {ok, LSocket} = gen_socket:listen(Port, ?OPTS), 143 | ?debugFmt("Listen ok ~p~n", [Port]), 144 | LSocket. 145 | 146 | accept(Parent, LSocket) -> 147 | receive 148 | ready -> ok 149 | end, 150 | ?debugFmt("Begin Accept LSocket: ~p~n", [LSocket]), 151 | {ok, S} = gen_socket:accept(LSocket), 152 | ?debugFmt("Accept NewSocket: ~p~n", [S]), 153 | ok = gen_socket:controlling_process(S, Parent), 154 | ok = gen_socket:controlling_process(LSocket, Parent), 155 | Parent ! {server, S}. 156 | 157 | connect(Parent) -> 158 | {ok, S} = gen_socket:connect(?IP, ?SERVER_PORT, []), 159 | ?debugFmt("Connect ok ~p~n", [S]), 160 | ok = gen_socket:controlling_process(S, Parent), 161 | Parent ! {client, S}, 162 | receive 163 | hold -> hold 164 | end. 165 | 166 | %% Spwan 2 process: client & server 167 | %% Return: {ServerSocket, ClientSocket, ClientPid}. 168 | get_socket_pair(LSocket) -> 169 | Pid = self(), 170 | ServerPid = spawn(fun() -> accept(Pid, LSocket) end), 171 | ok = gen_socket:controlling_process(LSocket, ServerPid), 172 | ServerPid ! ready, 173 | 174 | ClientPid = spawn(fun() -> connect(Pid) end), 175 | ?debugFmt("Spawn client&server process ok~n", []), 176 | 177 | ServerSocket = receive 178 | {server, SS1} -> SS1 179 | end, 180 | ClientSocket = receive 181 | {client, SS2} -> SS2 182 | end, 183 | {ServerSocket, ClientSocket, ClientPid}. 184 | 185 | 186 | proxy_test_() -> 187 | {timeout, 60, ?_test(proxy_test__())}. 188 | proxy_test__() -> 189 | ?debugFmt("==================== proxy_test begin ===================================", []), 190 | ServerPid = spawn(fun() -> echo_server() end), 191 | ProxyPid = spawn(fun() -> proxy_server() end), 192 | 193 | {ok, ClientSocket} = gen_socket:connect(?IP, ?PROXY_PORT, []), 194 | ?assertMatch(ok, gen_socket:send(ClientSocket, ?MSG)), 195 | {ok, Data} = gen_socket:recv(ClientSocket, erlang:size(?MSG)), 196 | ?assertMatch(Data, ?MSG), 197 | ?assertMatch(ok, gen_socket:close(ClientSocket)), 198 | ?debugFmt("Proxy Test End", []), 199 | ?debugFmt("==================== proxy_test end ===================================~n~n", []). 200 | 201 | echo_server() -> 202 | {ok, LSocket} = gen_socket:listen(?SERVER_PORT, ?OPTS), 203 | {ok, ServerSocket} = gen_socket:accept(LSocket), 204 | {ok, Data} = gen_socket:recv(ServerSocket, erlang:size(?MSG)), 205 | ?assertMatch(ok, gen_socket:send(ServerSocket, Data)), 206 | 207 | ?assertMatch(ok, gen_socket:close(LSocket)), 208 | ?assertMatch(ok, gen_socket:close(ServerSocket)). 209 | 210 | proxy_server() -> 211 | {ok, LSocket} = gen_socket:listen(?PROXY_PORT, ?OPTS), 212 | {ok, ClientSocket} = gen_socket:accept(LSocket), 213 | {ok, ServerSocket} = gen_socket:connect(?IP, ?SERVER_PORT, []), 214 | 215 | ?assertMatch(ok, gen_socket:setopts(ClientSocket, [{active, ?ACTIVEN}])), 216 | ?assertMatch(ok, gen_socket:setopts(ServerSocket, [{active, ?ACTIVEN}])), 217 | proxy_loop(ServerSocket, ClientSocket). 218 | 219 | proxy_loop(ServerSocket, ClientSocket) -> 220 | receive 221 | {tcp, ServerSocket, Data} -> 222 | ?debugFmt("Proxy Server, received a msg from server, Socket: ~p, msg: ~p~n", [ServerSocket, Data]), 223 | ?assertMatch(ok, gen_socket:send(ClientSocket, Data)), 224 | proxy_loop(ServerSocket, ClientSocket); 225 | {tcp, ClientSocket, Data} -> 226 | ?debugFmt("Proxy Server, received a msg from client, Socket: ~p, msg: ~p~n", [ClientSocket , Data]), 227 | ?assertMatch(ok, gen_socket:send(ServerSocket, Data)), 228 | proxy_loop(ServerSocket, ClientSocket); 229 | {tcp_closed, ServerSocket} -> 230 | ?debugFmt("Proxy Server, ServerSocket closed: ~p~n", [ServerSocket]), 231 | proxy_loop(ServerSocket, ClientSocket); 232 | {tcp_closed, ClientSocket} -> 233 | ?debugFmt("Proxy Server, ClientSocket closed: ~p~n", [ClientSocket]), 234 | proxy_loop(ServerSocket, ClientSocket); 235 | {tcp_passive, Socket} -> 236 | ?debugFmt("Proxy Server, Passive received, Socket: ~p~n", [Socket]), 237 | ok = gen_socket:setopts(Socket, [{active, 1}]), 238 | proxy_loop(ServerSocket, ClientSocket); 239 | {tcp_error, Socket, _Reason} -> 240 | ?debugFmt("Proxy Server, Error, Socket: ~p, Reason~n", [Socket, _Reason]), 241 | {stop, ssock_error}; 242 | _Msg -> 243 | ?debugFmt("Proxy Server, Unkown Msg: ~p~n", [_Msg]), 244 | proxy_loop(ServerSocket, ClientSocket) 245 | end. 246 | 247 | 248 | multi_process_accept_test_() -> 249 | {timeout, 60, ?_test(multi_process_accept_test__())}. 250 | multi_process_accept_test__() -> 251 | ?debugFmt("==================== multi_process_accept_test begin ===================================", []), 252 | LSocket = listen(?SERVER_PORT), 253 | Pid = self(), 254 | P1 = spawn(fun() -> do_multi_process_accept(LSocket, Pid) end), 255 | receive 256 | ready -> ok; 257 | _ -> ?assert(false) 258 | end, 259 | 260 | P2 = spawn(fun() -> do_multi_process_accept(LSocket, Pid) end), 261 | receive 262 | ready -> ok; 263 | _ -> ?assert(false) 264 | end, 265 | 266 | L1 = spawn(fun() -> do_multi_process_connect() end), 267 | L2 = spawn(fun() -> do_multi_process_connect() end), 268 | 269 | receive 270 | {fin, P11} when P11=:=P1 orelse P11=:=P2 -> 271 | ?debugFmt("multi_process_accept_test, P1 accept ok~n", []), 272 | ?assert(true); 273 | R1 -> 274 | ?debugFmt("1, multi_process_accept_test, unknown msg: ~p~n", [R1]), 275 | ?assert(false) 276 | end, 277 | 278 | receive 279 | {fin, P22} when P22=:=P1 orelse P22=:=P2 -> 280 | ?assert(true), 281 | ?debugFmt("multi_process_accept_test, P2 accept ok~n", []); 282 | R2 -> 283 | ?debugFmt("2, multi_process_accept_test, unknown msg: ~p~n", [R2]), 284 | ?assert(false) 285 | end, 286 | 287 | L3 = spawn(fun() -> do_multi_process_connect() end), 288 | L4 = spawn(fun() -> do_multi_process_connect() end), 289 | L5 = spawn(fun() -> do_multi_process_connect() end), 290 | 291 | P3 = spawn(fun() -> do_multi_process_accept(LSocket, Pid) end), 292 | receive 293 | ready -> ok 294 | end, 295 | 296 | P4 = spawn(fun() -> do_multi_process_accept(LSocket, Pid) end), 297 | receive 298 | ready -> ok 299 | end, 300 | 301 | P5 = spawn(fun() -> do_multi_process_accept(LSocket, Pid) end), 302 | receive 303 | ready -> ok 304 | end, 305 | 306 | receive 307 | {fin, P33} when P33=:=P3 orelse P33=:=P4 orelse P33=:=P5 -> 308 | ?debugFmt("multi_process_accept_test, P3 accept ok~n", []), 309 | ?assert(true); 310 | R3 -> 311 | ?debugFmt("3, multi_process_accept_test, unknown msg: ~p~n", [R3]), 312 | ?assert(false) 313 | end, 314 | 315 | receive 316 | {fin, P44} when P44=:=P3 orelse P44=:=P4 orelse P44=:=P5 -> 317 | ?debugFmt("multi_process_accept_test, P4 accept ok", []), 318 | ?assert(true); 319 | R4 -> 320 | ?debugFmt("4, multi_process_accept_test, unknown msg: ~p", [R4]), 321 | ?assert(false) 322 | end, 323 | 324 | receive 325 | {fin, P55} when P55=:=P3 orelse P55=:=P4 orelse P55=:=P5 -> 326 | ?debugFmt("multi_process_accept_test, P5 accept ok~n", []), 327 | ?assert(true); 328 | R5 -> 329 | ?debugFmt("5, multi_process_accept_test, unknown msg: ~p~n", [R5]), 330 | ?assert(false) 331 | end, 332 | 333 | ?assertMatch(ok, gen_socket:close(LSocket)), 334 | ?debugFmt("==================== multi_process_accept_test end ===================================~n~n", []). 335 | 336 | do_multi_process_connect() -> 337 | {ok, ServerSocket} = gen_socket:connect(?IP, ?SERVER_PORT, []), 338 | ?assertMatch(ok, gen_socket:close(ServerSocket)). 339 | 340 | do_multi_process_accept(LSocket, Parent) -> 341 | {ok, NewSock} = gen_socket:dup_socket(LSocket), 342 | ?debugFmt("do_multi_process_accept begin, LSocket: ~p, NewLSocket: ~p~n", [LSocket, NewSock]), 343 | Parent ! ready, 344 | {ok, ClientSocket} = gen_socket:accept(NewSock), 345 | ?assertMatch(ok, gen_socket:close(ClientSocket)), 346 | ?debugFmt("do_multi_process_accept end~n", []), 347 | Parent ! {fin, self()}. 348 | 349 | %% receive_msg in other process 350 | multi_process_recv_test_() -> 351 | {timeout, 60, ?_test(multi_process_recv_test__())}. 352 | 353 | multi_process_recv_test__() -> 354 | ?debugFmt("==================== multi_process_recv_test begin ===================================", []), 355 | LSocket = listen(?SERVER_PORT), 356 | {ServerSocket, ClientSocket, ClientPid} = get_socket_pair(LSocket), 357 | ?assertNotMatch(ServerSocket, ClientSocket), 358 | 359 | Opts = [{active, false}], 360 | ?assertMatch(ok, gen_socket:setopts(ServerSocket, Opts)), 361 | ?assertMatch(ok, gen_socket:setopts(ClientSocket, Opts)), 362 | 363 | Self = self(), 364 | ReceivePid = spawn(fun() -> do_receive(ServerSocket, ClientSocket, Self) end), 365 | 366 | receive 367 | ready -> ok; 368 | R0 -> 369 | ?debugFmt("0, multi_process_recv_test received an unkonw msg: ~p~n", [R0]), 370 | ?assert(false) 371 | end, 372 | 373 | %% Insure do_receive recv() run before send 374 | timer:sleep(1000), 375 | ?assertMatch(ok, gen_socket:send(ClientSocket, [?INTEGER])), 376 | 377 | receive 378 | ready -> ok; 379 | R1 -> 380 | ?debugFmt("1, multi_process_recv_test received an unkonw msg: ~p~n", [R1]), 381 | ?assert(false) 382 | end, 383 | %% Insure do_receive recv() run before send 384 | timer:sleep(1000), 385 | ?assertMatch(ok, gen_socket:send(ClientSocket, ?MSG)), 386 | 387 | receive 388 | fin -> ok; 389 | R2 -> 390 | ?debugFmt("multi_process_recv_test received an unkonw msg: ~p~n", [R2]), 391 | ?assert(false) 392 | end, 393 | ?assertMatch(ok, gen_socket:send(ClientSocket, ?MSG)), 394 | 395 | case gen_socket:recv(ServerSocket, erlang:size(?MSG)) of 396 | {ok, Bin3} -> 397 | ?assertMatch(Bin3, ?MSG), 398 | ok; 399 | Error3 -> 400 | ?assert(false) 401 | end, 402 | 403 | %% Test close() 404 | ?assertMatch(ok, gen_socket:close(ServerSocket)), 405 | ?assertMatch(ok, gen_socket:close(ClientSocket)), 406 | ?assertMatch(ok, gen_socket:close(LSocket)), 407 | ?debugFmt("==================== multi_process_recv_test end ===================================~n~n", []). 408 | 409 | 410 | 411 | do_receive(ServerSocket, ClientSocket, Parent) -> 412 | ?debugFmt("Another process begin to recv 3 msg~n", []), 413 | Parent ! ready, 414 | case gen_socket:recv(ServerSocket, 1) of 415 | {ok, Bin0} -> 416 | ?assertMatch(Bin0, <>), 417 | ok; 418 | Error0 -> 419 | ?assert(false) 420 | end, 421 | 422 | Parent ! ready, 423 | case gen_socket:recv(ServerSocket, erlang:size(?MSG)) of 424 | {ok, Bin1} -> 425 | ?assertMatch(Bin1, ?MSG), 426 | ok; 427 | Error1 -> 428 | ?assert(false) 429 | end, 430 | 431 | 432 | ?assertMatch(ok, gen_socket:send(ClientSocket, ?MSG)), 433 | case gen_socket:recv(ServerSocket, erlang:size(?MSG)) of 434 | {ok, Bin2} -> 435 | ?assertMatch(Bin2, ?MSG), 436 | ok; 437 | Error2 -> 438 | ?assert(false) 439 | end, 440 | 441 | ?debugFmt("Another process end to recv 3 msg~n", []), 442 | Parent ! fin. 443 | 444 | drain_message() -> 445 | receive 446 | _Msg -> 447 | ?debugFmt("Message: ~p~n", [_Msg]), 448 | drain_message() 449 | after 0 -> 450 | ok 451 | end. 452 | 453 | -endif. 454 | -------------------------------------------------------------------------------- /test/simple_client.erl: -------------------------------------------------------------------------------- 1 | -module(simple_client). 2 | -export([start/0]). 3 | 4 | start() -> 5 | %% MUST start gen_socket app!!! 6 | application:start(gen_socket), 7 | {ok, S} = gen_socket:connect("127.0.0.1", 8008, []), 8 | ok = gen_socket:setopts(S, [{active, true}]), 9 | loop(S, 5). 10 | 11 | loop(S, 0) -> 12 | gen_socket:close(S); 13 | loop(S, N) -> 14 | gen_socket:send(S, <<"hello">>), 15 | receive 16 | {tcp, S, D} -> 17 | io:format("client receive data: ~p~n", [D]), 18 | loop(S, N-1); 19 | {tcp_closed, S} -> 20 | io:format("client socket closed~n", []); 21 | {tcp_error, S, R} -> 22 | io:format("client socket error, Reason:~p ~n", [R]); 23 | Err -> 24 | io:format("client socket receive error, Err:~p ~n", [Err]) 25 | end. 26 | -------------------------------------------------------------------------------- /test/simple_server.erl: -------------------------------------------------------------------------------- 1 | -module(simple_server). 2 | -export([start/0]). 3 | 4 | start() -> 5 | %% MUST start gen_socket app!!! 6 | application:start(gen_socket), 7 | {ok, L} = gen_socket:listen(8008, 8 | [{backlog, 128}, 9 | {reuseaddr, true}, 10 | {nodelay, true}]), 11 | do_accept(L). 12 | 13 | do_accept(L) -> 14 | {ok, S} = gen_socket:accept(L), 15 | io:format("server accept a new socket~n"), 16 | Pid = spawn(fun() -> loop(S) end), 17 | gen_socket:controlling_process(S, Pid), 18 | do_accept(L). 19 | 20 | loop(S) -> 21 | case gen_socket:recv(S, 0) of 22 | {ok, D} -> 23 | io:format("server receive data: ~p~n", [D]), 24 | gen_socket:send(S, D), 25 | loop(S); 26 | {error, closed} -> 27 | io:format("server socket closed~n", []); 28 | Err -> 29 | io:format("server socket error: ~p~n", [Err]) 30 | end. 31 | --------------------------------------------------------------------------------