summaryrefslogtreecommitdiff
path: root/khb/khs_exec_local.ml
blob: fb97b47b972a6f91c401d28d375752fed06589c0 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
open Util
open Khs_ast
open Khs_exec

(* TODO : channels cannot be communicated over channels, although
    it should totally be possible because it's so great and so usefull ! *)

let childs = ref []

let wait_childs () =
    Unix.handle_unix_error (fun () ->
        List.iter (fun pid -> ignore (Unix.waitpid [] pid)) !childs)
        ()

let max_chans = 12
let ochans = Hashtbl.create 12
let ochans_time = ref Smap.empty
let get_chan id =
    ochans_time := Smap.map (fun v -> v+1) !ochans_time;
    ochans_time := Smap.add id 0 !ochans_time;
    if Smap.cardinal !ochans_time > max_chans then begin
        let maxchan, _ =
            Smap.fold
                (fun k v (mk, mv) -> if v > mv then (k, v) else (mk, mv))
                !ochans_time ("", 0) in
        let i_fd, o_fd = Hashtbl.find ochans maxchan in
        Unix.close i_fd; Unix.close o_fd;
        Hashtbl.remove ochans maxchan;
        ochans_time := Smap.remove maxchan !ochans_time
    end;
    try
        Hashtbl.find ochans id
    with Not_found ->
        let i, o = Unix.openfile ("/tmp/"^id) [Unix.O_RDONLY] 0,
                   Unix.openfile ("/tmp/"^id) [Unix.O_WRONLY] 0 in
        Hashtbl.add ochans id (i, o);
        i, o

let newchan proc =
    let id = "khs_ch_" ^ string_of_int (Random.int 1000000)
        ^ "-" ^ string_of_int (Random.int 1000000) in
    Unix.mkfifo ("/tmp/" ^ id) 0o666;
    Many (Smap.add (psep^"in") (VStr id)
            (Smap.add (psep^"out") (VStr id) Smap.empty))
    
let exec_proc proc =
    while proc.xstatus <> PSDone do
        match proc.xstatus with
        | PSDone -> assert false
        | PSExec | PSExecRecvd _ ->
            exec_stmt proc
        | PSSend(c, kv) ->
            proc.xstatus <- PSExec;
            begin
                if c == "stdout" then
                    Format.printf "%s@." (kval_descr kv)
                else
                    let _, c_out = get_chan c in
                    Marshal.to_channel (Unix.out_channel_of_descr c_out) kv []
            end
        | PSRecv c ->
            let c_in, _ = get_chan c in
            proc.xstatus <- PSExecRecvd (Marshal.from_channel (Unix.in_channel_of_descr c_in))
    done

let spawn proc pos =
    let pid = Unix.fork () in
    if pid = 0 then begin
        childs := [];
        exec_proc { proc with xpos = pos};
        wait_childs();
        exit 0
    end else
        childs := pid::!childs



let exec_program p =
    Random.init (int_of_float (Unix.time()));
    let proc = {
        xspawn = spawn;
        xnewchan = newchan;
        xprog = p;
        xvals = Smap.empty;
        xstatus = PSExec;
        xpos = 0;
    } in
    proc.xvals <- Smap.add framevar (VInt 0) proc.xvals;
    proc.xvals <- Smap.add "stdout" (VStr "stdout") proc.xvals;
    exec_proc proc;
    wait_childs()