├── test ├── t10.expected ├── t12.expected ├── t14.expected ├── t15.expected ├── t20.expected ├── t21.expected ├── t16.expected ├── t17.expected ├── t18.expected ├── t23.expected ├── t1.expected ├── t19.expected ├── t33.expected ├── t5.expected ├── t11.expected ├── t13.expected ├── t22.expected ├── t25.expected ├── t30.expected ├── t29.expected ├── t4.expected ├── t8.expected ├── t27.expected ├── t32.expected ├── t1.ml ├── t31.expected ├── t3.expected ├── t9.expected ├── t21.ml ├── t6.expected ├── t7.expected ├── t10.ml ├── dune ├── t20.ml ├── t18.ml ├── t14.ml ├── t31.ml ├── t17.ml ├── t2.expected ├── t12.ml ├── t5.ml ├── t24.ml ├── t6.ml ├── t22.ml ├── t27.ml ├── t33.ml ├── t19.ml ├── t11.ml ├── t25.ml ├── t3.ml ├── t29.ml ├── t15.ml ├── t26.expected ├── t26.ml ├── t30.ml ├── t7.ml ├── t8.ml ├── t4.ml ├── t13.ml ├── t32.ml ├── t16.ml ├── t9.ml ├── t28.ml ├── t2.ml ├── t23.ml └── t28.expected ├── dune-project ├── fiber_coro.cx ├── bench ├── dune └── bench.ml ├── libev ├── autogen.sh ├── README.embed ├── update_ev_c ├── update_symbols ├── Symbols.event ├── configure.ac ├── update_ev_wrap ├── Makefile.am ├── Symbols.ev ├── libev.m4 ├── LICENSE ├── README ├── import_libevent ├── ev_poll.c ├── ev_win32.c ├── ev_wrap.h ├── event.h ├── ev_vars.h ├── ev_port.c ├── ev_kqueue.c ├── event_compat.h ├── ev_select.c ├── ev_epoll.c └── event.c ├── dune-workspace ├── .dir-locals.el ├── examples ├── dune ├── producer_consumer.ml └── socket_io.ml ├── libcoro ├── README ├── LICENSE ├── coro.h └── coro.c ├── fiber.opam ├── Makefile ├── dune ├── readme.md ├── fiber_ev.cx ├── fiber_ev.h ├── fiber.h ├── fiber.ml ├── fiber.mli └── fiber_stubs.c /test/t10.expected: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/t12.expected: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/t14.expected: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/t15.expected: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/t20.expected: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/t21.expected: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/t16.expected: -------------------------------------------------------------------------------- 1 | ok 2 | -------------------------------------------------------------------------------- /test/t17.expected: -------------------------------------------------------------------------------- 1 | ok 2 | -------------------------------------------------------------------------------- /test/t18.expected: -------------------------------------------------------------------------------- 1 | ok 2 | -------------------------------------------------------------------------------- /test/t23.expected: -------------------------------------------------------------------------------- 1 | 204468 2 | -------------------------------------------------------------------------------- /dune-project: -------------------------------------------------------------------------------- 1 | (lang dune 1.1) 2 | -------------------------------------------------------------------------------- /test/t1.expected: -------------------------------------------------------------------------------- 1 | hello, world 2 | -------------------------------------------------------------------------------- /test/t19.expected: -------------------------------------------------------------------------------- 1 | 1st 2 | 2nd 3 | -------------------------------------------------------------------------------- /test/t33.expected: -------------------------------------------------------------------------------- 1 | ok 2 | ok 3 | -------------------------------------------------------------------------------- /test/t5.expected: -------------------------------------------------------------------------------- 1 | ok 2 | ret 3 | -------------------------------------------------------------------------------- /fiber_coro.cx: -------------------------------------------------------------------------------- 1 | #include "libcoro/coro.c" -------------------------------------------------------------------------------- /test/t11.expected: -------------------------------------------------------------------------------- 1 | sigusr1 2 | ok 3 | -------------------------------------------------------------------------------- /test/t13.expected: -------------------------------------------------------------------------------- 1 | hello, world 2 | -------------------------------------------------------------------------------- /test/t22.expected: -------------------------------------------------------------------------------- 1 | loop 2 | exit 3 | -------------------------------------------------------------------------------- /test/t25.expected: -------------------------------------------------------------------------------- 1 | hello, world 2 | -------------------------------------------------------------------------------- /test/t30.expected: -------------------------------------------------------------------------------- 1 | fiber 2 | exit 3 | -------------------------------------------------------------------------------- /test/t29.expected: -------------------------------------------------------------------------------- 1 | Invalid_argument("Fiber.yield") 2 | -------------------------------------------------------------------------------- /test/t4.expected: -------------------------------------------------------------------------------- 1 | Invalid_argument("Fiber.resume") 2 | -------------------------------------------------------------------------------- /test/t8.expected: -------------------------------------------------------------------------------- 1 | 1st 2 | 2nd 3 | 3rd 4 | done 5 | -------------------------------------------------------------------------------- /test/t27.expected: -------------------------------------------------------------------------------- 1 | before_wake 2 | after_wake 3 | ok 4 | -------------------------------------------------------------------------------- /test/t32.expected: -------------------------------------------------------------------------------- 1 | 1 -> 1 2 | 2 -> 2 3 | 3 -> 3 4 | 4 -> 4 5 | 5 -> 5 6 | -------------------------------------------------------------------------------- /bench/dune: -------------------------------------------------------------------------------- 1 | (executable 2 | (name bench) 3 | (libraries fiber benchmark)) 4 | -------------------------------------------------------------------------------- /libev/autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | autoreconf --install --symlink --force 4 | -------------------------------------------------------------------------------- /dune-workspace: -------------------------------------------------------------------------------- 1 | (lang dune 1.1) 2 | 3 | ; Default build profile. 4 | (profile release) 5 | -------------------------------------------------------------------------------- /test/t1.ml: -------------------------------------------------------------------------------- 1 | 2 | let _ = 3 | Fiber.create print_string "hello, world\n" |> Fiber.resume 4 | -------------------------------------------------------------------------------- /test/t31.expected: -------------------------------------------------------------------------------- 1 | 1 2 | 2 3 | 3 4 | 4 5 | 5 6 | 6 7 | 7 8 | 8 9 | 9 10 | 10 11 | -------------------------------------------------------------------------------- /test/t3.expected: -------------------------------------------------------------------------------- 1 | a 10 2 | b 9 3 | a 8 4 | b 7 5 | a 6 6 | b 5 7 | a 4 8 | b 3 9 | a 2 10 | b 1 11 | -------------------------------------------------------------------------------- /test/t9.expected: -------------------------------------------------------------------------------- 1 | 1st 2 2 | 2nd 2 3 | 3rd 2 4 | 1st 1 5 | 2nd 1 6 | 3rd 1 7 | 1st 0 8 | 2nd 0 9 | 3rd 0 10 | -------------------------------------------------------------------------------- /libev/README.embed: -------------------------------------------------------------------------------- 1 | This file is now included in the main libev documentation, see 2 | 3 | http://cvs.schmorp.de/libev/ev.html 4 | -------------------------------------------------------------------------------- /test/t21.ml: -------------------------------------------------------------------------------- 1 | let main () = 2 | let c = Fiber.Condition.create () in 3 | Fiber.Condition.signal c 4 | 5 | let _ = Fiber.run main () 6 | -------------------------------------------------------------------------------- /test/t6.expected: -------------------------------------------------------------------------------- 1 | loop: 1 2 | loop: 2 3 | loop: 3 4 | loop: 4 5 | loop: 5 6 | loop: 6 7 | loop: 7 8 | loop: 8 9 | loop: 9 10 | loop: 10 11 | -------------------------------------------------------------------------------- /test/t7.expected: -------------------------------------------------------------------------------- 1 | loop: 1 2 | loop: 2 3 | loop: 3 4 | loop: 4 5 | loop: 5 6 | loop: 6 7 | loop: 7 8 | loop: 8 9 | loop: 9 10 | loop: 10 11 | -------------------------------------------------------------------------------- /.dir-locals.el: -------------------------------------------------------------------------------- 1 | ;;; Directory Local Variables 2 | ;;; For more information see (info "(emacs) Directory Variables") 3 | 4 | ((nil 5 | (indent-tabs-mode . t))) 6 | -------------------------------------------------------------------------------- /test/t10.ml: -------------------------------------------------------------------------------- 1 | let mutex = Fiber.Mutex.create () 2 | 3 | let _ = 4 | assert(Fiber.Mutex.try_lock mutex == true); 5 | assert(Fiber.Mutex.try_lock mutex == false) 6 | -------------------------------------------------------------------------------- /test/dune: -------------------------------------------------------------------------------- 1 | (tests 2 | (names t1 t2 t3 t4 t5 t6 t7 t8 t9 t10 3 | t11 t12 t13 t14 t15 t16 t17 t18 t19 t20 4 | t21 t22 t23 t24 t25 t26 t27 t28 t29 t30 5 | t31 t32 t33) 6 | (libraries fiber)) 7 | -------------------------------------------------------------------------------- /libev/update_ev_c: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | ( 4 | sed -ne '1,\%/\* ECB.H BEGIN \*/%p' ev.c 5 | cat ~/src/libecb/ecb.h 6 | sed -ne '\%/\* ECB.H END \*/%,$p' ev.c 7 | ) >ev.c~ && mv ev.c~ ev.c 8 | 9 | -------------------------------------------------------------------------------- /bench/bench.ml: -------------------------------------------------------------------------------- 1 | 2 | let _ = 3 | let fiber = Fiber.create (fun _ -> while true do Fiber.yield () done) () in 4 | Benchmark.throughput1 ~repeat:10 ~name:"resume" 3 Fiber.resume fiber 5 | |> Benchmark.tabulate 6 | -------------------------------------------------------------------------------- /test/t20.ml: -------------------------------------------------------------------------------- 1 | let main () = 2 | let v = Fiber.MVar.create_empty () in 3 | assert(Fiber.MVar.take_available v = None); 4 | Fiber.MVar.put v "ok"; 5 | assert(Fiber.MVar.take_available v = Some "ok") 6 | 7 | let _ = Fiber.run main () 8 | -------------------------------------------------------------------------------- /libev/update_symbols: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | make ev.o event.o || exit 4 | 5 | nm ev.o | perl -ne 'print "$1\n" if /\S+ [A-Z] (\S+)/' > Symbols.ev 6 | nm event.o | perl -ne 'print "$1\n" if /\S+ [A-Z] (\S+)/' > Symbols.event 7 | 8 | -------------------------------------------------------------------------------- /test/t18.ml: -------------------------------------------------------------------------------- 1 | let main () = 2 | let v = Fiber.MVar.create "ok" in 3 | assert(Fiber.MVar.is_empty v = false); 4 | Fiber.MVar.take v |> print_string; 5 | print_newline (); 6 | assert(Fiber.MVar.is_empty v) 7 | 8 | let _ = Fiber.run main () 9 | -------------------------------------------------------------------------------- /test/t14.ml: -------------------------------------------------------------------------------- 1 | let _ = 2 | let mutex = Fiber.Mutex.create () in 3 | begin 4 | try 5 | Fiber.Mutex.with_lock mutex (fun _ -> raise Exit) 6 | with 7 | Exit -> (); 8 | end; 9 | assert (Fiber.Mutex.is_locked mutex = false) 10 | -------------------------------------------------------------------------------- /test/t31.ml: -------------------------------------------------------------------------------- 1 | let fb = Fiber.create (fun () -> 2 | for i = 1 to 10 do 3 | print_int i; 4 | print_newline (); 5 | Fiber.yield () 6 | done) () 7 | 8 | let _ = 9 | for j = 1 to 10 do 10 | Fiber.resume fb 11 | done 12 | -------------------------------------------------------------------------------- /test/t17.ml: -------------------------------------------------------------------------------- 1 | let main () = 2 | let v = Fiber.MVar.create_empty () in 3 | Fiber.create (fun () -> 4 | assert(Fiber.MVar.is_empty v); 5 | Fiber.MVar.take v |> print_string) () |> Fiber.resume; 6 | Fiber.MVar.put v "ok\n" 7 | 8 | let _ = Fiber.run main () 9 | -------------------------------------------------------------------------------- /test/t2.expected: -------------------------------------------------------------------------------- 1 | hello, world 2 | Invalid_argument("Fiber.yield") 3 | Invalid_argument("Fiber.unsafe_yield") 4 | Invalid_argument("Fiber.resume") 5 | Invalid_argument("Fiber.unsafe_resume") 6 | Invalid_argument("Fiber.wake") 7 | Invalid_argument("Fiber.cancel_wake") 8 | -------------------------------------------------------------------------------- /test/t12.ml: -------------------------------------------------------------------------------- 1 | let mutex = Fiber.Mutex.create () 2 | let _ = 3 | assert(Fiber.Mutex.is_locked mutex = false); 4 | Fiber.Mutex.lock mutex; 5 | assert(Fiber.Mutex.is_locked mutex = true); 6 | Fiber.Mutex.unlock mutex; 7 | assert(Fiber.Mutex.is_locked mutex = false); 8 | -------------------------------------------------------------------------------- /examples/dune: -------------------------------------------------------------------------------- 1 | (executable 2 | (name socket_io) 3 | (modules socket_io) 4 | (libraries fiber)) 5 | 6 | (executable 7 | (name producer_consumer) 8 | (modules producer_consumer) 9 | (libraries fiber)) 10 | 11 | (alias 12 | (name default) 13 | (deps socket_io.exe producer_consumer.exe)) 14 | -------------------------------------------------------------------------------- /test/t5.ml: -------------------------------------------------------------------------------- 1 | 2 | let f = Fiber.create (fun () -> Fiber.sleep 0.01; "ok\n") () 3 | 4 | let main () = 5 | Fiber.resume f; 6 | print_string (Fiber.join f); 7 | "ret\n" 8 | 9 | let _ = 10 | match Fiber.run main () 11 | with Some str -> print_string str 12 | | None -> assert false 13 | -------------------------------------------------------------------------------- /test/t24.ml: -------------------------------------------------------------------------------- 1 | module S = Set.Make(String) 2 | 3 | let n = 100 4 | let main () = 5 | let zombies = Array.init 4096 (fun _ -> Fiber.create Fiber.yield ()) in 6 | Array.iter Fiber.resume zombies; 7 | Array.iter Fiber.resume zombies; 8 | Gc.compact () 9 | 10 | 11 | let _ = Fiber.run main () 12 | -------------------------------------------------------------------------------- /libcoro/README: -------------------------------------------------------------------------------- 1 | Configuration, documentation etc. is provided in the coro.h file. Please 2 | note that the file conftest.c in this distribution is under the GPL. It is 3 | not needed for proper operation of this library though, for that, coro.h 4 | and coro.c suffice. 5 | 6 | Marc Lehmann 7 | -------------------------------------------------------------------------------- /test/t6.ml: -------------------------------------------------------------------------------- 1 | let main () = 2 | let rec loop () = 3 | print_string "loop: "; 4 | Fiber.yield (); 5 | loop () in 6 | let l = Fiber.create loop () in 7 | for i = 1 to 10 do 8 | Fiber.resume l; 9 | print_int i; 10 | print_newline (); 11 | done 12 | 13 | let _ = Fiber.run main () 14 | -------------------------------------------------------------------------------- /test/t22.ml: -------------------------------------------------------------------------------- 1 | let main () = 2 | Fiber.create (fun () -> Fiber.sleep 1.0; 3 | print_string "fiber"; 4 | print_newline ()) () |> Fiber.resume; 5 | print_string "loop"; 6 | print_newline () 7 | 8 | let _ = 9 | Unix.alarm 1 |> ignore; 10 | Fiber.run main () |> ignore; 11 | print_string "exit"; 12 | print_newline () 13 | -------------------------------------------------------------------------------- /test/t27.ml: -------------------------------------------------------------------------------- 1 | let main () = 2 | let a = Fiber.create (fun () -> print_string "ok"; 3 | print_newline ()) () in 4 | print_string "before_wake"; 5 | print_newline (); 6 | Fiber.wake a; 7 | Fiber.cancel_wake a; 8 | print_string "after_wake"; 9 | print_newline (); 10 | Fiber.resume a 11 | 12 | let _ = Fiber.run main () 13 | -------------------------------------------------------------------------------- /test/t33.ml: -------------------------------------------------------------------------------- 1 | 2 | let main () = 3 | let f = Fiber.create (fun () -> 4 | Fiber.sleep 0.01; (* long io wait *) 5 | "ok") () in 6 | Fiber.resume f; 7 | print_string (Fiber.join f); (* join first time *) 8 | print_newline (); 9 | print_string (Fiber.join f); (* join second time *) 10 | print_newline () 11 | 12 | let _ = Fiber.run main () 13 | 14 | -------------------------------------------------------------------------------- /test/t19.ml: -------------------------------------------------------------------------------- 1 | let main () = 2 | let v = Fiber.MVar.create "1st" in 3 | assert(Fiber.MVar.is_empty v = false); 4 | Fiber.create (fun () -> 5 | Fiber.MVar.put v "2nd") () |> Fiber.resume; 6 | for _ = 1 to 2 do 7 | Fiber.MVar.take v |> print_string; 8 | print_newline (); 9 | done; 10 | assert(Fiber.MVar.is_empty v) 11 | 12 | let _ = Fiber.run main () 13 | -------------------------------------------------------------------------------- /test/t11.ml: -------------------------------------------------------------------------------- 1 | let _ = 2 | let handler _ = print_string "sigusr1"; 3 | print_newline () in 4 | Sys.(set_signal sigusr1 (Signal_handle handler)) 5 | 6 | let main () = 7 | Fiber.create (fun () -> 8 | Unix.kill (Unix.getpid ()) Sys.sigusr1; 9 | print_string "ok"; 10 | print_newline ()) () |> Fiber.resume 11 | 12 | let _ = Fiber.run main () 13 | -------------------------------------------------------------------------------- /test/t25.ml: -------------------------------------------------------------------------------- 1 | let main () = 2 | let a = Fiber.create (fun () -> 3 | let v = Fiber.unsafe_yield () in 4 | print_string v; 5 | print_newline ()) () in 6 | Fiber.resume a; (* resume [a] so it can suspend on yield *) 7 | Fiber.create (fun () -> 8 | Fiber.unsafe_resume a "hello, world") () 9 | |> Fiber.resume 10 | 11 | let _ = Fiber.run main () 12 | -------------------------------------------------------------------------------- /test/t3.ml: -------------------------------------------------------------------------------- 1 | let i = ref 10 2 | let a = Fiber.create (fun () -> while !i > 0 do 3 | Printf.printf "a %i\n" !i; 4 | decr i; 5 | Fiber.yield (); 6 | done) () 7 | let b = Fiber.create (fun () -> while !i > 0 do 8 | Printf.printf "b %i\n" !i; 9 | decr i; 10 | Fiber.resume a 11 | done) () 12 | let _ = 13 | Fiber.resume a; 14 | Fiber.resume b 15 | -------------------------------------------------------------------------------- /test/t29.ml: -------------------------------------------------------------------------------- 1 | [@@@warning "-52"] 2 | 3 | let print_exc e = print_string (Printexc.to_string e); 4 | print_newline () 5 | 6 | let f = Fiber.create (fun () -> 7 | Fiber.sleep 0.01; 8 | "ok\n") () 9 | let _ = 10 | try print_string (Fiber.join f) 11 | with Invalid_argument("Fiber.yield") as e -> print_exc e (* FIXME: should it be "Fiber.join" ? *) 12 | -------------------------------------------------------------------------------- /test/t15.ml: -------------------------------------------------------------------------------- 1 | let main () = 2 | let mutex = Fiber.Mutex.create () in 3 | let f = Fiber.create (fun () -> 4 | Fiber.Mutex.with_lock mutex (fun () -> 5 | Fiber.sleep 0.01)) () 6 | in 7 | Fiber.resume f; 8 | assert (Fiber.Mutex.is_locked mutex = true); 9 | Fiber.join f; 10 | assert (Fiber.Mutex.is_locked mutex = false) 11 | 12 | let _ = Fiber.run main () 13 | -------------------------------------------------------------------------------- /test/t26.expected: -------------------------------------------------------------------------------- 1 | 1 2 | 2 3 | 3 4 | 4 5 | 5 6 | 6 7 | 7 8 | 8 9 | 9 10 | 10 11 | 11 12 | 12 13 | 13 14 | 14 15 | 15 16 | 16 17 | 17 18 | 18 19 | 19 20 | 20 21 | 21 22 | 22 23 | 23 24 | 24 25 | 25 26 | 26 27 | 27 28 | 28 29 | 29 30 | 30 31 | 31 32 | 32 33 | 33 34 | 34 35 | 35 36 | 36 37 | 37 38 | 38 39 | 39 40 | 40 41 | 41 42 | 42 43 | 43 44 | 44 45 | 45 46 | 46 47 | 47 48 | 48 49 | 49 50 | 50 51 | -------------------------------------------------------------------------------- /test/t26.ml: -------------------------------------------------------------------------------- 1 | let main () = 2 | let q = Queue.create () in 3 | for i = 1 to 50 do 4 | let f = Fiber.create (fun () -> 5 | Fiber.sleep 0.02; 6 | print_int i; 7 | print_newline ()) () in 8 | Fiber.resume f; 9 | Queue.push f q; 10 | Fiber.sleep 0.01; 11 | done; 12 | while not (Queue.is_empty q) do 13 | Fiber.join (Queue.pop q) 14 | done 15 | 16 | let _ = Fiber.run main () 17 | -------------------------------------------------------------------------------- /test/t30.ml: -------------------------------------------------------------------------------- 1 | let main () = 2 | Fiber.create (fun () -> Fiber.sleep 0.01; 3 | print_string "fiber"; 4 | print_newline (); 5 | Fiber.break ()) () |> Fiber.resume; 6 | Fiber.sleep 1.0; 7 | print_string "not printed"; 8 | print_newline () 9 | 10 | let _ = 11 | match Fiber.run main () 12 | with None -> print_string "exit"; 13 | print_newline () 14 | | Some _ -> assert false 15 | -------------------------------------------------------------------------------- /test/t7.ml: -------------------------------------------------------------------------------- 1 | let main () = 2 | let c = Fiber.Condition.create () in 3 | let rec loop () = 4 | print_string "loop: "; 5 | Fiber.Condition.signal c; 6 | Fiber.yield (); 7 | loop () in 8 | let l = Fiber.create loop () in 9 | for i = 1 to 10 do 10 | Fiber.wake l; 11 | Fiber.Condition.wait c; 12 | print_int i; 13 | print_newline (); 14 | done 15 | 16 | let _ = Fiber.run main () 17 | -------------------------------------------------------------------------------- /test/t8.ml: -------------------------------------------------------------------------------- 1 | let main () = 2 | let c = Fiber.Condition.create () in 3 | let wait msg = 4 | Fiber.Condition.wait c; 5 | print_string msg; 6 | print_newline () 7 | in 8 | let fs = List.map (Fiber.create wait) ["1st"; "2nd"; "3rd"] in 9 | List.iter Fiber.resume fs; 10 | Fiber.Condition.broadcast c; 11 | List.iter Fiber.join fs; 12 | print_string "done\n" 13 | 14 | let _ = Fiber.run main () 15 | -------------------------------------------------------------------------------- /test/t4.ml: -------------------------------------------------------------------------------- 1 | [@@@warning "-52"] 2 | 3 | let print_exc e = print_string (Printexc.to_string e); 4 | print_newline () 5 | 6 | let _ = 7 | let r = ref (Fiber.create (fun () -> ()) ()) (* dummy placeholder *) in 8 | let f = Fiber.create (fun () -> 9 | try Fiber.resume !r (* fiber recursion is not allowed *) 10 | with Invalid_argument("Fiber.resume") as e -> print_exc e) () in 11 | r := f; 12 | Fiber.resume f 13 | -------------------------------------------------------------------------------- /test/t13.ml: -------------------------------------------------------------------------------- 1 | let main () = 2 | let mutex = Fiber.Mutex.create () in 3 | let lock_sleep_and_print msg = 4 | Fiber.Mutex.lock mutex; 5 | String.iter (fun c -> 6 | Fiber.sleep 0.01; 7 | print_char c) msg; 8 | Fiber.Mutex.unlock mutex 9 | in 10 | let fs = List.map (Fiber.create lock_sleep_and_print) 11 | ["hello, "; "world\n"] in 12 | List.iter Fiber.resume fs; 13 | List.iter Fiber.join fs 14 | 15 | 16 | let _ = Fiber.run main () 17 | -------------------------------------------------------------------------------- /libev/Symbols.event: -------------------------------------------------------------------------------- 1 | event_active 2 | event_add 3 | event_base_dispatch 4 | event_base_free 5 | event_base_get_method 6 | event_base_loop 7 | event_base_loopexit 8 | event_base_new 9 | event_base_once 10 | event_base_priority_init 11 | event_base_set 12 | event_del 13 | event_dispatch 14 | event_get_callback 15 | event_get_method 16 | event_get_version 17 | event_init 18 | event_loop 19 | event_loopexit 20 | event_once 21 | event_pending 22 | event_priority_init 23 | event_priority_set 24 | event_set 25 | -------------------------------------------------------------------------------- /test/t32.ml: -------------------------------------------------------------------------------- 1 | 2 | let rec consumer () = 3 | match Fiber.unsafe_yield () with 4 | Some i -> print_int i; 5 | print_newline (); 6 | consumer () 7 | | None -> () 8 | 9 | let producer (fb, n) = 10 | for i = 1 to n do 11 | print_int i; 12 | print_string " -> "; 13 | Fiber.unsafe_resume fb (Some i) 14 | done 15 | 16 | let _ = 17 | let fb = Fiber.create consumer () in 18 | Fiber.resume fb; (* after resume [fb] will be suspended on unsafe_yield *) 19 | Fiber.create producer (fb, 5) |> Fiber.resume 20 | -------------------------------------------------------------------------------- /libev/configure.ac: -------------------------------------------------------------------------------- 1 | AC_INIT 2 | 3 | orig_CFLAGS="$CFLAGS" 4 | 5 | AC_CONFIG_SRCDIR([ev_epoll.c]) 6 | 7 | dnl also update ev.h! 8 | AM_INIT_AUTOMAKE(libev,4.24) 9 | AC_CONFIG_HEADERS([config.h]) 10 | AM_MAINTAINER_MODE 11 | 12 | AC_PROG_CC 13 | 14 | dnl Supply default CFLAGS, if not specified 15 | if test -z "$orig_CFLAGS"; then 16 | if test x$GCC = xyes; then 17 | CFLAGS="-g -O3" 18 | fi 19 | fi 20 | 21 | AC_PROG_INSTALL 22 | AC_PROG_LIBTOOL 23 | 24 | m4_include([libev.m4]) 25 | 26 | AC_CONFIG_FILES([Makefile]) 27 | AC_OUTPUT 28 | -------------------------------------------------------------------------------- /examples/producer_consumer.ml: -------------------------------------------------------------------------------- 1 | 2 | let rec consumer () = 3 | match Fiber.unsafe_yield () with 4 | Some i -> print_int i; 5 | print_newline (); 6 | consumer () 7 | | None -> () 8 | 9 | let producer (fb, n) = 10 | for i = 1 to n do 11 | print_int i; 12 | print_string " -> "; 13 | Fiber.unsafe_resume fb (Some i) 14 | done 15 | 16 | let _ = 17 | let fb = Fiber.create consumer () in 18 | Fiber.resume fb; (* after resume [fb] will be suspended on unsafe_yield *) 19 | Fiber.create producer (fb, 5) |> Fiber.resume 20 | -------------------------------------------------------------------------------- /test/t16.ml: -------------------------------------------------------------------------------- 1 | let main () = 2 | let mutex = Fiber.Mutex.create () in 3 | let c = Fiber.Condition.create () in 4 | Fiber.Condition.signal c; (* it's ok to signal a condition that nobody waits *) 5 | Fiber.create (fun () -> 6 | Fiber.Mutex.lock mutex; 7 | Fiber.Condition.wait ~mutex c; 8 | assert (Fiber.Mutex.is_locked mutex); 9 | print_string "ok"; 10 | print_newline ()) () |> Fiber.resume; 11 | assert (Fiber.Mutex.is_locked mutex = false); (* mutex is unlocked by Condition.wait *) 12 | Fiber.Condition.signal c 13 | 14 | let _ = Fiber.run main () 15 | -------------------------------------------------------------------------------- /libev/update_ev_wrap: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ( 4 | echo '#define VAR(name,decl) name' 5 | echo '#define EV_GENWRAP 1' 6 | cat ev_vars.h 7 | ) | cc -E -o - - | perl -ne ' 8 | while (<>) { 9 | push @syms, $1 if /(^\w+)/; 10 | } 11 | print "/* DO NOT EDIT, automatically generated by update_ev_wrap */\n", 12 | "#ifndef EV_WRAP_H\n", 13 | "#define EV_WRAP_H\n", 14 | (map "#define $_ ((loop)->$_)\n", sort @syms), 15 | "#else\n", 16 | "#undef EV_WRAP_H\n", 17 | (map "#undef $_\n", sort @syms), 18 | "#endif\n"; 19 | ' >ev_wrap.h 20 | -------------------------------------------------------------------------------- /libev/Makefile.am: -------------------------------------------------------------------------------- 1 | AUTOMAKE_OPTIONS = foreign 2 | 3 | VERSION_INFO = 4:0:0 4 | 5 | EXTRA_DIST = LICENSE Changes libev.m4 autogen.sh \ 6 | ev_vars.h ev_wrap.h \ 7 | ev_epoll.c ev_select.c ev_poll.c ev_kqueue.c ev_port.c ev_win32.c \ 8 | ev.3 ev.pod Symbols.ev Symbols.event 9 | 10 | man_MANS = ev.3 11 | 12 | include_HEADERS = ev.h ev++.h event.h 13 | 14 | lib_LTLIBRARIES = libev.la 15 | 16 | libev_la_SOURCES = ev.c event.c 17 | libev_la_LDFLAGS = -version-info $(VERSION_INFO) 18 | 19 | ev.3: ev.pod 20 | pod2man -n LIBEV -r "libev-$(VERSION)" -c "libev - high performance full featured event loop" -s3 <$< >$@ 21 | -------------------------------------------------------------------------------- /fiber.opam: -------------------------------------------------------------------------------- 1 | opam-version: "2.0" 2 | name: "camlfiber" 3 | synopsis: "Fast native fibers for OCaml" 4 | description: """ 5 | Fibers are implemented by tying together libcoro and OCaml's GC 6 | hooks. This design allows allocation free context switches with decent 7 | performance. 8 | """ 9 | version: "0.1.0" 10 | maintainer: "Yuriy Vostrikov " 11 | authors: "Yuriy Vostrikov " 12 | homepage: "https://github.com/delamonpansie/camlfiber" 13 | bug-reports: "https://github.com/delamonpansie/camlfiber" 14 | license: "BSD" 15 | dev-repo: "git+https://github.com/delamonpansie/camlfiber.git" 16 | build: [ [ make "build" ] ] 17 | install: [ make "install" ] 18 | remove: [ "ocamlfind" "remove" "fiber" ] 19 | depends: [ 20 | "dune" {build & > "1.5"} 21 | ] 22 | -------------------------------------------------------------------------------- /test/t9.ml: -------------------------------------------------------------------------------- 1 | let main () = 2 | let mutex = Fiber.Mutex.create () in 3 | let c = Fiber.Condition.create () in 4 | let shared = ref 3 in 5 | let wait msg = 6 | Fiber.Mutex.lock mutex; 7 | while !shared > 0 do 8 | Fiber.Condition.wait ~mutex c; 9 | assert(Fiber.Mutex.is_locked mutex); 10 | print_string (msg ^ " "); 11 | print_int !shared; 12 | print_newline (); 13 | done; 14 | Fiber.Mutex.unlock mutex; 15 | in 16 | let fs = List.map (Fiber.create wait) ["1st"; "2nd"; "3rd"] in 17 | List.iter Fiber.resume fs; 18 | 19 | for i = 1 to 3 do 20 | Fiber.Mutex.with_lock mutex (fun () -> 21 | decr shared; 22 | Fiber.Condition.broadcast c); 23 | Fiber.sleep 0.01 24 | done; 25 | 26 | List.iter Fiber.join fs 27 | 28 | let _ = Fiber.run main () 29 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: build 2 | install: build 3 | build install: 4 | dune $@ 5 | clean: 6 | dune $@ 7 | rm -rf _coverage 8 | test: build 9 | dune runtest 10 | build: libev/config.h 11 | libev/config.h: 12 | cd libev && autoreconf --install --symlink 13 | cd libev && ./configure 14 | coverage: clean 15 | mkdir -p _coverage/c _coverage/ml 16 | BISECT_ENABLE=YES C_FLAGS="--coverage" C_LIBRARY_FLAGS="-lgcov" dune runtest 17 | bisect-ppx-report -I _build/default -html _coverage/ml \ 18 | `find _build/default/test -name "bisect*.out"` 19 | lcov --output-file _coverage/c/fiber.cov --capture --directory _build/default 20 | genhtml --output-directory _coverage/c _coverage/c/fiber.cov 21 | bench: build 22 | dune build bench/bench.exe 23 | _build/default/bench/bench.exe 24 | doc: 25 | dune build @doc 26 | .PHONY: all clean install coverage test bench doc 27 | -------------------------------------------------------------------------------- /test/t28.ml: -------------------------------------------------------------------------------- 1 | let main () = 2 | let open Unix in 3 | let a, b = socketpair PF_UNIX SOCK_SEQPACKET 0 in 4 | set_nonblock a; 5 | set_nonblock b; 6 | let n = 64 in 7 | let reader = Fiber.create (fun () -> 8 | let buf = Bytes.create 256 in 9 | for i = 1 to n do 10 | Fiber.wait_io_ready a Fiber.READ; 11 | let n = read a buf 0 256 in 12 | print_string "read: "; 13 | print_string (Bytes.sub_string buf 0 n); 14 | print_newline () 15 | done) () 16 | in 17 | let writer = Fiber.create (fun () -> 18 | let buf = Bytes.make 256 'a' in 19 | for i = 1 to n do 20 | Fiber.wait_io_ready b Fiber.WRITE; 21 | let n' = single_write b buf 0 i in 22 | print_string "write: "; 23 | print_string (Bytes.sub_string buf 0 n'); 24 | print_newline () 25 | done) () in 26 | Fiber.resume reader; 27 | Fiber.resume writer; 28 | Fiber.join reader 29 | 30 | let _ = Fiber.run main () 31 | -------------------------------------------------------------------------------- /examples/socket_io.ml: -------------------------------------------------------------------------------- 1 | let main () = 2 | let open Unix in 3 | let a, b = socketpair PF_UNIX SOCK_SEQPACKET 0 in 4 | set_nonblock a; 5 | set_nonblock b; 6 | let n = 64 in 7 | let reader = Fiber.create (fun () -> 8 | let buf = Bytes.create 256 in 9 | for i = 1 to n do 10 | Fiber.wait_io_ready a Fiber.READ; 11 | let n = read a buf 0 256 in 12 | print_string "read: "; 13 | print_string (Bytes.sub_string buf 0 n); 14 | print_newline () 15 | done) () 16 | in 17 | let writer = Fiber.create (fun () -> 18 | let buf = Bytes.make 256 'a' in 19 | for i = 1 to n do 20 | Fiber.wait_io_ready b Fiber.WRITE; 21 | let n' = single_write b buf 0 i in 22 | print_string "write: "; 23 | print_string (Bytes.sub_string buf 0 n'); 24 | print_newline () 25 | done) () in 26 | Fiber.resume reader; 27 | Fiber.resume writer; 28 | Fiber.join reader 29 | 30 | let _ = Fiber.run main () 31 | -------------------------------------------------------------------------------- /test/t2.ml: -------------------------------------------------------------------------------- 1 | [@@@warning "-52"] 2 | 3 | let _= 4 | let f = Fiber.create print_string "hello, world\n" in 5 | Fiber.resume f; 6 | let print_exc e = print_string (Printexc.to_string e); 7 | print_newline () in 8 | (* yield must be called only from inside of fiber *) 9 | (try Fiber.yield (); 10 | with Invalid_argument("Fiber.yield") as e -> print_exc e); 11 | (try Fiber.unsafe_yield () |> ignore; 12 | with Invalid_argument("Fiber.unsafe_yield") as e -> print_exc e); 13 | (* f is dead here, wake and resumy should raise an exception *) 14 | (try Fiber.resume f 15 | with Invalid_argument("Fiber.resume") as e -> print_exc e); 16 | (try Fiber.unsafe_resume f () 17 | with Invalid_argument("Fiber.unsafe_resume") as e -> print_exc e); 18 | (try Fiber.wake f; 19 | with Invalid_argument("Fiber.wake") as e -> print_exc e); 20 | (try Fiber.cancel_wake f; 21 | with Invalid_argument("Fiber.cancel_wake") as e -> print_exc e); 22 | -------------------------------------------------------------------------------- /dune: -------------------------------------------------------------------------------- 1 | (library 2 | (public_name fiber) 3 | (libraries unix) 4 | (c_names fiber_stubs fiber_ev fiber_coro) 5 | (c_flags -D_GNU_SOURCE -O2 -g3 -Wall (:include _c_flags)) 6 | (c_library_flags (:include _c_library_flags)) 7 | (wrapped false) 8 | (preprocess (pps bisect_ppx -conditional))) 9 | 10 | (rule 11 | (targets _c_flags) 12 | (action (with-stdout-to %{targets} (bash "echo '('${C_FLAGS:-}')'")))) 13 | (rule 14 | (targets _c_library_flags) 15 | (action (with-stdout-to %{targets} (bash "echo '('${C_LIBRARY_FLAGS:-}')'")))) 16 | 17 | (rule 18 | (targets fiber_coro.c) 19 | (deps libcoro/coro.c 20 | libcoro/coro.h) 21 | (action (copy fiber_coro.cx fiber_coro.c))) 22 | 23 | (rule 24 | (targets fiber_ev.c) 25 | (deps fiber_ev.h 26 | libev/ev.c 27 | libev/ev.h 28 | libev/ev_vars.h 29 | libev/config.h 30 | libev/ev_epoll.c 31 | libev/ev_poll.c 32 | libev/ev_select.c) 33 | (action (copy fiber_ev.cx fiber_ev.c))) 34 | 35 | ;; Local Variables: 36 | ;; mode: lisp 37 | ;; End: 38 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # camlfiber 2 | 3 | Fast native fibers for OCaml. 4 | 5 | Fibers are implemented by tying together [libcoro](http://software.schmorp.de/pkg/libcoro.html) and OCaml's GC 6 | hooks. This design allows **allocation free** context switches with 7 | decent performance. For example, i7-8750H running Linux can perform 8 | about 13.7M fiber context switches per second. If you want to measure 9 | it by yourself just run `make bench` from the source directory. 10 | 11 | ## Documentation 12 | 13 | [camlfiber API](https://delamonpansie.github.io/camlfiber/fiber/Fiber/index.html) 14 | 15 | Also, there are few examples in [examples](https://github.com/delamonpansie/camlfiber/tree/master/examples) directory. 16 | 17 | 18 | ## Installing 19 | 20 | ### Prerequisites 21 | * `dune` 22 | * `odoc` if you are planning to build documentation. 23 | * `bisect-ppx` and `lcov` are required for coverage testing. 24 | 25 | ### Available targets 26 | * `make` build library 27 | * `make install` install library 28 | * `make bench` run benchmark 29 | * `make doc` build documentation 30 | * `make test` run tests 31 | * `make coverage` run tests with coverage reporting 32 | 33 | ## License 34 | 35 | This project is licensed under the BSD License 36 | -------------------------------------------------------------------------------- /test/t23.ml: -------------------------------------------------------------------------------- 1 | module S = Set.Make(String) 2 | 3 | let n = 50 4 | let main () = 5 | let mvar = Fiber.MVar.create S.empty in 6 | 7 | let rand_string n = 8 | let alphanum = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" in 9 | let len = String.length alphanum in 10 | Bytes.init n (fun _ -> alphanum.[Random.int len]) |> Bytes.to_string 11 | in 12 | let worker () = 13 | for i = 1 to n do 14 | let s = Fiber.MVar.take mvar in 15 | let el = rand_string (Random.int 1024) in 16 | Fiber.MVar.put mvar (S.add el s); 17 | Fiber.yield () 18 | done 19 | in 20 | let zombies = Array.init 128 (fun _ -> Fiber.create Fiber.yield ()) in 21 | let w = Array.init 4096 (fun _ -> Fiber.create worker ()) in 22 | Array.iter Fiber.resume zombies; 23 | 24 | Array.iter Fiber.resume zombies; 25 | Array.iter Fiber.resume w; 26 | 27 | for i = 1 to n do 28 | Array.iter Fiber.resume w; 29 | if i mod 20 == 0 then 30 | Gc.compact () 31 | done; 32 | Fiber.MVar.take mvar 33 | 34 | 35 | let _ = 36 | (match Fiber.run main () 37 | with Some s -> print_int (S.cardinal s); 38 | print_newline (); 39 | | None -> assert false); 40 | let stat = Gc.stat () in 41 | assert(stat.minor_collections > 0); 42 | assert(stat.major_collections > 0); 43 | assert(stat.compactions > 0) 44 | -------------------------------------------------------------------------------- /libev/Symbols.ev: -------------------------------------------------------------------------------- 1 | ev_async_send 2 | ev_async_start 3 | ev_async_stop 4 | ev_backend 5 | ev_break 6 | ev_check_start 7 | ev_check_stop 8 | ev_child_start 9 | ev_child_stop 10 | ev_cleanup_start 11 | ev_cleanup_stop 12 | ev_clear_pending 13 | ev_default_loop 14 | ev_default_loop_ptr 15 | ev_depth 16 | ev_embed_start 17 | ev_embed_stop 18 | ev_embed_sweep 19 | ev_embeddable_backends 20 | ev_feed_event 21 | ev_feed_fd_event 22 | ev_feed_signal 23 | ev_feed_signal_event 24 | ev_fork_start 25 | ev_fork_stop 26 | ev_idle_start 27 | ev_idle_stop 28 | ev_invoke 29 | ev_invoke_pending 30 | ev_io_start 31 | ev_io_stop 32 | ev_iteration 33 | ev_loop_destroy 34 | ev_loop_fork 35 | ev_loop_new 36 | ev_now 37 | ev_now_update 38 | ev_once 39 | ev_pending_count 40 | ev_periodic_again 41 | ev_periodic_start 42 | ev_periodic_stop 43 | ev_prepare_start 44 | ev_prepare_stop 45 | ev_recommended_backends 46 | ev_ref 47 | ev_resume 48 | ev_run 49 | ev_set_allocator 50 | ev_set_invoke_pending_cb 51 | ev_set_io_collect_interval 52 | ev_set_loop_release_cb 53 | ev_set_syserr_cb 54 | ev_set_timeout_collect_interval 55 | ev_set_userdata 56 | ev_signal_start 57 | ev_signal_stop 58 | ev_sleep 59 | ev_stat_start 60 | ev_stat_stat 61 | ev_stat_stop 62 | ev_supported_backends 63 | ev_suspend 64 | ev_time 65 | ev_timer_again 66 | ev_timer_remaining 67 | ev_timer_start 68 | ev_timer_stop 69 | ev_unref 70 | ev_userdata 71 | ev_verify 72 | ev_version_major 73 | ev_version_minor 74 | -------------------------------------------------------------------------------- /libcoro/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2000-2009 Marc Alexander Lehmann 2 | 3 | Redistribution and use in source and binary forms, with or without modifica- 4 | tion, are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, 7 | this list of conditions and the following disclaimer. 8 | 9 | 2. Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 14 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- 15 | CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 16 | EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- 17 | CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 18 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 19 | OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 20 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- 21 | ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 22 | OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | Alternatively, the following files carry an additional notice that 25 | explicitly allows relicensing under the GPLv2: coro.c, coro.h. 26 | 27 | -------------------------------------------------------------------------------- /fiber_ev.cx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010-2016 Mail.RU 3 | * Copyright (C) 2010-2018 Yuriy Vostrikov 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | */ 26 | 27 | 28 | #include "fiber.h" 29 | #include "fiber_ev.h" 30 | #pragma GCC diagnostic ignored "-Wcomment" 31 | #pragma GCC diagnostic ignored "-Wunused-value" 32 | #pragma GCC diagnostic ignored "-Wparentheses" 33 | #include "libev/ev.c" 34 | 35 | -------------------------------------------------------------------------------- /libev/libev.m4: -------------------------------------------------------------------------------- 1 | dnl this file is part of libev, do not make local modifications 2 | dnl http://software.schmorp.de/pkg/libev 3 | 4 | dnl libev support 5 | AC_CHECK_HEADERS(sys/inotify.h sys/epoll.h sys/event.h port.h poll.h sys/select.h sys/eventfd.h sys/signalfd.h) 6 | 7 | AC_CHECK_FUNCS(inotify_init epoll_ctl kqueue port_create poll select eventfd signalfd) 8 | 9 | AC_CHECK_FUNCS(clock_gettime, [], [ 10 | dnl on linux, try syscall wrapper first 11 | if test $(uname) = Linux; then 12 | AC_MSG_CHECKING(for clock_gettime syscall) 13 | AC_LINK_IFELSE([AC_LANG_PROGRAM( 14 | [#include 15 | #include 16 | #include ], 17 | [struct timespec ts; int status = syscall (SYS_clock_gettime, CLOCK_REALTIME, &ts)])], 18 | [ac_have_clock_syscall=1 19 | AC_DEFINE(HAVE_CLOCK_SYSCALL, 1, Define to 1 to use the syscall interface for clock_gettime) 20 | AC_MSG_RESULT(yes)], 21 | [AC_MSG_RESULT(no)]) 22 | fi 23 | if test -z "$LIBEV_M4_AVOID_LIBRT" && test -z "$ac_have_clock_syscall"; then 24 | AC_CHECK_LIB(rt, clock_gettime) 25 | unset ac_cv_func_clock_gettime 26 | AC_CHECK_FUNCS(clock_gettime) 27 | fi 28 | ]) 29 | 30 | AC_CHECK_FUNCS(nanosleep, [], [ 31 | if test -z "$LIBEV_M4_AVOID_LIBRT"; then 32 | AC_CHECK_LIB(rt, nanosleep) 33 | unset ac_cv_func_nanosleep 34 | AC_CHECK_FUNCS(nanosleep) 35 | fi 36 | ]) 37 | 38 | if test -z "$LIBEV_M4_AVOID_LIBM"; then 39 | LIBM=m 40 | fi 41 | AC_SEARCH_LIBS(floor, $LIBM, [AC_DEFINE(HAVE_FLOOR, 1, Define to 1 if the floor function is available)]) 42 | 43 | -------------------------------------------------------------------------------- /libev/LICENSE: -------------------------------------------------------------------------------- 1 | All files in libev are 2 | Copyright (c)2007,2008,2009,2010,2011,2012,2013 Marc Alexander Lehmann. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above 12 | copyright notice, this list of conditions and the following 13 | disclaimer in the documentation and/or other materials provided 14 | with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | Alternatively, the contents of this package may be used under the terms 29 | of 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 the 31 | above. If you wish to allow the use of your version of this package only 32 | under the terms of the GPL and not to allow others to use your version of 33 | this file under the BSD license, indicate your decision by deleting the 34 | provisions above and replace them with the notice and other provisions 35 | required by the GPL in this and the other files of this package. If you do 36 | not delete the provisions above, a recipient may use your version of this 37 | file under either the BSD or the GPL. 38 | -------------------------------------------------------------------------------- /fiber_ev.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010-2016 Mail.RU 3 | * Copyright (C) 2010-2018 Yuriy Vostrikov 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | */ 26 | 27 | #define EV_MULTIPLICITY 0 28 | #define EV_CONFIG_H "config.h" 29 | 30 | 31 | #define EV_STRINGIFY2(x) #x 32 | #define EV_STRINGIFY(x) EV_STRINGIFY2(x) 33 | #define EV_COMMON void *data; char coro; const char *cb_src; 34 | #define ev_set_cb(ev,cb_) (ev_cb_ (ev) = (cb_), memmove (&((ev_watcher *)(ev))->cb, &ev_cb_ (ev), sizeof (ev_cb_ (ev))), (ev)->cb_src = __FILE__ ":" EV_STRINGIFY(__LINE__)) 35 | #define EV_CB_DECLARE(type) void (*cb)(struct type *w, int revents); 36 | 37 | #if defined(FIBER_TRACE) || defined(FIBER_EV_DEBUG) 38 | extern void fiber_ev_cb(void *); 39 | #define EV_CB_LOG(arg) fiber_ev_cb((arg)) 40 | #else 41 | #define EV_CB_LOG(arg) (void)0 42 | #endif 43 | 44 | #define EV_CB_INVOKE(watcher, revents) ({ \ 45 | if ((watcher)->coro) { \ 46 | fiber = (struct fiber *)(watcher)->cb; \ 47 | fiber->coro.w = (watcher); \ 48 | fiber->caller = sched; \ 49 | EV_CB_LOG((watcher)); \ 50 | coro_transfer(sched_ctx, &fiber->coro.ctx); \ 51 | } else \ 52 | (watcher)->cb((watcher), (revents)); \ 53 | }) 54 | 55 | #include "libev/ev.h" 56 | -------------------------------------------------------------------------------- /libev/README: -------------------------------------------------------------------------------- 1 | libev is a high-performance event loop/event model with lots of features. 2 | (see benchmark at http://libev.schmorp.de/bench.html) 3 | 4 | 5 | ABOUT 6 | 7 | Homepage: http://software.schmorp.de/pkg/libev 8 | Mailinglist: libev@lists.schmorp.de 9 | http://lists.schmorp.de/cgi-bin/mailman/listinfo/libev 10 | Library Documentation: http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod 11 | 12 | Libev is modelled (very losely) after libevent and the Event perl 13 | module, but is faster, scales better and is more correct, and also more 14 | featureful. And also smaller. Yay. 15 | 16 | Some of the specialties of libev not commonly found elsewhere are: 17 | 18 | - extensive and detailed, readable documentation (not doxygen garbage). 19 | - fully supports fork, can detect fork in various ways and automatically 20 | re-arms kernel mechanisms that do not support fork. 21 | - highly optimised select, poll, epoll, kqueue and event ports backends. 22 | - filesystem object (path) watching (with optional linux inotify support). 23 | - wallclock-based times (using absolute time, cron-like). 24 | - relative timers/timeouts (handle time jumps). 25 | - fast intra-thread communication between multiple 26 | event loops (with optional fast linux eventfd backend). 27 | - extremely easy to embed (fully documented, no dependencies, 28 | autoconf supported but optional). 29 | - very small codebase, no bloated library, simple code. 30 | - fully extensible by being able to plug into the event loop, 31 | integrate other event loops, integrate other event loop users. 32 | - very little memory use (small watchers, small event loop data). 33 | - optional C++ interface allowing method and function callbacks 34 | at no extra memory or runtime overhead. 35 | - optional Perl interface with similar characteristics (capable 36 | of running Glib/Gtk2 on libev). 37 | - support for other languages (multiple C++ interfaces, D, Ruby, 38 | Python) available from third-parties. 39 | 40 | Examples of programs that embed libev: the EV perl module, node.js, 41 | auditd, rxvt-unicode, gvpe (GNU Virtual Private Ethernet), the 42 | Deliantra MMORPG server (http://www.deliantra.net/), Rubinius (a 43 | next-generation Ruby VM), the Ebb web server, the Rev event toolkit. 44 | 45 | 46 | CONTRIBUTORS 47 | 48 | libev was written and designed by Marc Lehmann and Emanuele Giaquinta. 49 | 50 | The following people sent in patches or made other noteworthy 51 | contributions to the design (for minor patches, see the Changes 52 | file. If I forgot to include you, please shout at me, it was an 53 | accident): 54 | 55 | W.C.A. Wijngaards 56 | Christopher Layne 57 | Chris Brody 58 | 59 | -------------------------------------------------------------------------------- /fiber.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010-2016 Mail.RU 3 | * Copyright (C) 2010-2018 Yuriy Vostrikov 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | */ 26 | 27 | #ifndef FIBER_H 28 | #define FIBER_H 29 | 30 | #include "libcoro/coro.h" 31 | #include "queue.h" 32 | #include 33 | #include 34 | #include 35 | #ifdef FIBER_DEBUG 36 | # include 37 | #endif 38 | 39 | struct coro { 40 | struct coro_context ctx; 41 | void *stack, *mmap; 42 | size_t stack_size, mmap_size; 43 | void *w; 44 | }; 45 | 46 | 47 | struct fiber { 48 | struct coro coro; 49 | struct fiber *caller; 50 | long long id; 51 | 52 | SLIST_ENTRY(fiber) link, zombie_link; 53 | TAILQ_ENTRY(fiber) wake_link; 54 | void *wake; 55 | char cancel; 56 | 57 | char * top_of_stack; 58 | char * bottom_of_stack; 59 | uintptr_t last_retaddr; 60 | intptr_t * gc_regs; 61 | char * exception_pointer; 62 | struct caml__roots_block * caml_local_roots; 63 | int caml_backtrace_pos; 64 | void ** caml_backtrace_buffer; 65 | intptr_t caml_backtrace_last_exn; 66 | 67 | char name[20]; 68 | intptr_t cb, arg; 69 | }; 70 | 71 | extern struct fiber *fiber, *sched; 72 | extern struct coro_context *sched_ctx; 73 | 74 | #ifdef FIBER_TRACE 75 | void fiber_resume(struct fiber *callee, void *w); 76 | void *fiber_yield(void); 77 | #define resume(callee, w) ({ \ 78 | fprintf(stderr, "resume: %lli/%s -> %lli/%s arg:%p\n", \ 79 | fiber->id, fiber->name, callee->id, callee->name, w); \ 80 | fiber_resume(callee, w); \ 81 | }) 82 | #define yield() ({ \ 83 | fprintf(stderr, "yield: %lli/%s -> %lli/%s\n", \ 84 | fiber->id, fiber->name, \ 85 | fiber->caller->id, fiber->caller->name); \ 86 | void *yield_ret = fiber_yield(); \ 87 | fprintf(stderr, "yield: return arg:%p\n", yield_ret); \ 88 | yield_ret; \ 89 | }) 90 | #else 91 | void resume(struct fiber *callee, void *w); 92 | void *yield(void); 93 | #endif 94 | 95 | #endif 96 | -------------------------------------------------------------------------------- /libev/import_libevent: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | LE=../libevent-1.4.3-stable 4 | 5 | if ! [ -e evbuffer.c ]; then 6 | echo do not run this programm unless you know what you are doing 7 | exit 1 8 | fi 9 | 10 | # this program combines libev and libevent into a single package 11 | 12 | cvs update -AdP libev 13 | rsync -avP libev/. . --exclude CVS 14 | 15 | rm -f configure.ac 16 | 17 | cp $LE/evdns.h . 18 | 19 | perl -i -pe 's%^/.libevent-include./%#include "event_compat.h"%' event.h 20 | 21 | perl -ne ' 22 | s/\s+char buf\[64\];/\tchar buf[96];/; 23 | if (/#include "event.h"/) { 24 | print "#ifndef EV_STANDALONE\n$_#endif\n"; 25 | next; 26 | } 27 | if (/#include "misc.h"/) { 28 | print "#ifndef EV_STANDALONE\n$_#endif\n"; 29 | next; 30 | } 31 | if (/#include "(unistd.h|sys\/time.h)"/) { 32 | print "#ifndef WIN32\n$_#endif\n"; 33 | next; 34 | } 35 | next if /#include "log.h"/; 36 | 37 | print; 38 | ' <$LE/evdns.c >evdns.c 39 | 40 | cp $LE/autogen.sh . 41 | cp $LE/epoll_sub.c . 42 | cp $LE/evbuffer.c . 43 | cp $LE/buffer.c . 44 | cp $LE/evhttp.h . 45 | cp $LE/evutil.h . 46 | cp $LE/evutil.c . 47 | cp $LE/event-config.h . 48 | cp $LE/event-internal.h . 49 | cp $LE/evrpc.h . 50 | cp $LE/evrpc.c . 51 | cp $LE/evrpc-internal.h . 52 | cp $LE/http.c . 53 | cp $LE/event_tagging.c . 54 | cp $LE/http-internal.h . 55 | cp $LE/strlcpy-internal.h . 56 | cp $LE/log.c . 57 | cp $LE/log.h . 58 | cp $LE/strlcpy.c . 59 | rsync -a $LE/WIN32* $LE/sample $LE/test $LE/compat . --del 60 | #rename 's/libevent/libev/' WIN32-Prj/lib* 61 | cp $LE/aclocal.m4 . 62 | #cp $LE/acconfig.h . 63 | cp $LE/config.h.in . 64 | cp $LE/event_rpcgen.py . 65 | cp $LE/*.3 . 66 | 67 | #perl -i -pe 's/libevent/libev/g' sample/Makefile.am 68 | #perl -i -pe 's/libevent/libev/g' test/Makefile.am 69 | 70 | perl -i -pe 's/#include $/#include "event.h"/' test/*.c 71 | 72 | perl -i -ne ' 73 | next if /"event-internal.h"/; 74 | s/base\d?->sig.ev_signal_added/0/; 75 | s/base\d?->sig.ev_signal_pair\[0\]/-1/; 76 | s/base->sig.evsignal_caught/0/; 77 | next if /^\ttest_signal_(dealloc|pipeloss|switchbase|assert|restore)\(\)/; 78 | next if /^\ttest_simplesignal\(\)/; # non-default-loop 79 | next if /^\ttest_immediatesignal\(\)/; # non-default-loop 80 | next if /test_priorities\(\d\)/; 81 | print; 82 | ' test/regress.c 83 | 84 | perl -ne ' 85 | s/\bmin_heap.h\b//g; 86 | s/\bsignal.c\b//g; 87 | s/\bevport.c\b//g; 88 | s/\bkqueue.c\b//g; 89 | s/\bdevpoll.c\b//g; 90 | s/\brtsig.c\b//g; 91 | s/\bselect.c\b//g; 92 | s/\bpoll.c\b//g; 93 | s/\bepoll.c\b//g; 94 | s/\bepoll_sub.c\b//g; 95 | s/\bevent-internal.h\b//g; 96 | s/\bevsignal.h\b//g; 97 | s/^(man_MANS\s*=)/$1 ev.3 /; 98 | s/^(EXTRA_DIST\s*=)/$1 libev.m4 ev.h ev_vars.h ev_wrap.h event_compat.h ev++.h ev_epoll.c ev_select.c ev_poll.c ev_kqueue.c ev_port.c ev_win32.c ev.3 ev.pod /; 99 | s/^(include_HEADERS\s*=)/$1 ev.h event_compat.h ev++.h /; 100 | s/^(CORE_SRC\s*=)/$1 ev.c /; 101 | s/^(SYS_LIBS\s*=)/$1 -lm /; 102 | #s/libevent/libev/g; 103 | print; 104 | ' <$LE/Makefile.am >Makefile.am 105 | 106 | perl -ne ' 107 | #s/-Wall/-Wall -Wno-comment -Wunused-function -Wno-unused-value/; 108 | s/-Wall//g; 109 | #s/libevent/libev/g; 110 | #VERSION 111 | s/AM_INIT_AUTOMAKE\s*\(.*,(.*)\)/AM_INIT_AUTOMAKE(libevent-$1+libev,3.1)/; 112 | s/AC_LIBOBJ\(select\)/: ;/g; 113 | s/AC_LIBOBJ\(poll\)/: ;/g; 114 | s/AC_LIBOBJ\(kqueue\)/: ;/g; 115 | s/AC_LIBOBJ\(epoll\)/: ;/g; 116 | s/AC_LIBOBJ\(devpoll\)/: ;/g; 117 | s/AC_LIBOBJ\(evport\)/: ;/g; 118 | s/AC_LIBOBJ\(signal\)/: ;/g; 119 | s/AC_LIBOBJ\(rtsig\)/: ;/g; 120 | print "m4_include([libev.m4])\n" if /^AC_OUTPUT/; 121 | print; 122 | ' <$LE/configure.in >configure.in 123 | 124 | aclocal-1.7 125 | automake-1.7 --add-missing 126 | autoconf 127 | autoheader 128 | libtoolize 129 | CC="ccache gcc" ./configure --prefix=/opt/libev --disable-shared "$@" 130 | 131 | 132 | -------------------------------------------------------------------------------- /libev/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 | inline_size 43 | void 44 | pollidx_init (int *base, int count) 45 | { 46 | /* consider using memset (.., -1, ...), which is practically guaranteed 47 | * to work on all systems implementing poll */ 48 | while (count--) 49 | *base++ = -1; 50 | } 51 | 52 | static void 53 | poll_modify (EV_P_ int fd, int oev, int nev) 54 | { 55 | int idx; 56 | 57 | if (oev == nev) 58 | return; 59 | 60 | array_needsize (int, pollidxs, pollidxmax, fd + 1, pollidx_init); 61 | 62 | idx = pollidxs [fd]; 63 | 64 | if (idx < 0) /* need to allocate a new pollfd */ 65 | { 66 | pollidxs [fd] = idx = pollcnt++; 67 | array_needsize (struct pollfd, polls, pollmax, pollcnt, EMPTY2); 68 | polls [idx].fd = fd; 69 | } 70 | 71 | assert (polls [idx].fd == fd); 72 | 73 | if (nev) 74 | polls [idx].events = 75 | (nev & EV_READ ? POLLIN : 0) 76 | | (nev & EV_WRITE ? POLLOUT : 0); 77 | else /* remove pollfd */ 78 | { 79 | pollidxs [fd] = -1; 80 | 81 | if (expect_true (idx < --pollcnt)) 82 | { 83 | polls [idx] = polls [pollcnt]; 84 | pollidxs [polls [idx].fd] = idx; 85 | } 86 | } 87 | } 88 | 89 | static void 90 | poll_poll (EV_P_ ev_tstamp timeout) 91 | { 92 | struct pollfd *p; 93 | int res; 94 | 95 | EV_RELEASE_CB; 96 | res = poll (polls, pollcnt, timeout * 1e3); 97 | EV_ACQUIRE_CB; 98 | 99 | if (expect_false (res < 0)) 100 | { 101 | if (errno == EBADF) 102 | fd_ebadf (EV_A); 103 | else if (errno == ENOMEM && !syserr_cb) 104 | fd_enomem (EV_A); 105 | else if (errno != EINTR) 106 | ev_syserr ("(libev) poll"); 107 | } 108 | else 109 | for (p = polls; res; ++p) 110 | { 111 | assert (("libev: poll() returned illegal result, broken BSD kernel?", p < polls + pollcnt)); 112 | 113 | if (expect_false (p->revents)) /* this expect is debatable */ 114 | { 115 | --res; 116 | 117 | if (expect_false (p->revents & POLLNVAL)) 118 | fd_kill (EV_A_ p->fd); 119 | else 120 | fd_event ( 121 | EV_A_ 122 | p->fd, 123 | (p->revents & (POLLOUT | POLLERR | POLLHUP) ? EV_WRITE : 0) 124 | | (p->revents & (POLLIN | POLLERR | POLLHUP) ? EV_READ : 0) 125 | ); 126 | } 127 | } 128 | } 129 | 130 | inline_size 131 | int 132 | poll_init (EV_P_ int flags) 133 | { 134 | backend_mintime = 1e-3; 135 | backend_modify = poll_modify; 136 | backend_poll = poll_poll; 137 | 138 | pollidxs = 0; pollidxmax = 0; 139 | polls = 0; pollmax = 0; pollcnt = 0; 140 | 141 | return EVBACKEND_POLL; 142 | } 143 | 144 | inline_size 145 | void 146 | poll_destroy (EV_P) 147 | { 148 | ev_free (pollidxs); 149 | ev_free (polls); 150 | } 151 | 152 | -------------------------------------------------------------------------------- /test/t28.expected: -------------------------------------------------------------------------------- 1 | write: a 2 | read: a 3 | write: aa 4 | read: aa 5 | write: aaa 6 | read: aaa 7 | write: aaaa 8 | read: aaaa 9 | write: aaaaa 10 | read: aaaaa 11 | write: aaaaaa 12 | read: aaaaaa 13 | write: aaaaaaa 14 | read: aaaaaaa 15 | write: aaaaaaaa 16 | read: aaaaaaaa 17 | write: aaaaaaaaa 18 | read: aaaaaaaaa 19 | write: aaaaaaaaaa 20 | read: aaaaaaaaaa 21 | write: aaaaaaaaaaa 22 | read: aaaaaaaaaaa 23 | write: aaaaaaaaaaaa 24 | read: aaaaaaaaaaaa 25 | write: aaaaaaaaaaaaa 26 | read: aaaaaaaaaaaaa 27 | write: aaaaaaaaaaaaaa 28 | read: aaaaaaaaaaaaaa 29 | write: aaaaaaaaaaaaaaa 30 | read: aaaaaaaaaaaaaaa 31 | write: aaaaaaaaaaaaaaaa 32 | read: aaaaaaaaaaaaaaaa 33 | write: aaaaaaaaaaaaaaaaa 34 | read: aaaaaaaaaaaaaaaaa 35 | write: aaaaaaaaaaaaaaaaaa 36 | read: aaaaaaaaaaaaaaaaaa 37 | write: aaaaaaaaaaaaaaaaaaa 38 | read: aaaaaaaaaaaaaaaaaaa 39 | write: aaaaaaaaaaaaaaaaaaaa 40 | read: aaaaaaaaaaaaaaaaaaaa 41 | write: aaaaaaaaaaaaaaaaaaaaa 42 | read: aaaaaaaaaaaaaaaaaaaaa 43 | write: aaaaaaaaaaaaaaaaaaaaaa 44 | read: aaaaaaaaaaaaaaaaaaaaaa 45 | write: aaaaaaaaaaaaaaaaaaaaaaa 46 | read: aaaaaaaaaaaaaaaaaaaaaaa 47 | write: aaaaaaaaaaaaaaaaaaaaaaaa 48 | read: aaaaaaaaaaaaaaaaaaaaaaaa 49 | write: aaaaaaaaaaaaaaaaaaaaaaaaa 50 | read: aaaaaaaaaaaaaaaaaaaaaaaaa 51 | write: aaaaaaaaaaaaaaaaaaaaaaaaaa 52 | read: aaaaaaaaaaaaaaaaaaaaaaaaaa 53 | write: aaaaaaaaaaaaaaaaaaaaaaaaaaa 54 | read: aaaaaaaaaaaaaaaaaaaaaaaaaaa 55 | write: aaaaaaaaaaaaaaaaaaaaaaaaaaaa 56 | read: aaaaaaaaaaaaaaaaaaaaaaaaaaaa 57 | write: aaaaaaaaaaaaaaaaaaaaaaaaaaaaa 58 | read: aaaaaaaaaaaaaaaaaaaaaaaaaaaaa 59 | write: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 60 | read: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 61 | write: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 62 | read: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 63 | write: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 64 | read: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 65 | write: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 66 | read: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 67 | write: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 68 | read: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 69 | write: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 70 | read: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 71 | write: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 72 | read: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 73 | write: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 74 | read: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 75 | write: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 76 | read: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 77 | write: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 78 | read: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 79 | write: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 80 | read: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 81 | write: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 82 | read: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 83 | write: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 84 | read: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 85 | write: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 86 | read: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 87 | write: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 88 | read: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 89 | write: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 90 | read: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 91 | write: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 92 | read: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 93 | write: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 94 | read: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 95 | write: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 96 | read: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 97 | write: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 98 | read: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 99 | write: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 100 | read: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 101 | write: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 102 | read: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 103 | write: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 104 | read: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 105 | write: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 106 | read: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 107 | write: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 108 | read: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 109 | write: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 110 | read: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 111 | write: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 112 | read: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 113 | write: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 114 | read: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 115 | write: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 116 | read: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 117 | write: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 118 | read: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 119 | write: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 120 | read: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 121 | write: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 122 | read: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 123 | write: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 124 | read: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 125 | write: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 126 | read: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 127 | write: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 128 | read: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 129 | -------------------------------------------------------------------------------- /fiber.ml: -------------------------------------------------------------------------------- 1 | (* 2 | * Copyright (C) 2018 Yuriy Vostrikov 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 17 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | * SUCH DAMAGE. 24 | *) 25 | 26 | type id = int 27 | type fqueue = id Queue.t 28 | 29 | type 'a fiber = { mutable id: id; 30 | mutable result: 'a option; 31 | joinq: fqueue; 32 | } 33 | 34 | external self_id : unit -> int = "stub_fiber_id" [@@noalloc] 35 | 36 | external stub_create : ('a -> unit) -> 'a -> int = "stub_fiber_create" 37 | external break : unit -> unit = "stub_break" 38 | 39 | external wake_id : int -> unit = "stub_wake" 40 | external cancel_wake_id : int -> unit = "stub_cancel_wake" 41 | 42 | external yield : unit -> unit = "stub_yield" 43 | external resume : 'a fiber -> unit = "stub_resume" 44 | 45 | external unsafe_yield : unit -> 'a = "stub_unsafe_yield" 46 | external unsafe_resume : 'a fiber -> 'b -> unit = "stub_unsafe_resume" 47 | 48 | external sleep : float -> unit = "stub_fiber_sleep" 49 | 50 | module FQueue = struct 51 | include Queue 52 | let yield q = 53 | Queue.push (self_id ()) q; 54 | yield () 55 | 56 | let wake q = 57 | Queue.iter wake_id q; 58 | Queue.clear q 59 | end 60 | 61 | let wake f = 62 | wake_id f.id 63 | 64 | let cancel_wake f = 65 | cancel_wake_id f.id 66 | 67 | let create f v = 68 | let fiber = { id = -1; result = None; joinq = FQueue.create (); } in 69 | let wrap v = 70 | fiber.result <- Some (f v); 71 | FQueue.wake fiber.joinq 72 | in 73 | fiber.id <- stub_create wrap v; 74 | fiber 75 | 76 | let rec join f = 77 | match f.result with 78 | Some v -> v 79 | | None -> 80 | FQueue.yield f.joinq; 81 | join f 82 | 83 | external stub_run : 'a fiber -> unit = "stub_fiber_run" 84 | 85 | let run g a = 86 | let f = create (fun () -> 87 | let v = g a in 88 | break (); 89 | v) () in 90 | wake f; 91 | stub_run f; 92 | f.result 93 | 94 | type event = READ | WRITE 95 | external wait_io_ready : Unix.file_descr -> event -> unit = "stub_wait_io_ready" 96 | 97 | module Mutex = struct 98 | type t = { mutable locked: bool; 99 | waitq: fqueue } 100 | 101 | let create () = { locked = false; 102 | waitq = FQueue.create () } 103 | let rec lock l = 104 | match l.locked with 105 | false -> l.locked <- true 106 | | true -> FQueue.yield l.waitq; 107 | lock l 108 | 109 | let try_lock l = 110 | match l.locked with 111 | false -> l.locked <- true; 112 | true 113 | | true -> false 114 | 115 | let unlock l = 116 | l.locked <- false; 117 | FQueue.wake l.waitq 118 | 119 | let with_lock l f = 120 | try 121 | lock l; 122 | let r = f () in 123 | unlock l; 124 | r 125 | with e -> 126 | unlock l; 127 | raise e 128 | 129 | let is_locked l = l.locked 130 | end 131 | 132 | module Condition = struct 133 | type t = fqueue 134 | 135 | let create () = FQueue.create () 136 | 137 | let wait ?mutex c = 138 | let option_iter f = function Some v -> f v 139 | | None -> () in 140 | option_iter Mutex.unlock mutex; 141 | FQueue.yield c; 142 | option_iter Mutex.lock mutex 143 | 144 | let signal c = 145 | try wake_id (Queue.pop c) 146 | with FQueue.Empty -> () 147 | 148 | let broadcast c = 149 | FQueue.wake c 150 | end 151 | 152 | module MVar = struct 153 | type 'a t = { mutable value : 'a option; 154 | mutable put_wait : fqueue; 155 | mutable take_wait : fqueue } 156 | 157 | let make value = { value; 158 | put_wait = FQueue.create (); 159 | take_wait = FQueue.create (); } 160 | 161 | let create value = make (Some value) 162 | let create_empty () = make None 163 | 164 | let rec put v value = 165 | match v.value with 166 | None -> 167 | v.value <- Some value; 168 | FQueue.wake v.take_wait 169 | | Some _ -> 170 | FQueue.yield v.put_wait; 171 | put v value 172 | 173 | let rec take v = 174 | match v.value with 175 | Some value -> 176 | v.value <- None; 177 | FQueue.wake v.put_wait; 178 | value 179 | | None -> 180 | FQueue.yield v.take_wait; 181 | take v 182 | 183 | let take_available ({value; _} as v) = 184 | if value <> None then begin 185 | v.value <- None; 186 | FQueue.wake v.put_wait; 187 | end; 188 | value 189 | 190 | let is_empty v = v.value == None 191 | end 192 | 193 | let _ = 194 | assert(Sys.int_size == 63) 195 | -------------------------------------------------------------------------------- /libev/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 | /* note: the comment below could not be substantiated, but what would I care */ 43 | /* MSDN says this is required to handle SIGFPE */ 44 | /* my wild guess would be that using something floating-pointy is required */ 45 | /* for the crt to do something about it */ 46 | volatile double SIGFPE_REQ = 0.0f; 47 | 48 | static SOCKET 49 | ev_tcp_socket (void) 50 | { 51 | #if EV_USE_WSASOCKET 52 | return WSASocket (AF_INET, SOCK_STREAM, 0, 0, 0, 0); 53 | #else 54 | return socket (AF_INET, SOCK_STREAM, 0); 55 | #endif 56 | } 57 | 58 | /* oh, the humanity! */ 59 | static int 60 | ev_pipe (int filedes [2]) 61 | { 62 | struct sockaddr_in addr = { 0 }; 63 | int addr_size = sizeof (addr); 64 | struct sockaddr_in adr2; 65 | int adr2_size = sizeof (adr2); 66 | SOCKET listener; 67 | SOCKET sock [2] = { -1, -1 }; 68 | 69 | if ((listener = ev_tcp_socket ()) == INVALID_SOCKET) 70 | return -1; 71 | 72 | addr.sin_family = AF_INET; 73 | addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK); 74 | addr.sin_port = 0; 75 | 76 | if (bind (listener, (struct sockaddr *)&addr, addr_size)) 77 | goto fail; 78 | 79 | if (getsockname (listener, (struct sockaddr *)&addr, &addr_size)) 80 | goto fail; 81 | 82 | if (listen (listener, 1)) 83 | goto fail; 84 | 85 | if ((sock [0] = ev_tcp_socket ()) == INVALID_SOCKET) 86 | goto fail; 87 | 88 | if (connect (sock [0], (struct sockaddr *)&addr, addr_size)) 89 | goto fail; 90 | 91 | /* TODO: returns INVALID_SOCKET on winsock accept, not < 0. fix it */ 92 | /* when convenient, probably by just removing error checking altogether? */ 93 | if ((sock [1] = accept (listener, 0, 0)) < 0) 94 | goto fail; 95 | 96 | /* windows vista returns fantasy port numbers for sockets: 97 | * example for two interconnected tcp sockets: 98 | * 99 | * (Socket::unpack_sockaddr_in getsockname $sock0)[0] == 53364 100 | * (Socket::unpack_sockaddr_in getpeername $sock0)[0] == 53363 101 | * (Socket::unpack_sockaddr_in getsockname $sock1)[0] == 53363 102 | * (Socket::unpack_sockaddr_in getpeername $sock1)[0] == 53365 103 | * 104 | * wow! tridirectional sockets! 105 | * 106 | * this way of checking ports seems to work: 107 | */ 108 | if (getpeername (sock [0], (struct sockaddr *)&addr, &addr_size)) 109 | goto fail; 110 | 111 | if (getsockname (sock [1], (struct sockaddr *)&adr2, &adr2_size)) 112 | goto fail; 113 | 114 | errno = WSAEINVAL; 115 | if (addr_size != adr2_size 116 | || addr.sin_addr.s_addr != adr2.sin_addr.s_addr /* just to be sure, I mean, it's windows */ 117 | || addr.sin_port != adr2.sin_port) 118 | goto fail; 119 | 120 | closesocket (listener); 121 | 122 | #if EV_SELECT_IS_WINSOCKET 123 | filedes [0] = EV_WIN32_HANDLE_TO_FD (sock [0]); 124 | filedes [1] = EV_WIN32_HANDLE_TO_FD (sock [1]); 125 | #else 126 | /* when select isn't winsocket, we also expect socket, connect, accept etc. 127 | * to work on fds */ 128 | filedes [0] = sock [0]; 129 | filedes [1] = sock [1]; 130 | #endif 131 | 132 | return 0; 133 | 134 | fail: 135 | closesocket (listener); 136 | 137 | if (sock [0] != INVALID_SOCKET) closesocket (sock [0]); 138 | if (sock [1] != INVALID_SOCKET) closesocket (sock [1]); 139 | 140 | return -1; 141 | } 142 | 143 | #undef pipe 144 | #define pipe(filedes) ev_pipe (filedes) 145 | 146 | #define EV_HAVE_EV_TIME 1 147 | ev_tstamp 148 | ev_time (void) 149 | { 150 | FILETIME ft; 151 | ULARGE_INTEGER ui; 152 | 153 | GetSystemTimeAsFileTime (&ft); 154 | ui.u.LowPart = ft.dwLowDateTime; 155 | ui.u.HighPart = ft.dwHighDateTime; 156 | 157 | /* msvc cannot convert ulonglong to double... yes, it is that sucky */ 158 | return (LONGLONG)(ui.QuadPart - 116444736000000000) * 1e-7; 159 | } 160 | 161 | #endif 162 | 163 | -------------------------------------------------------------------------------- /libev/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 | -------------------------------------------------------------------------------- /libev/event.h: -------------------------------------------------------------------------------- 1 | /* 2 | * libevent compatibility header, only core events supported 3 | * 4 | * Copyright (c) 2007,2008,2010,2012 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 EVENT_H_ 41 | #define EVENT_H_ 42 | 43 | #ifdef EV_H 44 | # include EV_H 45 | #else 46 | # include "ev.h" 47 | #endif 48 | 49 | #ifndef EVLOOP_NONBLOCK 50 | # define EVLOOP_NONBLOCK EVRUN_NOWAIT 51 | #endif 52 | #ifndef EVLOOP_ONESHOT 53 | # define EVLOOP_ONESHOT EVRUN_ONCE 54 | #endif 55 | #ifndef EV_TIMEOUT 56 | # define EV_TIMEOUT EV_TIMER 57 | #endif 58 | 59 | #ifdef __cplusplus 60 | extern "C" { 61 | #endif 62 | 63 | /* we need sys/time.h for struct timeval only */ 64 | #if !defined (WIN32) || defined (__MINGW32__) 65 | # include /* mingw seems to need this, for whatever reason */ 66 | # include 67 | #endif 68 | 69 | struct event_base; 70 | 71 | #define EVLIST_TIMEOUT 0x01 72 | #define EVLIST_INSERTED 0x02 73 | #define EVLIST_SIGNAL 0x04 74 | #define EVLIST_ACTIVE 0x08 75 | #define EVLIST_INTERNAL 0x10 76 | #define EVLIST_INIT 0x80 77 | 78 | typedef void (*event_callback_fn)(int, short, void *); 79 | 80 | struct event 81 | { 82 | /* libev watchers we map onto */ 83 | union { 84 | struct ev_io io; 85 | struct ev_signal sig; 86 | } iosig; 87 | struct ev_timer to; 88 | 89 | /* compatibility slots */ 90 | struct event_base *ev_base; 91 | event_callback_fn ev_callback; 92 | void *ev_arg; 93 | int ev_fd; 94 | int ev_pri; 95 | int ev_res; 96 | int ev_flags; 97 | short ev_events; 98 | }; 99 | 100 | event_callback_fn event_get_callback (const struct event *ev); 101 | 102 | #define EV_READ EV_READ 103 | #define EV_WRITE EV_WRITE 104 | #define EV_PERSIST 0x10 105 | #define EV_ET 0x20 /* nop */ 106 | 107 | #define EVENT_SIGNAL(ev) ((int) (ev)->ev_fd) 108 | #define EVENT_FD(ev) ((int) (ev)->ev_fd) 109 | 110 | #define event_initialized(ev) ((ev)->ev_flags & EVLIST_INIT) 111 | 112 | #define evtimer_add(ev,tv) event_add (ev, tv) 113 | #define evtimer_set(ev,cb,data) event_set (ev, -1, 0, cb, data) 114 | #define evtimer_del(ev) event_del (ev) 115 | #define evtimer_pending(ev,tv) event_pending (ev, EV_TIMEOUT, tv) 116 | #define evtimer_initialized(ev) event_initialized (ev) 117 | 118 | #define timeout_add(ev,tv) evtimer_add (ev, tv) 119 | #define timeout_set(ev,cb,data) evtimer_set (ev, cb, data) 120 | #define timeout_del(ev) evtimer_del (ev) 121 | #define timeout_pending(ev,tv) evtimer_pending (ev, tv) 122 | #define timeout_initialized(ev) evtimer_initialized (ev) 123 | 124 | #define signal_add(ev,tv) event_add (ev, tv) 125 | #define signal_set(ev,sig,cb,data) event_set (ev, sig, EV_SIGNAL | EV_PERSIST, cb, data) 126 | #define signal_del(ev) event_del (ev) 127 | #define signal_pending(ev,tv) event_pending (ev, EV_SIGNAL, tv) 128 | #define signal_initialized(ev) event_initialized (ev) 129 | 130 | const char *event_get_version (void); 131 | const char *event_get_method (void); 132 | 133 | void *event_init (void); 134 | void event_base_free (struct event_base *base); 135 | 136 | #define EVLOOP_ONCE EVLOOP_ONESHOT 137 | int event_loop (int); 138 | int event_loopexit (struct timeval *tv); 139 | int event_dispatch (void); 140 | 141 | #define _EVENT_LOG_DEBUG 0 142 | #define _EVENT_LOG_MSG 1 143 | #define _EVENT_LOG_WARN 2 144 | #define _EVENT_LOG_ERR 3 145 | typedef void (*event_log_cb)(int severity, const char *msg); 146 | void event_set_log_callback(event_log_cb cb); 147 | 148 | void event_set (struct event *ev, int fd, short events, void (*cb)(int, short, void *), void *arg); 149 | int event_once (int fd, short events, void (*cb)(int, short, void *), void *arg, struct timeval *tv); 150 | 151 | int event_add (struct event *ev, struct timeval *tv); 152 | int event_del (struct event *ev); 153 | void event_active (struct event *ev, int res, short ncalls); /* ncalls is being ignored */ 154 | 155 | int event_pending (struct event *ev, short, struct timeval *tv); 156 | 157 | int event_priority_init (int npri); 158 | int event_priority_set (struct event *ev, int pri); 159 | 160 | struct event_base *event_base_new (void); 161 | const char *event_base_get_method (const struct event_base *); 162 | int event_base_set (struct event_base *base, struct event *ev); 163 | int event_base_loop (struct event_base *base, int); 164 | int event_base_loopexit (struct event_base *base, struct timeval *tv); 165 | int event_base_dispatch (struct event_base *base); 166 | int event_base_once (struct event_base *base, int fd, short events, void (*cb)(int, short, void *), void *arg, struct timeval *tv); 167 | int event_base_priority_init (struct event_base *base, int fd); 168 | 169 | /* next line is different in the libevent+libev version */ 170 | /*libevent-include*/ 171 | 172 | #ifdef __cplusplus 173 | } 174 | #endif 175 | 176 | #endif 177 | 178 | -------------------------------------------------------------------------------- /libev/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 | -------------------------------------------------------------------------------- /fiber.mli: -------------------------------------------------------------------------------- 1 | (* 2 | * Copyright (C) 2018 Yuriy Vostrikov 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 17 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | * SUCH DAMAGE. 24 | *) 25 | 26 | (** Fast native fibers for OCaml. 27 | 28 | Fibers are implemented by tying together {{: 29 | http://software.schmorp.de/pkg/libcoro.html} libcoro} and OCaml's 30 | GC hooks. This design allows {e allocation free} context switches 31 | with decent performance. For example, i7-8750H running Linux can 32 | perform about 13.7M fiber context switches per second. If you want 33 | to measure it by yourself just run [make all bench] from the source 34 | directory. *) 35 | 36 | (** {2 Basic API} 37 | 38 | The basic API allows creation of a fiber and switching back and 39 | forth. It follows closely {{: https://www.lua.org/pil/9.1.html} 40 | Lua coroutines}. 41 | 42 | To give some idea of how it works, here is an example of printing 43 | numbers from 1 to 10 in a fiber way: 44 | {[ 45 | let fb = Fiber.create (fun () -> 46 | for i = 1 to 10 do 47 | print_int i; 48 | print_newline (); 49 | Fiber.yield () 50 | done) () 51 | 52 | let _ = 53 | for j = 1 to 10 do 54 | Fiber.resume fb 55 | done 56 | ]} 57 | 58 | More examples available in the [examples] directory. 59 | *) 60 | 61 | type 'a fiber 62 | (** a fiber type. *) 63 | 64 | val create : ('a -> 'b) -> 'a -> 'b fiber 65 | (** [create f arg] creates a fiber. 66 | 67 | A fiber can be in one of the following states: [sleeping], 68 | [running] or [dead]. A fiber will initially be in state 69 | {!sleeping}. Upon {!resume}, the fiber will execute [f arg]. When 70 | [f] returns the fiber enters state [dead]. return value of [f] can 71 | be obtained by calling {!join}. 72 | 73 | Current implementation allocates 112 pages of stack and 16 pages of 74 | guard zone. Graceful stack overflow detection is not (yet) 75 | implemented and stack overflow will result in segmentation 76 | violation. 77 | 78 | Fiber context creation is a relatively expensive process and 79 | therefore library caches unused contexts of dead fibers. The cache 80 | size is unbound, be careful when creating a lot of short living 81 | fibers. 82 | 83 | Exception behavior is similar to the vanilla OCaml. You can [raise] 84 | exception inside a fiber and catch it with a [try ... with ...]. 85 | Uncaught exception in a fiber will terminate the whole program. *) 86 | 87 | val yield : unit -> unit 88 | (** [yield] yields control to the caller (the fiber which issued 89 | {!resume}). Consequently, it is an error to call [yield] from 90 | initial context, since there is no caller. *) 91 | 92 | val resume : 'a fiber -> unit 93 | (** [resume fb] resumes the fiber [fb], that is: transfer execution 94 | context to it. Trying to resume dead fiber will raise 95 | [Invalid_argument "Fiber.resume"]. *) 96 | 97 | (** {2 Event loop} 98 | Integration with {{: http://software.schmorp.de/pkg/libev.html} libev} 99 | event loop. 100 | *) 101 | 102 | val run : ('a -> 'b) -> 'a -> 'b option 103 | (** [run f arg] starts the event loop and executes [f arg] inside a 104 | newly created fiber. Returns [None] if {!break} is called during 105 | execution of [f]. *) 106 | 107 | val break : unit -> unit 108 | (** [break] stops event loop and exits from {!run}. *) 109 | 110 | val wake : 'a fiber -> unit 111 | (** [wake fb] register a wakeup for a fiber [fb]. A fiber [fb] will be resumed 112 | in the next iteration of the event loop. *) 113 | 114 | val cancel_wake : 'a fiber -> unit 115 | (** [cancel_wake fb] cancels pending wakeup for [fb]. *) 116 | 117 | val join : 'a fiber -> 'a 118 | (** [join fb] suspends the current fiber until [fb] is dead and returns 119 | the return value of it. It is permitted to call [join fb] several times. *) 120 | 121 | type event = READ | WRITE 122 | val wait_io_ready : Unix.file_descr -> event -> unit 123 | (** [wait_io_ready fd ev] suspends the current fiber until reading or 124 | writing from file descriptor [fd] can be performed without 125 | blocking. 126 | 127 | Please note, that the library itself doesn't put file descriptor 128 | into a nonblocking mode. Therefore, it's advisable to either put 129 | file descriptor into nonblocking mode before calling [read(2)] and 130 | [write(2)]or use [send(2)]and [recv(2)] with corresponding 131 | [flags]. *) 132 | 133 | val sleep : float -> unit 134 | (** [sleep s] suspends the current fiber for [s] seconds. *) 135 | 136 | (**/**) 137 | 138 | (** {2 Unsafe} 139 | 140 | Value passing variants of {!yield} and {!resume}. Please note, that 141 | it's possible to circumvent type checker by using these 142 | functions. It's a user’s responsibility to ensure that the type of 143 | value on both sides of the calls match. *) 144 | 145 | val unsafe_yield : unit -> 'a 146 | (** [unsafe_yield] yeilds back to a caller. See {!yield}. *) 147 | 148 | val unsafe_resume : 'a fiber -> 'b -> unit 149 | (** [unsafe_resume fb value] resumes [fb] and passes [value] to 150 | it. This [value] will be returned inside [fb] as result of 151 | [unsafe_yield] call. *) 152 | 153 | (**/**) 154 | 155 | (** {2 Synchronisation} *) 156 | 157 | module Mutex : sig 158 | type t 159 | val create : unit -> t 160 | val lock : t -> unit 161 | val try_lock : t -> bool 162 | val unlock : t -> unit 163 | val with_lock : t -> (unit -> 'a) -> 'a 164 | val is_locked : t -> bool 165 | end 166 | 167 | module Condition : sig 168 | type t 169 | val create : unit -> t 170 | val wait : ?mutex:Mutex.t -> t -> unit 171 | val signal : t -> unit 172 | val broadcast : t -> unit 173 | end 174 | 175 | module MVar : sig 176 | type 'a t 177 | val create : 'a -> 'a t 178 | val create_empty : unit -> 'a t 179 | val put : 'a t -> 'a -> unit 180 | val take : 'a t -> 'a 181 | val take_available : 'a t -> 'a option 182 | val is_empty : 'a t -> bool 183 | end 184 | -------------------------------------------------------------------------------- /libev/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 | inline_speed 59 | void 60 | port_associate_and_check (EV_P_ int fd, int ev) 61 | { 62 | if (0 > 63 | port_associate ( 64 | backend_fd, PORT_SOURCE_FD, fd, 65 | (ev & EV_READ ? POLLIN : 0) 66 | | (ev & EV_WRITE ? POLLOUT : 0), 67 | 0 68 | ) 69 | ) 70 | { 71 | if (errno == EBADFD) 72 | fd_kill (EV_A_ fd); 73 | else 74 | ev_syserr ("(libev) port_associate"); 75 | } 76 | } 77 | 78 | static void 79 | port_modify (EV_P_ int fd, int oev, int nev) 80 | { 81 | /* we need to reassociate no matter what, as closes are 82 | * once more silently being discarded. 83 | */ 84 | if (!nev) 85 | { 86 | if (oev) 87 | port_dissociate (backend_fd, PORT_SOURCE_FD, fd); 88 | } 89 | else 90 | port_associate_and_check (EV_A_ fd, nev); 91 | } 92 | 93 | static void 94 | port_poll (EV_P_ ev_tstamp timeout) 95 | { 96 | int res, i; 97 | struct timespec ts; 98 | uint_t nget = 1; 99 | 100 | /* we initialise this to something we will skip in the loop, as */ 101 | /* port_getn can return with nget unchanged, but no indication */ 102 | /* whether it was the original value or has been updated :/ */ 103 | port_events [0].portev_source = 0; 104 | 105 | EV_RELEASE_CB; 106 | EV_TS_SET (ts, timeout); 107 | res = port_getn (backend_fd, port_events, port_eventmax, &nget, &ts); 108 | EV_ACQUIRE_CB; 109 | 110 | /* port_getn may or may not set nget on error */ 111 | /* so we rely on port_events [0].portev_source not being updated */ 112 | if (res == -1 && errno != ETIME && errno != EINTR) 113 | ev_syserr ("(libev) port_getn (see http://bugs.opensolaris.org/view_bug.do?bug_id=6268715, try LIBEV_FLAGS=3 env variable)"); 114 | 115 | for (i = 0; i < nget; ++i) 116 | { 117 | if (port_events [i].portev_source == PORT_SOURCE_FD) 118 | { 119 | int fd = port_events [i].portev_object; 120 | 121 | fd_event ( 122 | EV_A_ 123 | fd, 124 | (port_events [i].portev_events & (POLLOUT | POLLERR | POLLHUP) ? EV_WRITE : 0) 125 | | (port_events [i].portev_events & (POLLIN | POLLERR | POLLHUP) ? EV_READ : 0) 126 | ); 127 | 128 | fd_change (EV_A_ fd, EV__IOFDSET); 129 | } 130 | } 131 | 132 | if (expect_false (nget == port_eventmax)) 133 | { 134 | ev_free (port_events); 135 | port_eventmax = array_nextsize (sizeof (port_event_t), port_eventmax, port_eventmax + 1); 136 | port_events = (port_event_t *)ev_malloc (sizeof (port_event_t) * port_eventmax); 137 | } 138 | } 139 | 140 | inline_size 141 | int 142 | port_init (EV_P_ int flags) 143 | { 144 | /* Initialize the kernel queue */ 145 | if ((backend_fd = port_create ()) < 0) 146 | return 0; 147 | 148 | assert (("libev: PORT_SOURCE_FD must not be zero", PORT_SOURCE_FD)); 149 | 150 | fcntl (backend_fd, F_SETFD, FD_CLOEXEC); /* not sure if necessary, hopefully doesn't hurt */ 151 | 152 | /* if my reading of the opensolaris kernel sources are correct, then 153 | * opensolaris does something very stupid: it checks if the time has already 154 | * elapsed and doesn't round up if that is the case,m otherwise it DOES round 155 | * up. Since we can't know what the case is, we need to guess by using a 156 | * "large enough" timeout. Normally, 1e-9 would be correct. 157 | */ 158 | backend_mintime = 1e-3; /* needed to compensate for port_getn returning early */ 159 | backend_modify = port_modify; 160 | backend_poll = port_poll; 161 | 162 | port_eventmax = 64; /* initial number of events receivable per poll */ 163 | port_events = (port_event_t *)ev_malloc (sizeof (port_event_t) * port_eventmax); 164 | 165 | return EVBACKEND_PORT; 166 | } 167 | 168 | inline_size 169 | void 170 | port_destroy (EV_P) 171 | { 172 | ev_free (port_events); 173 | } 174 | 175 | inline_size 176 | void 177 | port_fork (EV_P) 178 | { 179 | close (backend_fd); 180 | 181 | while ((backend_fd = port_create ()) < 0) 182 | ev_syserr ("(libev) port"); 183 | 184 | fcntl (backend_fd, F_SETFD, FD_CLOEXEC); 185 | 186 | /* re-register interest in fds */ 187 | fd_rearm_all (EV_A); 188 | } 189 | 190 | -------------------------------------------------------------------------------- /libev/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 | inline_speed 47 | void 48 | kqueue_change (EV_P_ int fd, int filter, int flags, int fflags) 49 | { 50 | ++kqueue_changecnt; 51 | array_needsize (struct kevent, kqueue_changes, kqueue_changemax, kqueue_changecnt, EMPTY2); 52 | 53 | EV_SET (&kqueue_changes [kqueue_changecnt - 1], fd, filter, flags, fflags, 0, 0); 54 | } 55 | 56 | /* OS X at least needs this */ 57 | #ifndef EV_ENABLE 58 | # define EV_ENABLE 0 59 | #endif 60 | #ifndef NOTE_EOF 61 | # define NOTE_EOF 0 62 | #endif 63 | 64 | static void 65 | kqueue_modify (EV_P_ int fd, int oev, int nev) 66 | { 67 | if (oev != nev) 68 | { 69 | if (oev & EV_READ) 70 | kqueue_change (EV_A_ fd, EVFILT_READ , EV_DELETE, 0); 71 | 72 | if (oev & EV_WRITE) 73 | kqueue_change (EV_A_ fd, EVFILT_WRITE, EV_DELETE, 0); 74 | } 75 | 76 | /* to detect close/reopen reliably, we have to re-add */ 77 | /* event requests even when oev == nev */ 78 | 79 | if (nev & EV_READ) 80 | kqueue_change (EV_A_ fd, EVFILT_READ , EV_ADD | EV_ENABLE, NOTE_EOF); 81 | 82 | if (nev & EV_WRITE) 83 | kqueue_change (EV_A_ fd, EVFILT_WRITE, EV_ADD | EV_ENABLE, NOTE_EOF); 84 | } 85 | 86 | static void 87 | kqueue_poll (EV_P_ ev_tstamp timeout) 88 | { 89 | int res, i; 90 | struct timespec ts; 91 | 92 | /* need to resize so there is enough space for errors */ 93 | if (kqueue_changecnt > kqueue_eventmax) 94 | { 95 | ev_free (kqueue_events); 96 | kqueue_eventmax = array_nextsize (sizeof (struct kevent), kqueue_eventmax, kqueue_changecnt); 97 | kqueue_events = (struct kevent *)ev_malloc (sizeof (struct kevent) * kqueue_eventmax); 98 | } 99 | 100 | EV_RELEASE_CB; 101 | EV_TS_SET (ts, timeout); 102 | res = kevent (backend_fd, kqueue_changes, kqueue_changecnt, kqueue_events, kqueue_eventmax, &ts); 103 | EV_ACQUIRE_CB; 104 | kqueue_changecnt = 0; 105 | 106 | if (expect_false (res < 0)) 107 | { 108 | if (errno != EINTR) 109 | ev_syserr ("(libev) kevent"); 110 | 111 | return; 112 | } 113 | 114 | for (i = 0; i < res; ++i) 115 | { 116 | int fd = kqueue_events [i].ident; 117 | 118 | if (expect_false (kqueue_events [i].flags & EV_ERROR)) 119 | { 120 | int err = kqueue_events [i].data; 121 | 122 | /* we are only interested in errors for fds that we are interested in :) */ 123 | if (anfds [fd].events) 124 | { 125 | if (err == ENOENT) /* resubmit changes on ENOENT */ 126 | kqueue_modify (EV_A_ fd, 0, anfds [fd].events); 127 | else if (err == EBADF) /* on EBADF, we re-check the fd */ 128 | { 129 | if (fd_valid (fd)) 130 | kqueue_modify (EV_A_ fd, 0, anfds [fd].events); 131 | else 132 | fd_kill (EV_A_ fd); 133 | } 134 | else /* on all other errors, we error out on the fd */ 135 | fd_kill (EV_A_ fd); 136 | } 137 | } 138 | else 139 | fd_event ( 140 | EV_A_ 141 | fd, 142 | kqueue_events [i].filter == EVFILT_READ ? EV_READ 143 | : kqueue_events [i].filter == EVFILT_WRITE ? EV_WRITE 144 | : 0 145 | ); 146 | } 147 | 148 | if (expect_false (res == kqueue_eventmax)) 149 | { 150 | ev_free (kqueue_events); 151 | kqueue_eventmax = array_nextsize (sizeof (struct kevent), kqueue_eventmax, kqueue_eventmax + 1); 152 | kqueue_events = (struct kevent *)ev_malloc (sizeof (struct kevent) * kqueue_eventmax); 153 | } 154 | } 155 | 156 | inline_size 157 | int 158 | kqueue_init (EV_P_ int flags) 159 | { 160 | /* initialize the kernel queue */ 161 | kqueue_fd_pid = getpid (); 162 | if ((backend_fd = kqueue ()) < 0) 163 | return 0; 164 | 165 | fcntl (backend_fd, F_SETFD, FD_CLOEXEC); /* not sure if necessary, hopefully doesn't hurt */ 166 | 167 | backend_mintime = 1e-9; /* apparently, they did the right thing in freebsd */ 168 | backend_modify = kqueue_modify; 169 | backend_poll = kqueue_poll; 170 | 171 | kqueue_eventmax = 64; /* initial number of events receivable per poll */ 172 | kqueue_events = (struct kevent *)ev_malloc (sizeof (struct kevent) * kqueue_eventmax); 173 | 174 | kqueue_changes = 0; 175 | kqueue_changemax = 0; 176 | kqueue_changecnt = 0; 177 | 178 | return EVBACKEND_KQUEUE; 179 | } 180 | 181 | inline_size 182 | void 183 | kqueue_destroy (EV_P) 184 | { 185 | ev_free (kqueue_events); 186 | ev_free (kqueue_changes); 187 | } 188 | 189 | inline_size 190 | void 191 | kqueue_fork (EV_P) 192 | { 193 | /* some BSD kernels don't just destroy the kqueue itself, 194 | * but also close the fd, which isn't documented, and 195 | * impossible to support properly. 196 | * we remember the pid of the kqueue call and only close 197 | * the fd if the pid is still the same. 198 | * this leaks fds on sane kernels, but BSD interfaces are 199 | * notoriously buggy and rarely get fixed. 200 | */ 201 | pid_t newpid = getpid (); 202 | 203 | if (newpid == kqueue_fd_pid) 204 | close (backend_fd); 205 | 206 | kqueue_fd_pid = newpid; 207 | while ((backend_fd = kqueue ()) < 0) 208 | ev_syserr ("(libev) kqueue"); 209 | 210 | fcntl (backend_fd, F_SETFD, FD_CLOEXEC); 211 | 212 | /* re-register interest in fds */ 213 | fd_rearm_all (EV_A); 214 | } 215 | 216 | /* sys/event.h defines EV_ERROR */ 217 | #undef EV_ERROR 218 | 219 | -------------------------------------------------------------------------------- /libev/event_compat.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2000-2004 Niels Provos 3 | * Copyright (c) 2008 Marc Alexander Lehmann 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 3. The name of the author may not be used to endorse or promote products 15 | * derived from this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | #ifdef __cplusplus 29 | extern "C" { 30 | #endif 31 | 32 | #ifdef _WIN32 33 | # define WIN32_LEAN_AND_MEAN 34 | # include 35 | # undef WIN32_LEAN_AND_MEAN 36 | typedef unsigned char u_char; 37 | typedef unsigned short u_short; 38 | #else 39 | # include 40 | # include 41 | # include 42 | #endif 43 | 44 | #include 45 | 46 | /* Fix so that ppl dont have to run with */ 47 | #ifndef TAILQ_ENTRY 48 | #define _EVENT_DEFINED_TQENTRY 49 | #define TAILQ_ENTRY(type) \ 50 | struct { \ 51 | struct type *tqe_next; /* next element */ \ 52 | struct type **tqe_prev; /* address of previous next element */ \ 53 | } 54 | #endif /* !TAILQ_ENTRY */ 55 | #ifndef RB_ENTRY 56 | #define _EVENT_DEFINED_RBENTRY 57 | #define RB_ENTRY(type) \ 58 | struct { \ 59 | struct type *rbe_left; /* left element */ \ 60 | struct type *rbe_right; /* right element */ \ 61 | struct type *rbe_parent; /* parent element */ \ 62 | int rbe_color; /* node color */ \ 63 | } 64 | #endif /* !RB_ENTRY */ 65 | 66 | /* 67 | * Key-Value pairs. Can be used for HTTP headers but also for 68 | * query argument parsing. 69 | */ 70 | struct evkeyval { 71 | TAILQ_ENTRY(evkeyval) next; 72 | 73 | char *key; 74 | char *value; 75 | }; 76 | 77 | #ifdef _EVENT_DEFINED_TQENTRY 78 | #undef TAILQ_ENTRY 79 | struct event_list; 80 | struct evkeyvalq; 81 | #undef _EVENT_DEFINED_TQENTRY 82 | #else 83 | TAILQ_HEAD (event_list, event); 84 | TAILQ_HEAD (evkeyvalq, evkeyval); 85 | #endif /* _EVENT_DEFINED_TQENTRY */ 86 | #ifdef _EVENT_DEFINED_RBENTRY 87 | #undef RB_ENTRY 88 | #undef _EVENT_DEFINED_RBENTRY 89 | #endif /* _EVENT_DEFINED_RBENTRY */ 90 | 91 | struct eventop { 92 | char *name; 93 | void *(*init)(struct event_base *); 94 | int (*add)(void *, struct event *); 95 | int (*del)(void *, struct event *); 96 | int (*recalc)(struct event_base *, void *, int); 97 | int (*dispatch)(struct event_base *, void *, struct timeval *); 98 | void (*dealloc)(struct event_base *, void *); 99 | }; 100 | 101 | /* These functions deal with buffering input and output */ 102 | 103 | struct evbuffer { 104 | u_char *buffer; 105 | u_char *orig_buffer; 106 | 107 | size_t misalign; 108 | size_t totallen; 109 | size_t off; 110 | 111 | void (*cb)(struct evbuffer *, size_t, size_t, void *); 112 | void *cbarg; 113 | }; 114 | 115 | /* Just for error reporting - use other constants otherwise */ 116 | #define EVBUFFER_READ 0x01 117 | #define EVBUFFER_WRITE 0x02 118 | #define EVBUFFER_EOF 0x10 119 | #define EVBUFFER_ERROR 0x20 120 | #define EVBUFFER_TIMEOUT 0x40 121 | 122 | struct bufferevent; 123 | typedef void (*evbuffercb)(struct bufferevent *, void *); 124 | typedef void (*everrorcb)(struct bufferevent *, short what, void *); 125 | 126 | struct event_watermark { 127 | size_t low; 128 | size_t high; 129 | }; 130 | 131 | struct bufferevent { 132 | struct event ev_read; 133 | struct event ev_write; 134 | 135 | struct evbuffer *input; 136 | struct evbuffer *output; 137 | 138 | struct event_watermark wm_read; 139 | struct event_watermark wm_write; 140 | 141 | evbuffercb readcb; 142 | evbuffercb writecb; 143 | everrorcb errorcb; 144 | void *cbarg; 145 | 146 | int timeout_read; /* in seconds */ 147 | int timeout_write; /* in seconds */ 148 | 149 | short enabled; /* events that are currently enabled */ 150 | }; 151 | 152 | struct bufferevent *bufferevent_new(int fd, 153 | evbuffercb readcb, evbuffercb writecb, everrorcb errorcb, void *cbarg); 154 | int bufferevent_base_set(struct event_base *base, struct bufferevent *bufev); 155 | int bufferevent_priority_set(struct bufferevent *bufev, int pri); 156 | void bufferevent_free(struct bufferevent *bufev); 157 | int bufferevent_write(struct bufferevent *bufev, const void *data, size_t size); 158 | int bufferevent_write_buffer(struct bufferevent *bufev, struct evbuffer *buf); 159 | size_t bufferevent_read(struct bufferevent *bufev, void *data, size_t size); 160 | int bufferevent_enable(struct bufferevent *bufev, short event); 161 | int bufferevent_disable(struct bufferevent *bufev, short event); 162 | void bufferevent_settimeout(struct bufferevent *bufev, 163 | int timeout_read, int timeout_write); 164 | 165 | #define EVBUFFER_LENGTH(x) (x)->off 166 | #define EVBUFFER_DATA(x) (x)->buffer 167 | #define EVBUFFER_INPUT(x) (x)->input 168 | #define EVBUFFER_OUTPUT(x) (x)->output 169 | 170 | struct evbuffer *evbuffer_new(void); 171 | void evbuffer_free(struct evbuffer *); 172 | int evbuffer_expand(struct evbuffer *, size_t); 173 | int evbuffer_add(struct evbuffer *, const void *, size_t); 174 | int evbuffer_remove(struct evbuffer *, void *, size_t); 175 | char *evbuffer_readline(struct evbuffer *); 176 | int evbuffer_add_buffer(struct evbuffer *, struct evbuffer *); 177 | int evbuffer_add_printf(struct evbuffer *, const char *fmt, ...); 178 | int evbuffer_add_vprintf(struct evbuffer *, const char *fmt, va_list ap); 179 | void evbuffer_drain(struct evbuffer *, size_t); 180 | int evbuffer_write(struct evbuffer *, int); 181 | int evbuffer_read(struct evbuffer *, int, int); 182 | u_char *evbuffer_find(struct evbuffer *, const u_char *, size_t); 183 | void evbuffer_setcb(struct evbuffer *, void (*)(struct evbuffer *, size_t, size_t, void *), void *); 184 | 185 | /* 186 | * Marshaling tagged data - We assume that all tags are inserted in their 187 | * numeric order - so that unknown tags will always be higher than the 188 | * known ones - and we can just ignore the end of an event buffer. 189 | */ 190 | 191 | void evtag_init(void); 192 | 193 | void evtag_marshal(struct evbuffer *evbuf, uint32_t tag, const void *data, 194 | uint32_t len); 195 | 196 | void encode_int(struct evbuffer *evbuf, uint32_t number); 197 | 198 | void evtag_marshal_int(struct evbuffer *evbuf, uint32_t tag, uint32_t integer); 199 | 200 | void evtag_marshal_string(struct evbuffer *buf, uint32_t tag, 201 | const char *string); 202 | 203 | void evtag_marshal_timeval(struct evbuffer *evbuf, uint32_t tag, 204 | struct timeval *tv); 205 | 206 | int evtag_unmarshal(struct evbuffer *src, uint32_t *ptag, struct evbuffer *dst); 207 | int evtag_peek(struct evbuffer *evbuf, uint32_t *ptag); 208 | int evtag_peek_length(struct evbuffer *evbuf, uint32_t *plength); 209 | int evtag_payload_length(struct evbuffer *evbuf, uint32_t *plength); 210 | int evtag_consume(struct evbuffer *evbuf); 211 | 212 | int evtag_unmarshal_int(struct evbuffer *evbuf, uint32_t need_tag, 213 | uint32_t *pinteger); 214 | 215 | int evtag_unmarshal_fixed(struct evbuffer *src, uint32_t need_tag, void *data, 216 | size_t len); 217 | 218 | int evtag_unmarshal_string(struct evbuffer *evbuf, uint32_t need_tag, 219 | char **pstring); 220 | 221 | int evtag_unmarshal_timeval(struct evbuffer *evbuf, uint32_t need_tag, 222 | struct timeval *ptv); 223 | 224 | #ifdef __cplusplus 225 | } 226 | #endif 227 | -------------------------------------------------------------------------------- /libev/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 | inline_size 275 | int 276 | select_init (EV_P_ int flags) 277 | { 278 | backend_mintime = 1e-6; 279 | backend_modify = select_modify; 280 | backend_poll = select_poll; 281 | 282 | #if EV_SELECT_USE_FD_SET 283 | vec_ri = ev_malloc (sizeof (fd_set)); FD_ZERO ((fd_set *)vec_ri); 284 | vec_ro = ev_malloc (sizeof (fd_set)); 285 | vec_wi = ev_malloc (sizeof (fd_set)); FD_ZERO ((fd_set *)vec_wi); 286 | vec_wo = ev_malloc (sizeof (fd_set)); 287 | #ifdef _WIN32 288 | vec_eo = ev_malloc (sizeof (fd_set)); 289 | #endif 290 | #else 291 | vec_max = 0; 292 | vec_ri = 0; 293 | vec_ro = 0; 294 | vec_wi = 0; 295 | vec_wo = 0; 296 | #ifdef _WIN32 297 | vec_eo = 0; 298 | #endif 299 | #endif 300 | 301 | return EVBACKEND_SELECT; 302 | } 303 | 304 | inline_size 305 | void 306 | select_destroy (EV_P) 307 | { 308 | ev_free (vec_ri); 309 | ev_free (vec_ro); 310 | ev_free (vec_wi); 311 | ev_free (vec_wo); 312 | #ifdef _WIN32 313 | ev_free (vec_eo); 314 | #endif 315 | } 316 | 317 | -------------------------------------------------------------------------------- /libev/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 |= 2; 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 |= 2; /* 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 | inline_size 239 | int 240 | epoll_init (EV_P_ int flags) 241 | { 242 | #if defined EPOLL_CLOEXEC && !defined __ANDROID__ 243 | backend_fd = epoll_create1 (EPOLL_CLOEXEC); 244 | 245 | if (backend_fd < 0 && (errno == EINVAL || errno == ENOSYS)) 246 | #endif 247 | backend_fd = epoll_create (256); 248 | 249 | if (backend_fd < 0) 250 | return 0; 251 | 252 | fcntl (backend_fd, F_SETFD, FD_CLOEXEC); 253 | 254 | backend_mintime = 1e-3; /* epoll does sometimes return early, this is just to avoid the worst */ 255 | backend_modify = epoll_modify; 256 | backend_poll = epoll_poll; 257 | 258 | epoll_eventmax = 64; /* initial number of events receivable per poll */ 259 | epoll_events = (struct epoll_event *)ev_malloc (sizeof (struct epoll_event) * epoll_eventmax); 260 | 261 | return EVBACKEND_EPOLL; 262 | } 263 | 264 | inline_size 265 | void 266 | epoll_destroy (EV_P) 267 | { 268 | ev_free (epoll_events); 269 | array_free (epoll_eperm, EMPTY); 270 | } 271 | 272 | inline_size 273 | void 274 | epoll_fork (EV_P) 275 | { 276 | close (backend_fd); 277 | 278 | while ((backend_fd = epoll_create (256)) < 0) 279 | ev_syserr ("(libev) epoll_create"); 280 | 281 | fcntl (backend_fd, F_SETFD, FD_CLOEXEC); 282 | 283 | fd_rearm_all (EV_A); 284 | } 285 | 286 | -------------------------------------------------------------------------------- /libev/event.c: -------------------------------------------------------------------------------- 1 | /* 2 | * libevent compatibility layer 3 | * 4 | * Copyright (c) 2007,2008,2009,2010,2012 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 | 44 | #ifdef EV_EVENT_H 45 | # include EV_EVENT_H 46 | #else 47 | # include "event.h" 48 | #endif 49 | 50 | #if EV_MULTIPLICITY 51 | # define dLOOPev struct ev_loop *loop = (struct ev_loop *)ev->ev_base 52 | # define dLOOPbase struct ev_loop *loop = (struct ev_loop *)base 53 | #else 54 | # define dLOOPev 55 | # define dLOOPbase 56 | #endif 57 | 58 | /* never accessed, will always be cast from/to ev_loop */ 59 | struct event_base 60 | { 61 | int dummy; 62 | }; 63 | 64 | static struct event_base *ev_x_cur; 65 | 66 | static ev_tstamp 67 | ev_tv_get (struct timeval *tv) 68 | { 69 | if (tv) 70 | { 71 | ev_tstamp after = tv->tv_sec + tv->tv_usec * 1e-6; 72 | return after ? after : 1e-6; 73 | } 74 | else 75 | return -1.; 76 | } 77 | 78 | #define EVENT_STRINGIFY(s) # s 79 | #define EVENT_VERSION(a,b) EVENT_STRINGIFY (a) "." EVENT_STRINGIFY (b) 80 | 81 | const char * 82 | event_get_version (void) 83 | { 84 | /* returns ABI, not API or library, version */ 85 | return EVENT_VERSION (EV_VERSION_MAJOR, EV_VERSION_MINOR); 86 | } 87 | 88 | const char * 89 | event_get_method (void) 90 | { 91 | return "libev"; 92 | } 93 | 94 | void *event_init (void) 95 | { 96 | #if EV_MULTIPLICITY 97 | if (ev_x_cur) 98 | ev_x_cur = (struct event_base *)ev_loop_new (EVFLAG_AUTO); 99 | else 100 | ev_x_cur = (struct event_base *)ev_default_loop (EVFLAG_AUTO); 101 | #else 102 | assert (("libev: multiple event bases not supported when not compiled with EV_MULTIPLICITY", !ev_x_cur)); 103 | 104 | ev_x_cur = (struct event_base *)(long)ev_default_loop (EVFLAG_AUTO); 105 | #endif 106 | 107 | return ev_x_cur; 108 | } 109 | 110 | const char * 111 | event_base_get_method (const struct event_base *base) 112 | { 113 | return "libev"; 114 | } 115 | 116 | struct event_base * 117 | event_base_new (void) 118 | { 119 | #if EV_MULTIPLICITY 120 | return (struct event_base *)ev_loop_new (EVFLAG_AUTO); 121 | #else 122 | assert (("libev: multiple event bases not supported when not compiled with EV_MULTIPLICITY")); 123 | return NULL; 124 | #endif 125 | } 126 | 127 | void event_base_free (struct event_base *base) 128 | { 129 | dLOOPbase; 130 | 131 | #if EV_MULTIPLICITY 132 | if (!ev_is_default_loop (loop)) 133 | ev_loop_destroy (loop); 134 | #endif 135 | } 136 | 137 | int event_dispatch (void) 138 | { 139 | return event_base_dispatch (ev_x_cur); 140 | } 141 | 142 | #ifdef EV_STANDALONE 143 | void event_set_log_callback (event_log_cb cb) 144 | { 145 | /* nop */ 146 | } 147 | #endif 148 | 149 | int event_loop (int flags) 150 | { 151 | return event_base_loop (ev_x_cur, flags); 152 | } 153 | 154 | int event_loopexit (struct timeval *tv) 155 | { 156 | return event_base_loopexit (ev_x_cur, tv); 157 | } 158 | 159 | event_callback_fn event_get_callback 160 | (const struct event *ev) 161 | { 162 | return ev->ev_callback; 163 | } 164 | 165 | static void 166 | ev_x_cb (struct event *ev, int revents) 167 | { 168 | revents &= EV_READ | EV_WRITE | EV_TIMER | EV_SIGNAL; 169 | 170 | ev->ev_res = revents; 171 | ev->ev_callback (ev->ev_fd, (short)revents, ev->ev_arg); 172 | } 173 | 174 | static void 175 | ev_x_cb_sig (EV_P_ struct ev_signal *w, int revents) 176 | { 177 | struct event *ev = (struct event *)(((char *)w) - offsetof (struct event, iosig.sig)); 178 | 179 | if (revents & EV_ERROR) 180 | event_del (ev); 181 | 182 | ev_x_cb (ev, revents); 183 | } 184 | 185 | static void 186 | ev_x_cb_io (EV_P_ struct ev_io *w, int revents) 187 | { 188 | struct event *ev = (struct event *)(((char *)w) - offsetof (struct event, iosig.io)); 189 | 190 | if ((revents & EV_ERROR) || !(ev->ev_events & EV_PERSIST)) 191 | event_del (ev); 192 | 193 | ev_x_cb (ev, revents); 194 | } 195 | 196 | static void 197 | ev_x_cb_to (EV_P_ struct ev_timer *w, int revents) 198 | { 199 | struct event *ev = (struct event *)(((char *)w) - offsetof (struct event, to)); 200 | 201 | event_del (ev); 202 | 203 | ev_x_cb (ev, revents); 204 | } 205 | 206 | void event_set (struct event *ev, int fd, short events, void (*cb)(int, short, void *), void *arg) 207 | { 208 | if (events & EV_SIGNAL) 209 | ev_init (&ev->iosig.sig, ev_x_cb_sig); 210 | else 211 | ev_init (&ev->iosig.io, ev_x_cb_io); 212 | 213 | ev_init (&ev->to, ev_x_cb_to); 214 | 215 | ev->ev_base = ev_x_cur; /* not threadsafe, but it's how libevent works */ 216 | ev->ev_fd = fd; 217 | ev->ev_events = events; 218 | ev->ev_pri = 0; 219 | ev->ev_callback = cb; 220 | ev->ev_arg = arg; 221 | ev->ev_res = 0; 222 | ev->ev_flags = EVLIST_INIT; 223 | } 224 | 225 | int event_once (int fd, short events, void (*cb)(int, short, void *), void *arg, struct timeval *tv) 226 | { 227 | return event_base_once (ev_x_cur, fd, events, cb, arg, tv); 228 | } 229 | 230 | int event_add (struct event *ev, struct timeval *tv) 231 | { 232 | dLOOPev; 233 | 234 | if (ev->ev_events & EV_SIGNAL) 235 | { 236 | if (!ev_is_active (&ev->iosig.sig)) 237 | { 238 | ev_signal_set (&ev->iosig.sig, ev->ev_fd); 239 | ev_signal_start (EV_A_ &ev->iosig.sig); 240 | 241 | ev->ev_flags |= EVLIST_SIGNAL; 242 | } 243 | } 244 | else if (ev->ev_events & (EV_READ | EV_WRITE)) 245 | { 246 | if (!ev_is_active (&ev->iosig.io)) 247 | { 248 | ev_io_set (&ev->iosig.io, ev->ev_fd, ev->ev_events & (EV_READ | EV_WRITE)); 249 | ev_io_start (EV_A_ &ev->iosig.io); 250 | 251 | ev->ev_flags |= EVLIST_INSERTED; 252 | } 253 | } 254 | 255 | if (tv) 256 | { 257 | ev->to.repeat = ev_tv_get (tv); 258 | ev_timer_again (EV_A_ &ev->to); 259 | ev->ev_flags |= EVLIST_TIMEOUT; 260 | } 261 | else 262 | { 263 | ev_timer_stop (EV_A_ &ev->to); 264 | ev->ev_flags &= ~EVLIST_TIMEOUT; 265 | } 266 | 267 | ev->ev_flags |= EVLIST_ACTIVE; 268 | 269 | return 0; 270 | } 271 | 272 | int event_del (struct event *ev) 273 | { 274 | dLOOPev; 275 | 276 | if (ev->ev_events & EV_SIGNAL) 277 | ev_signal_stop (EV_A_ &ev->iosig.sig); 278 | else if (ev->ev_events & (EV_READ | EV_WRITE)) 279 | ev_io_stop (EV_A_ &ev->iosig.io); 280 | 281 | if (ev_is_active (&ev->to)) 282 | ev_timer_stop (EV_A_ &ev->to); 283 | 284 | ev->ev_flags = EVLIST_INIT; 285 | 286 | return 0; 287 | } 288 | 289 | void event_active (struct event *ev, int res, short ncalls) 290 | { 291 | dLOOPev; 292 | 293 | if (res & EV_TIMEOUT) 294 | ev_feed_event (EV_A_ &ev->to, res & EV_TIMEOUT); 295 | 296 | if (res & EV_SIGNAL) 297 | ev_feed_event (EV_A_ &ev->iosig.sig, res & EV_SIGNAL); 298 | 299 | if (res & (EV_READ | EV_WRITE)) 300 | ev_feed_event (EV_A_ &ev->iosig.io, res & (EV_READ | EV_WRITE)); 301 | } 302 | 303 | int event_pending (struct event *ev, short events, struct timeval *tv) 304 | { 305 | short revents = 0; 306 | dLOOPev; 307 | 308 | if (ev->ev_events & EV_SIGNAL) 309 | { 310 | /* sig */ 311 | if (ev_is_active (&ev->iosig.sig) || ev_is_pending (&ev->iosig.sig)) 312 | revents |= EV_SIGNAL; 313 | } 314 | else if (ev->ev_events & (EV_READ | EV_WRITE)) 315 | { 316 | /* io */ 317 | if (ev_is_active (&ev->iosig.io) || ev_is_pending (&ev->iosig.io)) 318 | revents |= ev->ev_events & (EV_READ | EV_WRITE); 319 | } 320 | 321 | if (ev->ev_events & EV_TIMEOUT || ev_is_active (&ev->to) || ev_is_pending (&ev->to)) 322 | { 323 | revents |= EV_TIMEOUT; 324 | 325 | if (tv) 326 | { 327 | ev_tstamp at = ev_now (EV_A); 328 | 329 | tv->tv_sec = (long)at; 330 | tv->tv_usec = (long)((at - (ev_tstamp)tv->tv_sec) * 1e6); 331 | } 332 | } 333 | 334 | return events & revents; 335 | } 336 | 337 | int event_priority_init (int npri) 338 | { 339 | return event_base_priority_init (ev_x_cur, npri); 340 | } 341 | 342 | int event_priority_set (struct event *ev, int pri) 343 | { 344 | ev->ev_pri = pri; 345 | 346 | return 0; 347 | } 348 | 349 | int event_base_set (struct event_base *base, struct event *ev) 350 | { 351 | ev->ev_base = base; 352 | 353 | return 0; 354 | } 355 | 356 | int event_base_loop (struct event_base *base, int flags) 357 | { 358 | dLOOPbase; 359 | 360 | return !ev_run (EV_A_ flags); 361 | } 362 | 363 | int event_base_dispatch (struct event_base *base) 364 | { 365 | return event_base_loop (base, 0); 366 | } 367 | 368 | static void 369 | ev_x_loopexit_cb (int revents, void *base) 370 | { 371 | dLOOPbase; 372 | 373 | ev_break (EV_A_ EVBREAK_ONE); 374 | } 375 | 376 | int event_base_loopexit (struct event_base *base, struct timeval *tv) 377 | { 378 | ev_tstamp after = ev_tv_get (tv); 379 | dLOOPbase; 380 | 381 | ev_once (EV_A_ -1, 0, after >= 0. ? after : 0., ev_x_loopexit_cb, (void *)base); 382 | 383 | return 0; 384 | } 385 | 386 | struct ev_x_once 387 | { 388 | int fd; 389 | void (*cb)(int, short, void *); 390 | void *arg; 391 | }; 392 | 393 | static void 394 | ev_x_once_cb (int revents, void *arg) 395 | { 396 | struct ev_x_once *once = (struct ev_x_once *)arg; 397 | 398 | once->cb (once->fd, (short)revents, once->arg); 399 | free (once); 400 | } 401 | 402 | int event_base_once (struct event_base *base, int fd, short events, void (*cb)(int, short, void *), void *arg, struct timeval *tv) 403 | { 404 | struct ev_x_once *once = (struct ev_x_once *)malloc (sizeof (struct ev_x_once)); 405 | dLOOPbase; 406 | 407 | if (!once) 408 | return -1; 409 | 410 | once->fd = fd; 411 | once->cb = cb; 412 | once->arg = arg; 413 | 414 | ev_once (EV_A_ fd, events & (EV_READ | EV_WRITE), ev_tv_get (tv), ev_x_once_cb, (void *)once); 415 | 416 | return 0; 417 | } 418 | 419 | int event_base_priority_init (struct event_base *base, int npri) 420 | { 421 | /*dLOOPbase;*/ 422 | 423 | return 0; 424 | } 425 | 426 | -------------------------------------------------------------------------------- /libcoro/coro.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2001-2009 Marc Alexander Lehmann 3 | * 4 | * Redistribution and use in source and binary forms, with or without modifica- 5 | * tion, are permitted provided that the following conditions are met: 6 | * 7 | * 1. Redistributions of source code must retain the above copyright notice, 8 | * this list of conditions and the following disclaimer. 9 | * 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 15 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- 16 | * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 17 | * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- 18 | * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 20 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 21 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- 22 | * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 23 | * OF THE POSSIBILITY OF SUCH DAMAGE. 24 | * 25 | * Alternatively, the contents of this file may be used under the terms of 26 | * the GNU General Public License ("GPL") version 2 or any later version, 27 | * in which case the provisions of the GPL are applicable instead of 28 | * the above. If you wish to allow the use of your version of this file 29 | * only under the terms of the GPL and not to allow others to use your 30 | * version of this file under the BSD license, indicate your decision 31 | * by deleting the provisions above and replace them with the notice 32 | * and other provisions required by the GPL. If you do not delete the 33 | * provisions above, a recipient may use your version of this file under 34 | * either the BSD or the GPL. 35 | * 36 | * This library is modelled strictly after Ralf S. Engelschalls article at 37 | * http://www.gnu.org/software/pth/rse-pmt.ps. So most of the credit must 38 | * go to Ralf S. Engelschall . 39 | * 40 | * This coroutine library is very much stripped down. You should either 41 | * build your own process abstraction using it or - better - just use GNU 42 | * Portable Threads, http://www.gnu.org/software/pth/. 43 | * 44 | */ 45 | 46 | /* 47 | * 2006-10-26 Include stddef.h on OS X to work around one of its bugs. 48 | * Reported by Michael_G_Schwern. 49 | * 2006-11-26 Use _setjmp instead of setjmp on GNU/Linux. 50 | * 2007-04-27 Set unwind frame info if gcc 3+ and ELF is detected. 51 | * Use _setjmp instead of setjmp on _XOPEN_SOURCE >= 600. 52 | * 2007-05-02 Add assembly versions for x86 and amd64 (to avoid reliance 53 | * on SIGUSR2 and sigaltstack in Crossfire). 54 | * 2008-01-21 Disable CFI usage on anything but GNU/Linux. 55 | * 2008-03-02 Switched to 2-clause BSD license with GPL exception. 56 | * 2008-04-04 New (but highly unrecommended) pthreads backend. 57 | * 2008-04-24 Reinstate CORO_LOSER (had wrong stack adjustments). 58 | * 2008-10-30 Support assembly method on x86 with and without frame pointer. 59 | * 2008-11-03 Use a global asm statement for CORO_ASM, idea by pippijn. 60 | * 2008-11-05 Hopefully fix misaligned stacks with CORO_ASM/SETJMP. 61 | * 2008-11-07 rbp wasn't saved in CORO_ASM on x86_64. 62 | * introduce coro_destroy, which is a nop except for pthreads. 63 | * speed up CORO_PTHREAD. Do no longer leak threads either. 64 | * coro_create now allows one to create source coro_contexts. 65 | * do not rely on makecontext passing a void * correctly. 66 | * try harder to get _setjmp/_longjmp. 67 | * major code cleanup/restructuring. 68 | * 2008-11-10 the .cfi hacks are no longer needed. 69 | * 2008-11-16 work around a freebsd pthread bug. 70 | * 2008-11-19 define coro_*jmp symbols for easier porting. 71 | * 2009-06-23 tentative win32-backend support for mingw32 (Yasuhiro Matsumoto). 72 | * 2010-12-03 tentative support for uclibc (which lacks all sorts of things). 73 | * 2011-05-30 set initial callee-saved-registers to zero with CORO_ASM. 74 | * use .cfi_undefined rip on linux-amd64 for better backtraces. 75 | * 2011-06-08 maybe properly implement weird windows amd64 calling conventions. 76 | * 2011-07-03 rely on __GCC_HAVE_DWARF2_CFI_ASM for cfi detection. 77 | */ 78 | 79 | #ifndef CORO_H 80 | #define CORO_H 81 | 82 | #if __cplusplus 83 | extern "C" { 84 | #endif 85 | 86 | #define CORO_VERSION 2 87 | 88 | /* 89 | * Changes since API version 1: 90 | * replaced bogus -DCORO_LOOSE with gramatically more correct -DCORO_LOSER 91 | */ 92 | 93 | /* 94 | * This library consists of only three files 95 | * coro.h, coro.c and LICENSE (and optionally README) 96 | * 97 | * It implements what is known as coroutines, in a hopefully 98 | * portable way. At the moment you have to define which kind 99 | * of implementation flavour you want: 100 | * 101 | * -DCORO_UCONTEXT 102 | * 103 | * This flavour uses SUSv2's get/set/swap/makecontext functions that 104 | * unfortunately only newer unices support. 105 | * 106 | * -DCORO_SJLJ 107 | * 108 | * This flavour uses SUSv2's setjmp/longjmp and sigaltstack functions to 109 | * do it's job. Coroutine creation is much slower than UCONTEXT, but 110 | * context switching is often a bit cheaper. It should work on almost 111 | * all unices. 112 | * 113 | * -DCORO_LINUX 114 | * 115 | * Old GNU/Linux systems (<= glibc-2.1) only work with this implementation 116 | * (it is very fast and therefore recommended over other methods, but 117 | * doesn't work with anything newer). 118 | * 119 | * -DCORO_LOSER 120 | * 121 | * Microsoft's highly proprietary platform doesn't support sigaltstack, and 122 | * this automatically selects a suitable workaround for this platform. 123 | * (untested) 124 | * 125 | * -DCORO_IRIX 126 | * 127 | * SGI's version of Microsoft's NT ;) 128 | * 129 | * -DCORO_ASM 130 | * 131 | * Handcoded assembly, known to work only on a few architectures/ABI: 132 | * GCC + x86/IA32 and amd64/x86_64 + GNU/Linux and a few BSDs. 133 | * 134 | * -DCORO_PTHREAD 135 | * 136 | * Use the pthread API. You have to provide and -lpthread. 137 | * This is likely the slowest backend, and it also does not support fork(), 138 | * so avoid it at all costs. 139 | * 140 | * If you define neither of these symbols, coro.h will try to autodetect 141 | * the model. This currently works for CORO_LOSER only. For the other 142 | * alternatives you should check (e.g. using autoconf) and define the 143 | * following symbols: HAVE_UCONTEXT_H / HAVE_SETJMP_H / HAVE_SIGALTSTACK. 144 | */ 145 | 146 | /* 147 | * This is the type for the initialization function of a new coroutine. 148 | */ 149 | typedef void (*coro_func)(void *); 150 | 151 | /* 152 | * A coroutine state is saved in the following structure. Treat it as an 153 | * opaque type. errno and sigmask might be saved, but don't rely on it, 154 | * implement your own switching primitive if you need that. 155 | */ 156 | typedef struct coro_context coro_context; 157 | 158 | /* 159 | * This function creates a new coroutine. Apart from a pointer to an 160 | * uninitialised coro_context, it expects a pointer to the entry function 161 | * and the single pointer value that is given to it as argument. 162 | * 163 | * Allocating/deallocating the stack is your own responsibility. 164 | * 165 | * As a special case, if coro, arg, sptr and ssize are all zero, 166 | * then an "empty" coro_context will be created that is suitable 167 | * as an initial source for coro_transfer. 168 | * 169 | * This function is not reentrant, but putting a mutex around it 170 | * will work. 171 | */ 172 | void coro_create (coro_context *ctx, /* an uninitialised coro_context */ 173 | coro_func coro, /* the coroutine code to be executed */ 174 | void *arg, /* a single pointer passed to the coro */ 175 | void *sptr, /* start of stack area */ 176 | long ssize); /* size of stack area */ 177 | 178 | /* 179 | * The following prototype defines the coroutine switching function. It is 180 | * usually implemented as a macro, so watch out. 181 | * 182 | * This function is thread-safe and reentrant. 183 | */ 184 | #if 0 185 | void coro_transfer (coro_context *prev, coro_context *next); 186 | #endif 187 | 188 | /* 189 | * The following prototype defines the coroutine destroy function. It is 190 | * usually implemented as a macro, so watch out. It also serves 191 | * no purpose unless you want to use the CORO_PTHREAD backend, 192 | * where it is used to clean up the thread. You are responsible 193 | * for freeing the stack and the context itself. 194 | * 195 | * This function is thread-safe and reentrant. 196 | */ 197 | #if 0 198 | void coro_destroy (coro_context *ctx); 199 | #endif 200 | 201 | /* 202 | * That was it. No other user-visible functions are implemented here. 203 | */ 204 | 205 | /*****************************************************************************/ 206 | 207 | #if !defined(CORO_LOSER) && !defined(CORO_UCONTEXT) \ 208 | && !defined(CORO_SJLJ) && !defined(CORO_LINUX) \ 209 | && !defined(CORO_IRIX) && !defined(CORO_ASM) \ 210 | && !defined(CORO_PTHREAD) 211 | # if defined(WINDOWS) || defined(_WIN32) 212 | # define CORO_LOSER 1 /* you don't win with windoze */ 213 | # elif defined(__linux) && (defined(__x86) || defined (__amd64)) 214 | # define CORO_ASM 1 215 | # elif defined(HAVE_UCONTEXT_H) 216 | # define CORO_UCONTEXT 1 217 | # elif defined(HAVE_SETJMP_H) && defined(HAVE_SIGALTSTACK) 218 | # define CORO_SJLJ 1 219 | # else 220 | error unknown or unsupported architecture 221 | # endif 222 | #endif 223 | 224 | /*****************************************************************************/ 225 | 226 | #if CORO_UCONTEXT 227 | 228 | # include 229 | 230 | struct coro_context { 231 | ucontext_t uc; 232 | }; 233 | 234 | # define coro_transfer(p,n) swapcontext (&((p)->uc), &((n)->uc)) 235 | # define coro_destroy(ctx) (void *)(ctx) 236 | 237 | #elif CORO_SJLJ || CORO_LOSER || CORO_LINUX || CORO_IRIX 238 | 239 | # if defined(CORO_LINUX) && !defined(_GNU_SOURCE) 240 | # define _GNU_SOURCE /* for linux libc */ 241 | # endif 242 | 243 | # if !CORO_LOSER 244 | # include 245 | # endif 246 | 247 | /* solaris is hopelessly borked, it expands _XOPEN_UNIX to nothing */ 248 | # if __sun 249 | # undef _XOPEN_UNIX 250 | # define _XOPEN_UNIX 1 251 | # endif 252 | 253 | # include 254 | 255 | # if _XOPEN_UNIX > 0 || defined (_setjmp) 256 | # define coro_jmp_buf jmp_buf 257 | # define coro_setjmp(env) _setjmp (env) 258 | # define coro_longjmp(env) _longjmp ((env), 1) 259 | # elif CORO_LOSER 260 | # define coro_jmp_buf jmp_buf 261 | # define coro_setjmp(env) setjmp (env) 262 | # define coro_longjmp(env) longjmp ((env), 1) 263 | # else 264 | # define coro_jmp_buf sigjmp_buf 265 | # define coro_setjmp(env) sigsetjmp (env, 0) 266 | # define coro_longjmp(env) siglongjmp ((env), 1) 267 | # endif 268 | 269 | struct coro_context { 270 | coro_jmp_buf env; 271 | }; 272 | 273 | # define coro_transfer(p,n) do { if (!coro_setjmp ((p)->env)) coro_longjmp ((n)->env); } while (0) 274 | # define coro_destroy(ctx) (void *)(ctx) 275 | 276 | #elif CORO_ASM 277 | 278 | struct coro_context { 279 | void **sp; /* must be at offset 0 */ 280 | }; 281 | 282 | void __attribute__ ((__noinline__, __regparm__(2))) 283 | coro_transfer (coro_context *prev, coro_context *next); 284 | 285 | # define coro_destroy(ctx) (void)(void *)(ctx) 286 | 287 | #elif CORO_PTHREAD 288 | 289 | # include 290 | 291 | extern pthread_mutex_t coro_mutex; 292 | 293 | struct coro_context { 294 | pthread_cond_t cv; 295 | pthread_t id; 296 | }; 297 | 298 | void coro_transfer (coro_context *prev, coro_context *next); 299 | void coro_destroy (coro_context *ctx); 300 | 301 | #endif 302 | 303 | #if __cplusplus 304 | } 305 | #endif 306 | 307 | #endif 308 | 309 | -------------------------------------------------------------------------------- /libcoro/coro.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2001-2011 Marc Alexander Lehmann 3 | * 4 | * Redistribution and use in source and binary forms, with or without modifica- 5 | * tion, are permitted provided that the following conditions are met: 6 | * 7 | * 1. Redistributions of source code must retain the above copyright notice, 8 | * this list of conditions and the following disclaimer. 9 | * 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 15 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- 16 | * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 17 | * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- 18 | * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 20 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 21 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- 22 | * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 23 | * OF THE POSSIBILITY OF SUCH DAMAGE. 24 | * 25 | * Alternatively, the contents of this file may be used under the terms of 26 | * the GNU General Public License ("GPL") version 2 or any later version, 27 | * in which case the provisions of the GPL are applicable instead of 28 | * the above. If you wish to allow the use of your version of this file 29 | * only under the terms of the GPL and not to allow others to use your 30 | * version of this file under the BSD license, indicate your decision 31 | * by deleting the provisions above and replace them with the notice 32 | * and other provisions required by the GPL. If you do not delete the 33 | * provisions above, a recipient may use your version of this file under 34 | * either the BSD or the GPL. 35 | * 36 | * This library is modelled strictly after Ralf S. Engelschalls article at 37 | * http://www.gnu.org/software/pth/rse-pmt.ps. So most of the credit must 38 | * go to Ralf S. Engelschall . 39 | */ 40 | 41 | #include "coro.h" 42 | 43 | #include 44 | 45 | /*****************************************************************************/ 46 | /* ucontext/setjmp/asm backends */ 47 | /*****************************************************************************/ 48 | #if CORO_UCONTEXT || CORO_SJLJ || CORO_LOSER || CORO_LINUX || CORO_IRIX || CORO_ASM 49 | 50 | # if CORO_UCONTEXT 51 | # include 52 | # endif 53 | 54 | # if !defined(STACK_ADJUST_PTR) 55 | # if __sgi 56 | /* IRIX is decidedly NON-unix */ 57 | # define STACK_ADJUST_PTR(sp,ss) ((char *)(sp) + (ss) - 8) 58 | # define STACK_ADJUST_SIZE(sp,ss) ((ss) - 8) 59 | # elif (__i386__ && CORO_LINUX) || (_M_IX86 && CORO_LOSER) 60 | # define STACK_ADJUST_PTR(sp,ss) ((char *)(sp) + (ss)) 61 | # define STACK_ADJUST_SIZE(sp,ss) (ss) 62 | # elif (__amd64__ && CORO_LINUX) || ((_M_AMD64 || _M_IA64) && CORO_LOSER) 63 | # define STACK_ADJUST_PTR(sp,ss) ((char *)(sp) + (ss) - 8) 64 | # define STACK_ADJUST_SIZE(sp,ss) (ss) 65 | # else 66 | # define STACK_ADJUST_PTR(sp,ss) (sp) 67 | # define STACK_ADJUST_SIZE(sp,ss) (ss) 68 | # endif 69 | # endif 70 | 71 | # include 72 | 73 | # if CORO_SJLJ 74 | # include 75 | # include 76 | # include 77 | # endif 78 | 79 | static coro_func coro_init_func; 80 | static void *coro_init_arg; 81 | static coro_context *new_coro, *create_coro; 82 | 83 | static void 84 | coro_init (void) 85 | { 86 | volatile coro_func func = coro_init_func; 87 | volatile void *arg = coro_init_arg; 88 | 89 | coro_transfer (new_coro, create_coro); 90 | 91 | #if __GCC_HAVE_DWARF2_CFI_ASM && __amd64 92 | asm (".cfi_undefined rip"); 93 | #endif 94 | 95 | func ((void *)arg); 96 | 97 | /* the new coro returned. bad. just abort() for now */ 98 | abort (); 99 | } 100 | 101 | # if CORO_SJLJ 102 | 103 | static volatile int trampoline_done; 104 | 105 | /* trampoline signal handler */ 106 | static void 107 | trampoline (int sig) 108 | { 109 | if (coro_setjmp (new_coro->env)) 110 | coro_init (); /* start it */ 111 | else 112 | trampoline_done = 1; 113 | } 114 | 115 | # endif 116 | 117 | # if CORO_ASM 118 | 119 | #if _WIN32 120 | #define CORO_WIN_TIB 1 121 | #endif 122 | 123 | asm ( 124 | "\t.text\n" 125 | "\t.globl coro_transfer\n" 126 | "coro_transfer:\n" 127 | /* windows, of course, gives a shit on the amd64 ABI and uses different registers */ 128 | /* http://blogs.msdn.com/freik/archive/2005/03/17/398200.aspx */ 129 | #if __amd64 130 | #ifdef WIN32 131 | /* TODO: xmm6..15 also would need to be saved. sigh. */ 132 | #define NUM_SAVED 8 133 | "\tpushq %rsi\n" 134 | "\tpushq %rdi\n" 135 | "\tpushq %rbp\n" 136 | "\tpushq %rbx\n" 137 | "\tpushq %r12\n" 138 | "\tpushq %r13\n" 139 | "\tpushq %r14\n" 140 | "\tpushq %r15\n" 141 | #if CORO_WIN_TIB 142 | "\tpushq %fs:0x0\n" 143 | "\tpushq %fs:0x8\n" 144 | "\tpushq %fs:0xc\n" 145 | #endif 146 | "\tmovq %rsp, (%rcx)\n" 147 | "\tmovq (%rdx), %rsp\n" 148 | #if CORO_WIN_TIB 149 | "\tpopq %fs:0xc\n" 150 | "\tpopq %fs:0x8\n" 151 | "\tpopq %fs:0x0\n" 152 | #endif 153 | "\tpopq %r15\n" 154 | "\tpopq %r14\n" 155 | "\tpopq %r13\n" 156 | "\tpopq %r12\n" 157 | "\tpopq %rbx\n" 158 | "\tpopq %rbp\n" 159 | "\tpopq %rdi\n" 160 | "\tpopq %rsi\n" 161 | #else 162 | #define NUM_SAVED 6 163 | "\tpushq %rbp\n" 164 | "\tpushq %rbx\n" 165 | "\tpushq %r12\n" 166 | "\tpushq %r13\n" 167 | "\tpushq %r14\n" 168 | "\tpushq %r15\n" 169 | "\tmovq %rsp, (%rdi)\n" 170 | "\tmovq (%rsi), %rsp\n" 171 | "\tpopq %r15\n" 172 | "\tpopq %r14\n" 173 | "\tpopq %r13\n" 174 | "\tpopq %r12\n" 175 | "\tpopq %rbx\n" 176 | "\tpopq %rbp\n" 177 | #endif 178 | #elif __i386 179 | #define NUM_SAVED 4 180 | "\tpushl %ebp\n" 181 | "\tpushl %ebx\n" 182 | "\tpushl %esi\n" 183 | "\tpushl %edi\n" 184 | #if CORO_WIN_TIB 185 | "\tpushl %fs:0\n" 186 | "\tpushl %fs:4\n" 187 | "\tpushl %fs:8\n" 188 | #endif 189 | "\tmovl %esp, (%eax)\n" 190 | "\tmovl (%edx), %esp\n" 191 | #if CORO_WIN_TIB 192 | "\tpopl %fs:8\n" 193 | "\tpopl %fs:4\n" 194 | "\tpopl %fs:0\n" 195 | #endif 196 | "\tpopl %edi\n" 197 | "\tpopl %esi\n" 198 | "\tpopl %ebx\n" 199 | "\tpopl %ebp\n" 200 | #else 201 | #error unsupported architecture 202 | #endif 203 | "\tret\n" 204 | ); 205 | 206 | # endif 207 | 208 | void 209 | coro_create (coro_context *ctx, coro_func coro, void *arg, void *sptr, long ssize) 210 | { 211 | coro_context nctx; 212 | # if CORO_SJLJ 213 | stack_t ostk, nstk; 214 | struct sigaction osa, nsa; 215 | sigset_t nsig, osig; 216 | # endif 217 | 218 | if (!coro) 219 | return; 220 | 221 | coro_init_func = coro; 222 | coro_init_arg = arg; 223 | 224 | new_coro = ctx; 225 | create_coro = &nctx; 226 | 227 | # if CORO_SJLJ 228 | /* we use SIGUSR2. first block it, then fiddle with it. */ 229 | 230 | sigemptyset (&nsig); 231 | sigaddset (&nsig, SIGUSR2); 232 | sigprocmask (SIG_BLOCK, &nsig, &osig); 233 | 234 | nsa.sa_handler = trampoline; 235 | sigemptyset (&nsa.sa_mask); 236 | nsa.sa_flags = SA_ONSTACK; 237 | 238 | if (sigaction (SIGUSR2, &nsa, &osa)) 239 | { 240 | perror ("sigaction"); 241 | abort (); 242 | } 243 | 244 | /* set the new stack */ 245 | nstk.ss_sp = STACK_ADJUST_PTR (sptr, ssize); /* yes, some platforms (IRIX) get this wrong. */ 246 | nstk.ss_size = STACK_ADJUST_SIZE (sptr, ssize); 247 | nstk.ss_flags = 0; 248 | 249 | if (sigaltstack (&nstk, &ostk) < 0) 250 | { 251 | perror ("sigaltstack"); 252 | abort (); 253 | } 254 | 255 | trampoline_done = 0; 256 | kill (getpid (), SIGUSR2); 257 | sigfillset (&nsig); sigdelset (&nsig, SIGUSR2); 258 | 259 | while (!trampoline_done) 260 | sigsuspend (&nsig); 261 | 262 | sigaltstack (0, &nstk); 263 | nstk.ss_flags = SS_DISABLE; 264 | if (sigaltstack (&nstk, 0) < 0) 265 | perror ("sigaltstack"); 266 | 267 | sigaltstack (0, &nstk); 268 | if (~nstk.ss_flags & SS_DISABLE) 269 | abort (); 270 | 271 | if (~ostk.ss_flags & SS_DISABLE) 272 | sigaltstack (&ostk, 0); 273 | 274 | sigaction (SIGUSR2, &osa, 0); 275 | sigprocmask (SIG_SETMASK, &osig, 0); 276 | 277 | # elif CORO_LOSER 278 | 279 | coro_setjmp (ctx->env); 280 | #if __CYGWIN__ && __i386 281 | ctx->env[8] = (long) coro_init; 282 | ctx->env[7] = (long) ((char *)sptr + ssize) - sizeof (long); 283 | #elif __CYGWIN__ && __x86_64 284 | ctx->env[7] = (long) coro_init; 285 | ctx->env[6] = (long) ((char *)sptr + ssize) - sizeof (long); 286 | #elif defined(__MINGW32__) 287 | ctx->env[5] = (long) coro_init; 288 | ctx->env[4] = (long) ((char *)sptr + ssize) - sizeof (long); 289 | #elif defined(_M_IX86) 290 | ((_JUMP_BUFFER *)&ctx->env)->Eip = (long) coro_init; 291 | ((_JUMP_BUFFER *)&ctx->env)->Esp = (long) STACK_ADJUST_PTR (sptr, ssize) - sizeof (long); 292 | #elif defined(_M_AMD64) 293 | ((_JUMP_BUFFER *)&ctx->env)->Rip = (__int64) coro_init; 294 | ((_JUMP_BUFFER *)&ctx->env)->Rsp = (__int64) STACK_ADJUST_PTR (sptr, ssize) - sizeof (__int64); 295 | #elif defined(_M_IA64) 296 | ((_JUMP_BUFFER *)&ctx->env)->StIIP = (__int64) coro_init; 297 | ((_JUMP_BUFFER *)&ctx->env)->IntSp = (__int64) STACK_ADJUST_PTR (sptr, ssize) - sizeof (__int64); 298 | #else 299 | #error "microsoft libc or architecture not supported" 300 | #endif 301 | 302 | # elif CORO_LINUX 303 | 304 | coro_setjmp (ctx->env); 305 | #if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 0 && defined (JB_PC) && defined (JB_SP) 306 | ctx->env[0].__jmpbuf[JB_PC] = (long) coro_init; 307 | ctx->env[0].__jmpbuf[JB_SP] = (long) STACK_ADJUST_PTR (sptr, ssize) - sizeof (long); 308 | #elif __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 0 && defined (__mc68000__) 309 | ctx->env[0].__jmpbuf[0].__aregs[0] = (long int)coro_init; 310 | ctx->env[0].__jmpbuf[0].__sp = (int *) ((char *)sptr + ssize) - sizeof (long); 311 | #elif defined (__GNU_LIBRARY__) && defined (__i386__) 312 | ctx->env[0].__jmpbuf[0].__pc = (char *) coro_init; 313 | ctx->env[0].__jmpbuf[0].__sp = (void *) ((char *)sptr + ssize) - sizeof (long); 314 | #elif defined (__GNU_LIBRARY__) && defined (__amd64__) 315 | ctx->env[0].__jmpbuf[JB_PC] = (long) coro_init; 316 | ctx->env[0].__jmpbuf[0].__sp = (void *) ((char *)sptr + ssize) - sizeof (long); 317 | #else 318 | #error "linux libc or architecture not supported" 319 | #endif 320 | 321 | # elif CORO_IRIX 322 | 323 | coro_setjmp (ctx->env, 0); 324 | ctx->env[JB_PC] = (__uint64_t)coro_init; 325 | ctx->env[JB_SP] = (__uint64_t)STACK_ADJUST_PTR (sptr, ssize) - sizeof (long); 326 | 327 | # elif CORO_ASM 328 | 329 | ctx->sp = (void **)(ssize + (char *)sptr); 330 | *--ctx->sp = (void *)abort; /* needed for alignment only */ 331 | *--ctx->sp = (void *)coro_init; 332 | 333 | #if CORO_WIN_TIB 334 | *--ctx->sp = 0; /* ExceptionList */ 335 | *--ctx->sp = (char *)sptr + ssize; /* StackBase */ 336 | *--ctx->sp = sptr; /* StackLimit */ 337 | #endif 338 | 339 | ctx->sp -= NUM_SAVED; 340 | memset (ctx->sp, 0, sizeof (*ctx->sp) * NUM_SAVED); 341 | 342 | # elif CORO_UCONTEXT 343 | 344 | getcontext (&(ctx->uc)); 345 | 346 | ctx->uc.uc_link = 0; 347 | ctx->uc.uc_stack.ss_sp = sptr; 348 | ctx->uc.uc_stack.ss_size = (size_t)ssize; 349 | ctx->uc.uc_stack.ss_flags = 0; 350 | 351 | makecontext (&(ctx->uc), (void (*)())coro_init, 0); 352 | 353 | # endif 354 | 355 | coro_transfer (create_coro, new_coro); 356 | } 357 | 358 | /*****************************************************************************/ 359 | /* pthread backend */ 360 | /*****************************************************************************/ 361 | #elif CORO_PTHREAD 362 | 363 | /* this mutex will be locked by the running coroutine */ 364 | pthread_mutex_t coro_mutex = PTHREAD_MUTEX_INITIALIZER; 365 | 366 | struct coro_init_args 367 | { 368 | coro_func func; 369 | void *arg; 370 | coro_context *self, *main; 371 | }; 372 | 373 | static pthread_t null_tid; 374 | 375 | /* I'd so love to cast pthread_mutex_unlock to void (*)(void *)... */ 376 | static void 377 | mutex_unlock_wrapper (void *arg) 378 | { 379 | pthread_mutex_unlock ((pthread_mutex_t *)arg); 380 | } 381 | 382 | static void * 383 | coro_init (void *args_) 384 | { 385 | struct coro_init_args *args = (struct coro_init_args *)args_; 386 | coro_func func = args->func; 387 | void *arg = args->arg; 388 | 389 | pthread_mutex_lock (&coro_mutex); 390 | 391 | /* we try to be good citizens and use deferred cancellation and cleanup handlers */ 392 | pthread_cleanup_push (mutex_unlock_wrapper, &coro_mutex); 393 | coro_transfer (args->self, args->main); 394 | func (arg); 395 | pthread_cleanup_pop (1); 396 | 397 | return 0; 398 | } 399 | 400 | void 401 | coro_transfer (coro_context *prev, coro_context *next) 402 | { 403 | pthread_cond_signal (&next->cv); 404 | pthread_cond_wait (&prev->cv, &coro_mutex); 405 | #if __FreeBSD__ /* freebsd is of course broken and needs manual testcancel calls... yay... */ 406 | pthread_testcancel (); 407 | #endif 408 | } 409 | 410 | void 411 | coro_create (coro_context *ctx, coro_func coro, void *arg, void *sptr, long ssize) 412 | { 413 | static coro_context nctx; 414 | static int once; 415 | 416 | if (!once) 417 | { 418 | once = 1; 419 | 420 | pthread_mutex_lock (&coro_mutex); 421 | pthread_cond_init (&nctx.cv, 0); 422 | null_tid = pthread_self (); 423 | } 424 | 425 | pthread_cond_init (&ctx->cv, 0); 426 | 427 | if (coro) 428 | { 429 | pthread_attr_t attr; 430 | struct coro_init_args args; 431 | 432 | args.func = coro; 433 | args.arg = arg; 434 | args.self = ctx; 435 | args.main = &nctx; 436 | 437 | pthread_attr_init (&attr); 438 | #if __UCLIBC__ 439 | /* exists, but is borked */ 440 | /*pthread_attr_setstacksize (&attr, (size_t)ssize);*/ 441 | #else 442 | pthread_attr_setstack (&attr, sptr, (size_t)ssize); 443 | #endif 444 | pthread_attr_setscope (&attr, PTHREAD_SCOPE_PROCESS); 445 | pthread_create (&ctx->id, &attr, coro_init, &args); 446 | 447 | coro_transfer (args.main, args.self); 448 | } 449 | else 450 | ctx->id = null_tid; 451 | } 452 | 453 | void 454 | coro_destroy (coro_context *ctx) 455 | { 456 | if (!pthread_equal (ctx->id, null_tid)) 457 | { 458 | pthread_cancel (ctx->id); 459 | pthread_mutex_unlock (&coro_mutex); 460 | pthread_join (ctx->id, 0); 461 | pthread_mutex_lock (&coro_mutex); 462 | } 463 | 464 | pthread_cond_destroy (&ctx->cv); 465 | } 466 | 467 | #else 468 | # error unsupported backend 469 | #endif 470 | 471 | -------------------------------------------------------------------------------- /fiber_stubs.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010-2016 Mail.RU 3 | * Copyright (C) 2010-2018 Yuriy Vostrikov 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | // #include 34 | #include 35 | 36 | #include "queue.h" 37 | 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | 45 | #include "fiber.h" 46 | #include "fiber_ev.h" 47 | #include "libcoro/coro.h" 48 | 49 | #if HAVE_VALGRIND_VALGRIND_H && !defined(NVALGRIND) 50 | # include 51 | # include 52 | #else 53 | # define VALGRIND_MAKE_MEM_DEFINED(_qzz_addr, _qzz_len) (void)0 54 | # define VALGRIND_MAKE_MEM_UNDEFINED(_qzz_addr, _qzz_len) (void)0 55 | # define VALGRIND_MALLOCLIKE_BLOCK(addr, sizeB, rzB, is_zeroed) (void)0 56 | # define VALGRIND_FREELIKE_BLOCK(addr, rzB) (void)0 57 | # define VALGRIND_MAKE_MEM_NOACCESS(_qzz_addr,_qzz_len) (void)0 58 | # define VALGRIND_STACK_REGISTER(_qzz_addr,_qzz_len) (void)0 59 | #endif 60 | 61 | #define MH_SOURCE 1 62 | #define mh_name _long 63 | #define mh_key_t long 64 | #define mh_val_t void * 65 | #include "mhash.h" 66 | 67 | static struct coro * 68 | coro_alloc(struct coro *coro, void (*f) (void *), void *data) 69 | { 70 | const int page = sysconf(_SC_PAGESIZE); 71 | 72 | assert(coro != NULL); 73 | memset(coro, 0, sizeof(*coro)); 74 | 75 | coro->mmap_size = page * 128; 76 | coro->mmap = mmap(NULL, coro->mmap_size, PROT_READ | PROT_WRITE | PROT_EXEC, 77 | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 78 | 79 | if (coro->mmap == MAP_FAILED) 80 | goto fail; 81 | 82 | /* 16 pages of no access guard zone */ 83 | if (mprotect(coro->mmap, page * 16, PROT_NONE) < 0) 84 | goto fail; 85 | 86 | (void)VALGRIND_MAKE_MEM_NOACCESS(coro->mmap, page * 16); 87 | 88 | const int red_zone_size = sizeof(void *) * 4; 89 | coro->stack = coro->mmap + 16 * page; 90 | coro->stack_size = coro->mmap_size - 16 * page - red_zone_size; 91 | void **red_zone = coro->stack + coro->stack_size; 92 | red_zone[0] = red_zone[1] = NULL; 93 | red_zone[2] = red_zone[3] = (void *)(uintptr_t)0xDEADDEADDEADDEADULL; 94 | 95 | (void)VALGRIND_STACK_REGISTER(coro->stack, coro->stack + coro->stack_size); 96 | coro_create(&coro->ctx, f, data, coro->stack, coro->stack_size); 97 | return coro; 98 | 99 | int saved_errno; 100 | fail: 101 | saved_errno = errno; 102 | coro_destroy(coro); 103 | errno = saved_errno; 104 | return NULL; 105 | } 106 | 107 | 108 | SLIST_HEAD(, fiber) fibers, zombie_fibers; 109 | 110 | 111 | struct fiber* sched; 112 | coro_context *sched_ctx = NULL; 113 | struct fiber *fiber = NULL; 114 | static unsigned long last_used_id = 100; /* ids below 99 are reserved */ 115 | 116 | 117 | static ev_prepare wake_prep; 118 | static ev_async wake_async; 119 | 120 | static struct mh_long_t *fibers_registry; 121 | 122 | static TAILQ_HEAD(, fiber) wake_list; 123 | 124 | #if defined(FIBER_TRACE) || defined(FIBER_EV_DEBUG) 125 | void 126 | fiber_ev_cb(void *arg) 127 | { 128 | fprintf(stderr, "%s: =<< arg:%p\n", __func__, arg); 129 | } 130 | #endif 131 | 132 | static void 133 | fiber_async_send() 134 | { 135 | ev_async_send(&wake_async); 136 | } 137 | 138 | static void 139 | fiber_async(ev_async* ev __attribute__((unused)), 140 | int events __attribute__((unused))) 141 | { 142 | } 143 | 144 | void 145 | #ifdef FIBER_TRACE 146 | fiber_resume(struct fiber *callee, void *w) 147 | #else 148 | resume(struct fiber *callee, void *w) 149 | #endif 150 | { 151 | #ifndef FIBER_NDEBUG 152 | assert(callee != sched && callee->caller == NULL); 153 | #endif 154 | struct fiber *caller = fiber; 155 | callee->caller = caller; 156 | fiber = callee; 157 | callee->coro.w = w; 158 | coro_transfer(&caller->coro.ctx, &callee->coro.ctx); 159 | } 160 | 161 | void * 162 | #ifdef FIBER_TRACE 163 | fiber_yield(void) 164 | #else 165 | yield(void) 166 | #endif 167 | { 168 | struct fiber *callee = fiber; 169 | #ifndef FIBER_NDEBUG 170 | assert(callee->id != 1); 171 | assert(callee->caller != NULL); 172 | #endif 173 | fiber = callee->caller; 174 | callee->caller = NULL; 175 | coro_transfer(&callee->coro.ctx, &fiber->coro.ctx); 176 | return fiber->coro.w; 177 | } 178 | 179 | int 180 | fiber_wake(struct fiber *f, void *arg) 181 | { 182 | assert(f != sched); 183 | /* tqe_prev points to prev elem or tailq head => not null if member */ 184 | if (f->wake_link.tqe_prev) 185 | return 0; 186 | #ifdef FIBER_TRACE 187 | fprintf(stderr, "%s: %lli/%s arg:%p\n", __func__, f->fid, f->name, arg); 188 | #endif 189 | f->wake = arg; 190 | TAILQ_INSERT_TAIL(&wake_list, f, wake_link); 191 | fiber_async_send(); 192 | return 1; 193 | } 194 | 195 | int 196 | fiber_cancel_wake(struct fiber *f) 197 | { 198 | assert(f != sched); 199 | /* see fiber_wake() comment */ 200 | if (f->wake_link.tqe_prev == NULL) 201 | return 0; 202 | TAILQ_REMOVE(&wake_list, f, wake_link); 203 | f->wake_link.tqe_prev = NULL; 204 | return 1; 205 | } 206 | 207 | void 208 | fiber_sleep(ev_tstamp delay) 209 | { 210 | assert(fiber != sched); 211 | ev_timer *s, w = { .coro = 1 }; 212 | ev_timer_init(&w, (void *)fiber, delay, 0.); 213 | ev_timer_start(&w); 214 | s = yield(); 215 | assert(s == &w); 216 | (void)s; 217 | ev_timer_stop(&w); 218 | } 219 | 220 | 221 | struct fiber * 222 | fid2fiber(int fid) 223 | { 224 | unsigned k = mh_long_get(fibers_registry, fid); 225 | if (k == mh_end(fibers_registry)) 226 | return NULL; 227 | return mh_long_value(fibers_registry, k); 228 | } 229 | 230 | static void 231 | register_id(struct fiber *fiber) 232 | { 233 | mh_long_put(fibers_registry, fiber->id, fiber, NULL); 234 | } 235 | 236 | static void 237 | unregister_id(struct fiber *fiber) 238 | { 239 | mh_long_remove(fibers_registry, fiber->id, NULL); 240 | } 241 | 242 | 243 | static void 244 | fiber_zombificate(struct fiber *f) 245 | { 246 | strcpy(f->name, "zombie"); 247 | unregister_id(f); 248 | f->id = 0; 249 | // TODO: trash fiber->last_retaddr and friends 250 | // TODO: madvise() 251 | SLIST_INSERT_HEAD(&zombie_fibers, f, zombie_link); 252 | } 253 | 254 | static value 255 | fiber_trampoline(value cb, value arg) 256 | { 257 | CAMLparam2(cb, arg); 258 | CAMLlocal1(ret); 259 | caml_leave_blocking_section(); 260 | ret = caml_callback(cb, arg); 261 | caml_enter_blocking_section(); 262 | CAMLreturn(ret); 263 | } 264 | 265 | static void 266 | fiber_loop(void *data __attribute__((unused))) 267 | { 268 | while (42) { 269 | assert(fiber != NULL && fiber->id != 0); 270 | value cb = fiber->cb, 271 | arg = fiber->arg; 272 | fiber->cb = fiber->arg = 0; 273 | fiber_trampoline(cb, arg); 274 | fiber_zombificate(fiber); 275 | yield(); /* give control back to scheduler */ 276 | } 277 | } 278 | 279 | static struct fiber * 280 | fiber_create(value cb, value arg) 281 | { 282 | struct fiber *new = NULL; 283 | 284 | if (!SLIST_EMPTY(&zombie_fibers)) { 285 | new = SLIST_FIRST(&zombie_fibers); 286 | SLIST_REMOVE_HEAD(&zombie_fibers, zombie_link); 287 | } else { 288 | new = calloc(1, sizeof(struct fiber)); 289 | if (coro_alloc(&new->coro, fiber_loop, NULL) == NULL) { 290 | perror("fiber_create"); 291 | exit(1); 292 | } 293 | 294 | SLIST_INSERT_HEAD(&fibers, new, link); 295 | } 296 | 297 | new->id = last_used_id++; /* we believe that 2**63 won't overflow */ 298 | register_id(new); 299 | 300 | new->cb = cb; 301 | new->arg = arg; 302 | memset(new->name, 0, sizeof(new->name)); 303 | return new; 304 | } 305 | 306 | static void 307 | fiber_wakeup_pending(void) 308 | { 309 | assert(fiber == sched); 310 | struct fiber *f; 311 | 312 | for(int i=10; i && !TAILQ_EMPTY(&wake_list); i--) { 313 | TAILQ_INSERT_TAIL(&wake_list, sched, wake_link); 314 | while (1) { 315 | f = TAILQ_FIRST(&wake_list); 316 | TAILQ_REMOVE(&wake_list, f, wake_link); 317 | if (f == sched) break; 318 | f->wake_link.tqe_prev = NULL; 319 | #ifdef FIBER_TRACE 320 | fprintf(stderr, "%s: %lli/%s arg:%p\n", __func__, 321 | f->fid, f->name, f->wake); 322 | #endif 323 | resume(f, f->wake); 324 | } 325 | } 326 | 327 | if (!TAILQ_EMPTY(&wake_list)) 328 | fiber_async_send(); 329 | } 330 | 331 | 332 | typedef void (*scanning_action) (value, value *); 333 | 334 | extern char * caml_top_of_stack; 335 | extern char * caml_bottom_of_stack; 336 | extern uintnat caml_last_return_address; 337 | extern value * caml_gc_regs; 338 | extern char * caml_exception_pointer; 339 | extern int caml_backtrace_pos; 340 | extern void * caml_backtrace_buffer; 341 | extern value caml_backtrace_last_exn; 342 | 343 | extern void (*caml_scan_roots_hook) (scanning_action); 344 | extern void (*caml_enter_blocking_section_hook)(void); 345 | extern void (*caml_leave_blocking_section_hook)(void); 346 | extern int (*caml_try_leave_blocking_section_hook)(void); 347 | extern uintnat (*caml_stack_usage_hook)(void); 348 | 349 | extern void caml_do_local_roots(scanning_action f, char * bottom_of_stack, 350 | uintnat last_retaddr, value * gc_regs, 351 | struct caml__roots_block * local_roots); 352 | 353 | static void (*prev_scan_roots_hook)(scanning_action); 354 | static void (*prev_enter_blocking_section_hook)(void); 355 | static void (*prev_leave_blocking_section_hook)(void); 356 | static int (*prev_try_leave_blocking_section_hook)(void); 357 | static uintnat (*prev_stack_usage_hook)(void); 358 | 359 | static void fiber_scan_roots(struct fiber *f, scanning_action action) 360 | { 361 | if (f->cb) { 362 | /* fiber is not fully initialized yet */ 363 | assert(f->arg); 364 | (*action)(f->cb, &f->cb); 365 | (*action)(f->arg, &f->arg); 366 | } else { 367 | (*action)(f->backtrace_last_exn, &f->backtrace_last_exn); 368 | 369 | /* Don't rescan the stack of the current fiber, it was done already */ 370 | if (f == fiber) 371 | return; 372 | 373 | assert(f->last_retaddr > 0xffff); 374 | caml_do_local_roots(action, 375 | f->bottom_of_stack, f->last_retaddr, 376 | f->gc_regs, f->local_roots); 377 | } 378 | } 379 | 380 | static void fiber_all_scan_roots(scanning_action action) 381 | { 382 | struct fiber *f; 383 | fiber_scan_roots(sched, action); 384 | SLIST_FOREACH(f, &fibers, link) { 385 | if (f->id == 0) /* skip zombie */ 386 | continue; 387 | fiber_scan_roots(f, action); 388 | } 389 | 390 | if (prev_scan_roots_hook != NULL) 391 | (*prev_scan_roots_hook)(action); 392 | } 393 | 394 | static void 395 | fiber_enter_blocking_section(void) 396 | { 397 | fiber->top_of_stack = caml_top_of_stack; 398 | fiber->bottom_of_stack = caml_bottom_of_stack; 399 | fiber->last_retaddr = caml_last_return_address; 400 | fiber->gc_regs = caml_gc_regs; 401 | fiber->exception_pointer = caml_exception_pointer; 402 | fiber->local_roots = caml_local_roots; 403 | 404 | fiber->backtrace_pos = caml_backtrace_pos; 405 | fiber->backtrace_buffer = caml_backtrace_buffer; 406 | fiber->backtrace_last_exn = caml_backtrace_last_exn; 407 | 408 | caml_local_roots = (void *)0xdead; 409 | caml_last_return_address = 0xbeef; 410 | } 411 | 412 | static void 413 | fiber_leave_blocking_section(void) 414 | { 415 | caml_top_of_stack = fiber->top_of_stack; 416 | caml_bottom_of_stack= fiber->bottom_of_stack; 417 | caml_last_return_address = fiber->last_retaddr; 418 | caml_gc_regs = fiber->gc_regs; 419 | caml_exception_pointer = fiber->exception_pointer; 420 | caml_local_roots = fiber->local_roots; 421 | 422 | caml_backtrace_pos = fiber->backtrace_pos; 423 | caml_backtrace_buffer = fiber->backtrace_buffer; 424 | caml_backtrace_last_exn = fiber->backtrace_last_exn; 425 | 426 | fiber->local_roots = (void *)0xdead; 427 | fiber->last_retaddr = 0xbeef; 428 | } 429 | 430 | static int 431 | fiber_try_leave_blocking_section(void) 432 | { 433 | return 1; 434 | } 435 | 436 | static uintnat 437 | fiber_stack_usage(void) 438 | { 439 | uintnat sz = 0; 440 | struct fiber *f; 441 | 442 | SLIST_FOREACH(f, &fibers, link) { 443 | if (f->id == 0) /* skip zombie */ 444 | continue; 445 | /* Don't add stack for current thread, this is done elsewhere */ 446 | if (f == fiber) 447 | continue; 448 | sz += (value *) f->top_of_stack - (value *) f->bottom_of_stack; 449 | } 450 | if (prev_stack_usage_hook != NULL) 451 | sz += prev_stack_usage_hook(); 452 | return sz; 453 | } 454 | 455 | static struct fiber * 456 | Fiber_val(value fib) 457 | { 458 | int fid = Int_val(Field(fib, 0)); 459 | return fid2fiber(fid); 460 | } 461 | 462 | value 463 | stub_fiber_sleep(value tm) 464 | { 465 | CAMLparam1(tm); 466 | caml_enter_blocking_section(); 467 | fiber_sleep(Double_val(tm)); 468 | caml_leave_blocking_section(); 469 | CAMLreturn(Val_unit); 470 | } 471 | 472 | value 473 | stub_fiber_create(value cb, value arg) 474 | { 475 | CAMLparam2(cb, arg); 476 | CAMLlocal1(fib); 477 | caml_enter_blocking_section(); 478 | struct fiber *f = fiber_create(cb, arg); 479 | caml_leave_blocking_section(); 480 | CAMLreturn(Val_int(f->id)); 481 | } 482 | 483 | value 484 | stub_fiber_run(value unit) 485 | { 486 | caml_enter_blocking_section(); 487 | ev_run(0); 488 | caml_leave_blocking_section(); 489 | return Val_unit; 490 | } 491 | 492 | value 493 | stub_yield(value init) 494 | { 495 | if (fiber->id == 1) 496 | caml_invalid_argument("Fiber.yield"); 497 | caml_enter_blocking_section(); 498 | yield(); 499 | caml_leave_blocking_section(); 500 | return Val_unit; 501 | } 502 | 503 | value 504 | stub_unsafe_yield(value unit) 505 | { 506 | CAMLparam1(unit); 507 | CAMLlocal1(result); 508 | if (fiber->id == 1) 509 | caml_invalid_argument("Fiber.unsafe_yield"); 510 | caml_enter_blocking_section(); 511 | result = (intptr_t)yield(); 512 | caml_leave_blocking_section(); 513 | CAMLreturn(result); 514 | } 515 | 516 | value 517 | stub_resume(value fib) 518 | { 519 | struct fiber *f = Fiber_val(fib); 520 | if (f == NULL || f->caller != NULL) 521 | caml_invalid_argument("Fiber.resume"); 522 | caml_enter_blocking_section(); 523 | resume(f, NULL); 524 | caml_leave_blocking_section(); 525 | return Val_unit; 526 | } 527 | 528 | value 529 | stub_unsafe_resume(value fib, value arg) 530 | { 531 | CAMLparam2(fib, arg); 532 | struct fiber *f = Fiber_val(fib); 533 | if (f == NULL || f->caller != NULL) 534 | caml_invalid_argument("Fiber.unsafe_resume"); 535 | caml_enter_blocking_section(); 536 | resume(f, (void *)arg); 537 | caml_leave_blocking_section(); 538 | CAMLreturn(Val_unit); 539 | } 540 | 541 | value 542 | stub_wake(value fid) 543 | { 544 | struct fiber *f = fid2fiber(Int_val(fid)); 545 | if (f == NULL) 546 | caml_invalid_argument("Fiber.wake"); 547 | fiber_wake(f, NULL); 548 | return Val_unit; 549 | } 550 | 551 | value 552 | stub_cancel_wake(value fid) 553 | { 554 | struct fiber *f = fid2fiber(Int_val(fid)); 555 | if (f == NULL) 556 | caml_invalid_argument("Fiber.cancel_wake"); 557 | fiber_cancel_wake(f); 558 | return Val_unit; 559 | } 560 | 561 | value 562 | stub_fiber_id(value unit) 563 | { 564 | return Val_int(fiber->id); 565 | } 566 | 567 | value 568 | stub_break(value unit) 569 | { 570 | ev_break(EVBREAK_ALL); 571 | return Val_unit; 572 | } 573 | 574 | value 575 | stub_wait_io_ready(value fd_value, value mode_value) 576 | { 577 | int mode; 578 | switch (Int_val(mode_value)) { 579 | case 0: mode = EV_READ; break; 580 | case 1: mode = EV_WRITE; break; 581 | default: assert(0); 582 | } 583 | ev_io io = { .coro = 1 }; 584 | ev_io_init(&io, (void *)fiber, Int_val(fd_value), mode); 585 | ev_io_start(&io); 586 | stub_yield(Val_unit); 587 | ev_io_stop(&io); 588 | return Val_unit; 589 | } 590 | 591 | 592 | __attribute__((constructor)) 593 | static void 594 | fiber_init(void) 595 | { 596 | SLIST_INIT(&fibers); 597 | SLIST_INIT(&zombie_fibers); 598 | TAILQ_INIT(&wake_list); 599 | 600 | fibers_registry = mh_long_init(realloc); 601 | 602 | sched = calloc(1, sizeof(struct fiber)); 603 | sched->id = 1; 604 | strcpy(sched->name, "sched"); 605 | sched->last_retaddr = 0xbeef; 606 | sched_ctx = &sched->coro.ctx; 607 | 608 | fiber = sched; 609 | 610 | ev_default_loop(ev_recommended_backends() | EVFLAG_SIGNALFD); 611 | 612 | ev_prepare_init(&wake_prep, (void *)fiber_wakeup_pending); 613 | ev_set_priority(&wake_prep, -1); 614 | ev_prepare_start(&wake_prep); 615 | ev_async_init(&wake_async, fiber_async); 616 | ev_async_start(&wake_async); 617 | 618 | prev_scan_roots_hook = caml_scan_roots_hook; 619 | prev_enter_blocking_section_hook = caml_enter_blocking_section_hook; 620 | prev_leave_blocking_section_hook = caml_leave_blocking_section_hook; 621 | prev_try_leave_blocking_section_hook = caml_try_leave_blocking_section_hook; 622 | prev_stack_usage_hook = caml_stack_usage_hook; 623 | 624 | caml_scan_roots_hook = fiber_all_scan_roots; 625 | caml_enter_blocking_section_hook = fiber_enter_blocking_section; 626 | caml_leave_blocking_section_hook = fiber_leave_blocking_section; 627 | caml_try_leave_blocking_section_hook = fiber_try_leave_blocking_section; 628 | caml_stack_usage_hook = fiber_stack_usage; 629 | } 630 | --------------------------------------------------------------------------------