PageRenderTime 43ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/src/lwt-js/lwt_unix.ml

http://ocamljs.googlecode.com/
OCaml | 511 lines | 302 code | 60 blank | 149 comment | 26 complexity | 6338a306424eb98652b49c2803ee050a MD5 | raw file
Possible License(s): LGPL-2.0, WTFPL
  1. (* Lightweight thread library for Objective Caml
  2. * http://www.ocsigen.org/lwt
  3. * Module Lwt_unix
  4. * Copyright (C) 2005-2008 Jérôme Vouillon
  5. * Laboratoire PPS - CNRS Université Paris Diderot
  6. *
  7. * This program is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU Lesser General Public License as
  9. * published by the Free Software Foundation, with linking exception;
  10. * either version 2.1 of the License, or (at your option) any later
  11. * version.
  12. *
  13. * This program is distributed in the hope that it will be useful, but
  14. * WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. * Lesser General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU Lesser General Public
  19. * License along with this program; if not, write to the Free Software
  20. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
  21. * 02111-1307, USA.
  22. *)
  23. (*
  24. Non-blocking I/O and select does not (fully) work under Windows.
  25. The libray therefore does not use them under Windows, and will
  26. therefore have the following limitations:
  27. - No read will be performed while there are some threads ready to run
  28. or waiting to write;
  29. - When a read is pending, everything else will be blocked: [sleep]
  30. will not terminate and other reads will not be performed before
  31. this read terminates;
  32. - A write on a socket or a pipe can block the execution of the program
  33. if the data are never consumed at the other end of the connection.
  34. In particular, if both ends use this library and write at the same
  35. time, this could result in a dead-lock.
  36. - [connect] is blocking
  37. *)
  38. let windows_hack = Sys.os_type <> "Unix"
  39. module SleepQueue =
  40. Pqueue.Make (struct
  41. type t = float * unit Lwt.t
  42. let compare (t, _) (t', _) = compare t t'
  43. end)
  44. let sleep_queue = ref SleepQueue.empty
  45. let new_sleeps = ref []
  46. let sleep d =
  47. let res = Lwt.wait () in
  48. let t = if d <= 0. then 0. else Unix.gettimeofday () +. d in
  49. new_sleeps := (t, res) :: !new_sleeps;
  50. res
  51. let yield () = sleep 0.
  52. let get_time t =
  53. if !t = -1. then t := Unix.gettimeofday ();
  54. !t
  55. let in_the_past now t =
  56. t = 0. || t <= get_time now
  57. let rec restart_threads now =
  58. match
  59. try Some (SleepQueue.find_min !sleep_queue) with Not_found -> None
  60. with
  61. Some (time, thr) when in_the_past now time ->
  62. sleep_queue := SleepQueue.remove_min !sleep_queue;
  63. Lwt.wakeup thr ();
  64. restart_threads now
  65. | _ ->
  66. ()
  67. (****)
  68. type state = Open | Closed | Aborted of exn
  69. type file_descr = { fd : Unix.file_descr; mutable state: state }
  70. let mk_ch fd =
  71. if not windows_hack then Unix.set_nonblock fd;
  72. { fd = fd; state = Open }
  73. let check_descriptor ch =
  74. match ch.state with
  75. Open ->
  76. ()
  77. | Aborted e ->
  78. raise e
  79. | Closed ->
  80. raise (Unix.Unix_error (Unix.EBADF, "check_descriptor", ""))
  81. (****)
  82. module FdMap =
  83. Map.Make (struct type t = Unix.file_descr let compare = compare end)
  84. type watchers = (file_descr * (unit -> unit) list ref) FdMap.t ref
  85. let inputs = ref FdMap.empty
  86. let outputs = ref FdMap.empty
  87. exception Retry
  88. exception Retry_write
  89. exception Retry_read
  90. let find_actions set ch =
  91. try
  92. FdMap.find ch.fd !set
  93. with Not_found ->
  94. let res = (ch, ref []) in
  95. set := FdMap.add ch.fd res !set;
  96. res
  97. type 'a outcome =
  98. Success of 'a
  99. | Exn of exn
  100. | Requeued
  101. let rec wrap_syscall set ch cont action =
  102. let res =
  103. try
  104. check_descriptor ch;
  105. Success (action ())
  106. with
  107. Retry
  108. | Unix.Unix_error ((Unix.EAGAIN | Unix.EWOULDBLOCK | Unix.EINTR), _, _)
  109. | Sys_blocked_io ->
  110. (* EINTR because we are catching SIG_CHLD hence the system call
  111. might be interrupted to handle the signal; this lets us restart
  112. the system call eventually. *)
  113. add_action set ch cont action;
  114. Requeued
  115. | Retry_read ->
  116. add_action inputs ch cont action;
  117. Requeued
  118. | Retry_write ->
  119. add_action outputs ch cont action;
  120. Requeued
  121. | e ->
  122. Exn e
  123. in
  124. match res with
  125. Success v ->
  126. Lwt.wakeup cont v
  127. | Exn e ->
  128. Lwt.wakeup_exn cont e
  129. | Requeued ->
  130. ()
  131. and add_action set ch cont action =
  132. assert (ch.state = Open);
  133. let (_, actions) = find_actions set ch in
  134. actions := (fun () -> wrap_syscall set ch cont action) :: !actions
  135. let register_action set ch action =
  136. let cont = Lwt.wait () in
  137. add_action set ch cont action;
  138. cont
  139. let perform_actions set fd =
  140. try
  141. let (ch, actions) = FdMap.find fd !set in
  142. set := FdMap.remove fd !set;
  143. List.iter (fun f -> f ()) !actions
  144. with Not_found ->
  145. ()
  146. let active_descriptors set = FdMap.fold (fun key _ l -> key :: l) !set []
  147. let blocked_thread_count set =
  148. FdMap.fold (fun key (_, l) c -> List.length !l + c) !set 0
  149. (****)
  150. let wait_children = ref []
  151. let child_exited = ref false
  152. let _ =
  153. if not windows_hack then
  154. ignore (Sys.signal Sys.sigchld
  155. (Sys.Signal_handle (fun _ -> child_exited := true)))
  156. let bad_fd fd =
  157. try ignore (Unix.LargeFile.fstat fd); false with
  158. Unix.Unix_error (_, _, _) ->
  159. true
  160. let rec run thread =
  161. match Lwt.poll thread with
  162. Some v ->
  163. v
  164. | None ->
  165. sleep_queue :=
  166. List.fold_left
  167. (fun q e -> SleepQueue.add e q) !sleep_queue !new_sleeps;
  168. new_sleeps := [];
  169. let next_event =
  170. try
  171. let (time, _) = SleepQueue.find_min !sleep_queue in Some time
  172. with Not_found ->
  173. None
  174. in
  175. let now = ref (-1.) in
  176. let delay =
  177. match next_event with
  178. None -> -1.
  179. | Some 0. -> 0.
  180. | Some time -> max 0. (time -. get_time now)
  181. in
  182. let infds = active_descriptors inputs in
  183. let outfds = active_descriptors outputs in
  184. let (readers, writers, _) =
  185. if windows_hack then
  186. let writers = outfds in
  187. let readers =
  188. if delay = 0. || writers <> [] then [] else infds in
  189. (readers, writers, [])
  190. else if infds = [] && outfds = [] && delay = 0. then
  191. ([], [], [])
  192. else
  193. try
  194. let (readers, writers, _) as res =
  195. Unix.select infds outfds [] delay in
  196. if delay > 0. && !now <> -1. && readers = [] && writers = [] then
  197. now := !now +. delay;
  198. res
  199. with
  200. Unix.Unix_error (Unix.EINTR, _, _) ->
  201. ([], [], [])
  202. | Unix.Unix_error (Unix.EBADF, _, _) ->
  203. (List.filter bad_fd infds, List.filter bad_fd outfds, [])
  204. in
  205. restart_threads now;
  206. List.iter (fun fd -> perform_actions inputs fd) readers;
  207. List.iter (fun fd -> perform_actions outputs fd) writers;
  208. if !child_exited then begin
  209. child_exited := false;
  210. let l = !wait_children in
  211. wait_children := [];
  212. List.iter
  213. (fun ((cont, flags, pid) as e) ->
  214. try
  215. let (pid', _) as v = Unix.waitpid flags pid in
  216. if pid' = 0 then
  217. wait_children := e :: !wait_children
  218. else
  219. Lwt.wakeup cont v
  220. with e ->
  221. Lwt.wakeup_exn cont e)
  222. l
  223. end;
  224. run thread
  225. (****)
  226. let set_state ch st =
  227. ch.state <- st;
  228. perform_actions inputs ch.fd;
  229. perform_actions outputs ch.fd
  230. let abort ch e =
  231. if ch.state <> Closed then
  232. set_state ch (Aborted e)
  233. let unix_file_descr ch = ch.fd
  234. let of_unix_file_descr fd = mk_ch fd
  235. let read ch buf pos len =
  236. try
  237. check_descriptor ch;
  238. if windows_hack then raise (Unix.Unix_error (Unix.EAGAIN, "", ""));
  239. Lwt.return (Unix.read ch.fd buf pos len)
  240. with
  241. Unix.Unix_error ((Unix.EAGAIN | Unix.EWOULDBLOCK), _, _) ->
  242. register_action inputs ch (fun () -> Unix.read ch.fd buf pos len)
  243. | e ->
  244. Lwt.fail e
  245. let write ch buf pos len =
  246. try
  247. check_descriptor ch;
  248. Lwt.return (Unix.write ch.fd buf pos len)
  249. with
  250. Unix.Unix_error ((Unix.EAGAIN | Unix.EWOULDBLOCK), _, _) ->
  251. register_action outputs ch (fun () -> Unix.write ch.fd buf pos len)
  252. | e ->
  253. Lwt.fail e
  254. let wait_read ch = register_action inputs ch (fun () -> ())
  255. let wait_write ch = register_action outputs ch (fun () -> ())
  256. let pipe () =
  257. let (out_fd, in_fd) = Unix.pipe() in
  258. (mk_ch out_fd, mk_ch in_fd)
  259. let pipe_in () =
  260. let (out_fd, in_fd) = Unix.pipe() in
  261. (mk_ch out_fd, in_fd)
  262. let pipe_out () =
  263. let (out_fd, in_fd) = Unix.pipe() in
  264. (out_fd, mk_ch in_fd)
  265. let socket dom typ proto =
  266. let s = Unix.socket dom typ proto in
  267. mk_ch s
  268. let shutdown ch shutdown_command =
  269. check_descriptor ch;
  270. Unix.shutdown ch.fd shutdown_command
  271. let socketpair dom typ proto =
  272. let (s1, s2) = Unix.socketpair dom typ proto in
  273. (mk_ch s1, mk_ch s2)
  274. let accept ch =
  275. try
  276. check_descriptor ch;
  277. register_action inputs ch
  278. (fun () ->
  279. let (s, addr) = Unix.accept ch.fd in
  280. (mk_ch s, addr))
  281. with e ->
  282. Lwt.fail e
  283. let check_socket ch =
  284. register_action outputs ch
  285. (fun () ->
  286. try ignore (Unix.getpeername ch.fd) with
  287. Unix.Unix_error (Unix.ENOTCONN, _, _) ->
  288. (* Get the socket error *)
  289. ignore (Unix.read ch.fd " " 0 1))
  290. let connect ch addr =
  291. try
  292. check_descriptor ch;
  293. Unix.connect ch.fd addr;
  294. Lwt.return ()
  295. with
  296. Unix.Unix_error
  297. ((Unix.EINPROGRESS | Unix.EWOULDBLOCK | Unix.EAGAIN), _, _) ->
  298. check_socket ch
  299. | e ->
  300. Lwt.fail e
  301. let _waitpid flags pid =
  302. try
  303. Lwt.return (Unix.waitpid flags pid)
  304. with e ->
  305. Lwt.fail e
  306. let waitpid flags pid =
  307. if List.mem Unix.WNOHANG flags || windows_hack then
  308. _waitpid flags pid
  309. else
  310. let flags = Unix.WNOHANG :: flags in
  311. Lwt.bind (_waitpid flags pid) (fun ((pid', _) as res) ->
  312. if pid' <> 0 then
  313. Lwt.return res
  314. else
  315. let res = Lwt.wait () in
  316. wait_children := (res, flags, pid) :: !wait_children;
  317. res)
  318. let wait () = waitpid [] (-1)
  319. let system cmd =
  320. match Unix.fork () with
  321. 0 -> begin try
  322. Unix.execv "/bin/sh" [| "/bin/sh"; "-c"; cmd |]
  323. with _ ->
  324. exit 127
  325. end
  326. | id -> Lwt.bind (waitpid [] id) (fun (pid, status) -> Lwt.return status)
  327. let close ch =
  328. if ch.state = Closed then check_descriptor ch;
  329. set_state ch Closed;
  330. Unix.close ch.fd
  331. let setsockopt ch opt v =
  332. check_descriptor ch;
  333. Unix.setsockopt ch.fd opt v
  334. let bind ch addr =
  335. check_descriptor ch;
  336. Unix.bind ch.fd addr
  337. let listen ch cnt =
  338. check_descriptor ch;
  339. Unix.listen ch.fd cnt
  340. let set_close_on_exec ch =
  341. check_descriptor ch;
  342. Unix.set_close_on_exec ch.fd
  343. (****)
  344. (*
  345. type popen_process =
  346. Process of in_channel * out_channel
  347. | Process_in of in_channel
  348. | Process_out of out_channel
  349. | Process_full of in_channel * out_channel * in_channel
  350. let popen_processes = (Hashtbl.create 7 : (popen_process, int) Hashtbl.t)
  351. let open_proc cmd proc input output toclose =
  352. match Unix.fork () with
  353. 0 -> if input <> Unix.stdin then begin
  354. Unix.dup2 input Unix.stdin;
  355. Unix.close input
  356. end;
  357. if output <> Unix.stdout then begin
  358. Unix.dup2 output Unix.stdout;
  359. Unix.close output
  360. end;
  361. List.iter Unix.close toclose;
  362. Unix.execv "/bin/sh" [| "/bin/sh"; "-c"; cmd |]
  363. | id -> Hashtbl.add popen_processes proc id
  364. let open_process_in cmd =
  365. let (in_read, in_write) = pipe_in () in
  366. let inchan = in_channel_of_descr in_read in
  367. open_proc cmd (Process_in inchan) Unix.stdin in_write [in_read];
  368. Unix.close in_write;
  369. Lwt.return inchan
  370. let open_process_out cmd =
  371. let (out_read, out_write) = pipe_out () in
  372. let outchan = out_channel_of_descr out_write in
  373. open_proc cmd (Process_out outchan) out_read Unix.stdout [out_write];
  374. Unix.close out_read;
  375. Lwt.return outchan
  376. let open_process cmd =
  377. let (in_read, in_write) = pipe_in () in
  378. let (out_read, out_write) = pipe_out () in
  379. let inchan = in_channel_of_descr in_read in
  380. let outchan = out_channel_of_descr out_write in
  381. open_proc cmd (Process(inchan, outchan)) out_read in_write
  382. [in_read; out_write];
  383. Unix.close out_read;
  384. Unix.close in_write;
  385. Lwt.return (inchan, outchan)
  386. let open_proc_full cmd env proc input output error toclose =
  387. match Unix.fork () with
  388. 0 -> Unix.dup2 input Unix.stdin; Unix.close input;
  389. Unix.dup2 output Unix.stdout; Unix.close output;
  390. Unix.dup2 error Unix.stderr; Unix.close error;
  391. List.iter Unix.close toclose;
  392. Unix.execve "/bin/sh" [| "/bin/sh"; "-c"; cmd |] env
  393. | id -> Hashtbl.add popen_processes proc id
  394. let open_process_full cmd env =
  395. let (in_read, in_write) = pipe_in () in
  396. let (out_read, out_write) = pipe_out () in
  397. let (err_read, err_write) = pipe_in () in
  398. let inchan = in_channel_of_descr in_read in
  399. let outchan = out_channel_of_descr out_write in
  400. let errchan = in_channel_of_descr err_read in
  401. open_proc_full cmd env (Process_full(inchan, outchan, errchan))
  402. out_read in_write err_write [in_read; out_write; err_read];
  403. Unix.close out_read;
  404. Unix.close in_write;
  405. Unix.close err_write;
  406. Lwt.return (inchan, outchan, errchan)
  407. let find_proc_id fun_name proc =
  408. try
  409. let pid = Hashtbl.find popen_processes proc in
  410. Hashtbl.remove popen_processes proc;
  411. pid
  412. with Not_found ->
  413. raise (Unix.Unix_error (Unix.EBADF, fun_name, ""))
  414. let close_process_in inchan =
  415. let pid = find_proc_id "close_process_in" (Process_in inchan) in
  416. close_in inchan;
  417. Lwt.bind (waitpid [] pid) (fun (_, status) -> Lwt.return status)
  418. let close_process_out outchan =
  419. let pid = find_proc_id "close_process_out" (Process_out outchan) in
  420. close_out outchan;
  421. Lwt.bind (waitpid [] pid) (fun (_, status) -> Lwt.return status)
  422. let close_process (inchan, outchan) =
  423. let pid = find_proc_id "close_process" (Process(inchan, outchan)) in
  424. close_in inchan; close_out outchan;
  425. Lwt.bind (waitpid [] pid) (fun (_, status) -> Lwt.return status)
  426. let close_process_full (inchan, outchan, errchan) =
  427. let pid =
  428. find_proc_id "close_process_full"
  429. (Process_full(inchan, outchan, errchan)) in
  430. close_in inchan; close_out outchan; close_in errchan;
  431. Lwt.bind (waitpid [] pid) (fun (_, status) -> Lwt.return status)
  432. *)
  433. (****)
  434. (* Monitoring functions *)
  435. let inputs_length () = blocked_thread_count inputs
  436. let outputs_length () = blocked_thread_count outputs
  437. let wait_children_length () = List.length !wait_children
  438. let get_new_sleeps () = List.length !new_sleeps
  439. let sleep_queue_size () = SleepQueue.size !sleep_queue