irc-bot/dispatch.ml

140 lines
3.8 KiB
OCaml
Raw Normal View History

2008-03-15 19:46:06 -06:00
type event = Input | Priority | Output | Error | Hangup
type timer_handler = float -> unit
type fd_handler = Unix.file_descr -> event list -> unit
2008-03-14 12:16:04 -06:00
2008-03-15 19:46:06 -06:00
module Timer =
2008-03-14 12:16:04 -06:00
Set.Make (struct
2008-03-15 19:46:06 -06:00
type t = (float * timer_handler)
let compare (time, handler) (time', handler') = compare time time'
2008-03-15 19:46:06 -06:00
end)
2008-03-14 12:16:04 -06:00
2008-03-15 19:46:06 -06:00
module Fd_map =
Map.Make (struct
type t = Unix.file_descr
let compare = compare
end)
2008-03-14 12:16:04 -06:00
type t = {
e : Epoll.t;
fds : (fd_handler * event list) Fd_map.t ref;
numfds : int ref;
2008-03-15 19:46:06 -06:00
timers : Timer.t ref;
2008-03-14 12:16:04 -06:00
}
(* select(), poll(), and epoll() treat timeout as an upper bound of time
to wait. This fudge factor helps ensure that given no FD activity,
this isn't run in a tight loop as a timer approaches. This value was
determined experimentally on a 1.25GHz G4 PPC to work most of the
time. Your mileage may vary. *)
let timeout_fudge = 0.001
2008-03-15 19:46:06 -06:00
let to_epoll = function
| Input -> Epoll.In
| Priority -> Epoll.Priority
| Output -> Epoll.Out
| Error -> Epoll.Error
| Hangup -> Epoll.Hangup
let from_epoll = function
| Epoll.In -> Input
| Epoll.Priority -> Priority
| Epoll.Out -> Output
| Epoll.Error -> Error
| Epoll.Hangup -> Hangup
let rec epoll_events_of_events = List.map to_epoll
2008-03-14 12:16:04 -06:00
2008-03-15 19:46:06 -06:00
let rec events_of_epoll_events = List.map from_epoll
2008-03-14 12:16:04 -06:00
let create size =
{e = Epoll.create size;
fds = ref Fd_map.empty;
numfds = ref 0;
2008-03-15 19:46:06 -06:00
timers = ref Timer.empty}
2008-03-14 12:16:04 -06:00
let destroy d =
Epoll.destroy d.e;
2008-03-15 19:46:06 -06:00
(* Explicitly unreference fds and timers, in case d sticks around *)
2008-03-14 12:16:04 -06:00
d.fds := Fd_map.empty;
d.numfds := 0;
2008-03-15 19:46:06 -06:00
d.timers := Timer.empty
2008-03-14 12:16:04 -06:00
let add d fd handler events =
Epoll.ctl d.e Epoll.Add (fd, (epoll_events_of_events events));
2008-03-15 19:46:06 -06:00
d.fds := Fd_map.add fd (handler, events) !(d.fds);
d.numfds := !(d.numfds) + 1
2008-03-14 12:16:04 -06:00
let modify d fd events =
Epoll.ctl d.e Epoll.Modify (fd, (epoll_events_of_events events))
let set_handler d fd handler =
2008-03-15 19:46:06 -06:00
let (_, events) = Fd_map.find fd !(d.fds) in
d.fds := Fd_map.add fd (handler, events) !(d.fds)
2008-03-14 12:16:04 -06:00
let delete d fd =
Epoll.ctl d.e Epoll.Delete (fd, []);
2008-03-15 19:46:06 -06:00
d.fds := Fd_map.remove fd !(d.fds);
d.numfds := !(d.numfds) - 1
2008-03-14 12:16:04 -06:00
let add_timer d handler time =
2008-03-15 19:46:06 -06:00
d.timers := Timer.add (time, handler) !(d.timers)
2008-03-14 12:16:04 -06:00
2008-03-15 19:46:06 -06:00
let delete_timer d time =
2008-03-14 12:16:04 -06:00
let may_remain (time', _) =
time' <> time
in
2008-03-15 19:46:06 -06:00
d.timers := Timer.filter may_remain !(d.timers)
2008-03-14 12:16:04 -06:00
2008-03-15 19:46:06 -06:00
let rec dispatch_timers d now =
if (!(d.timers) != Timer.empty) then
let (time, handler) = Timer.min_elt !(d.timers) in
if now < time then
2008-03-15 19:46:06 -06:00
()
else begin
handler time;
d.timers := Timer.remove (time, handler) !(d.timers);
dispatch_timers d now
end
2008-03-14 12:16:04 -06:00
let rec dispatch_results d events_list =
2008-03-14 12:16:04 -06:00
match events_list with
| [] ->
2008-03-15 19:46:06 -06:00
()
2008-03-14 12:16:04 -06:00
| (fd, epoll_events) :: tl ->
2008-03-15 19:46:06 -06:00
let handler, _ = Fd_map.find fd !(d.fds) in
let events = events_of_epoll_events epoll_events in
handler fd events;
dispatch_results d tl
2008-03-14 12:16:04 -06:00
let once d =
let now = Unix.gettimeofday () in
2008-03-14 12:16:04 -06:00
let timeout =
try
2008-03-15 19:46:06 -06:00
let (time, _) = Timer.min_elt !(d.timers) in
let delta = (time -. now +. timeout_fudge) in
max delta 0.0
2008-03-14 12:16:04 -06:00
with Not_found ->
(-1.0)
2008-03-14 12:16:04 -06:00
in
(if !(d.numfds) = 0 then
(* epoll()--and probably poll()--barfs if it has no file descriptors *)
ignore (Unix.select [] [] [] timeout)
else
(* poll() and epoll() wait *at most* timeout ms. If you have fds but they're not
doing anything, multiple calls to once may be required. This is lame. *)
let timeout_ms = int_of_float (timeout *. 1000.0) in
let result = Epoll.wait d.e !(d.numfds) timeout_ms in
dispatch_results d result);
dispatch_timers d (Unix.gettimeofday ())
2008-03-15 19:46:06 -06:00
2008-03-14 12:16:04 -06:00
let rec run d =
2008-03-15 19:46:06 -06:00
if ((!(d.fds) == Fd_map.empty) &&
(!(d.timers) == Timer.empty)) then
2008-03-14 12:16:04 -06:00
()
else begin
once d;
run d
end