File: joinHelper.ml

package info (click to toggle)
jocaml 4.01.0-3
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 16,736 kB
  • ctags: 23,836
  • sloc: ml: 111,262; ansic: 32,746; sh: 6,057; lisp: 4,230; makefile: 3,861; asm: 3,734; awk: 88; perl: 45; fortran: 21; sed: 19; cs: 9
file content (256 lines) | stat: -rw-r--r-- 6,698 bytes parent folder | download
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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
(***********************************************************************)
(*                                                                     *)
(*                           Objective Caml                            *)
(*                                                                     *)
(*            Luc Maranget, projet Moscova, INRIA Rocquencourt         *)
(*                                                                     *)
(*  Copyright 2004 Institut National de Recherche en Informatique et   *)
(*  en Automatique.  All rights reserved.  This file is distributed    *)
(*  under the terms of the Q Public License version 1.0.               *)
(*                                                                     *)
(***********************************************************************)

(* $Id: joinHelper.ml 12959 2012-09-27 13:12:51Z maranget $ *)

open Printf

(* Error *)
type error =
  | Magic     (** Magic number mismatch *)
  | Connect   (** Connection cannot be established *)

exception Error of (error * string)

let error e = raise (Error e)

(* Fork utilities *)

type fork_args =
| No_argument
| Same_arguments of string array
| Argument_generator of (int -> string array)

let prepend s a =
  let len = Array.length a in
  Array.init
    (succ len)
    (function
      | 0 -> String.copy s
      | i -> a.(pred i))

let get_args k = function
  | No_argument -> [| |]
  | Same_arguments a -> a
  | Argument_generator f -> f k

let filter_clients a =
  let a' = Array.copy a in
  let len = Array.length a in
  let i = ref 0 in
  let i' = ref 0 in
  while (!i < len) do
    if a.(!i) = "-clients" then begin
      i := !i + 2
    end else begin
      a'.(!i') <- a.(!i);
      incr i;
      incr i'
    end
  done;
  Array.sub a' 0 !i'

let do_forks name args n =
  let rec df k =
    if k >= n then []
    else match Unix.fork () with
    | 0 ->
        let args = prepend name (get_args k args) in
        Unix.handle_unix_error
          (fun () -> Unix.execv name args) ()
    | pid -> pid :: (df (succ k)) in
  df 0


(* Configuration *)

type configuration = {
    mutable host : string;
    mutable port : int;
    mutable clients : int;
    mutable forked_program : string;
    mutable fork_args : fork_args;
    mutable magic_id : string;
    mutable magic_value : string;
}

let default_host = "localhost"

let default_port = 12345

let default_clients = 0

let default_forked_program =
  if Array.length Sys.argv > 0 then
    String.copy Sys.argv.(0)
  else
    ""

let default_fork_args =
  let len = Array.length Sys.argv in
  Same_arguments (filter_clients (Array.sub Sys.argv 1 (pred len)))

let default_magic_id = "magic-number"

let default_magic_value = "magic-value"

let default_configuration () = {
  host = String.copy default_host;
  port = default_port;
  clients = default_clients;
  forked_program = String.copy default_forked_program;
  fork_args = default_fork_args;
  magic_id = String.copy default_magic_id;
  magic_value = String.copy default_magic_value;
}

let do_split s =
  try
    let idx = String.index s ':' in
    let host = String.sub s 0 idx in
    let port = String.sub s (idx+1) (String.length s - idx - 1) in
    host,port
  with Not_found -> s,""

let split_addr s =
  let host,port = do_split s in
  (match host with "" -> String.copy default_host | _ -> host),
  (match port with
  | "" -> default_port
  | _  ->
      try int_of_string port with _ ->
        raise (Arg.Bad ("invalid port: " ^ port)))

let make_commandline cfg =
  ("-host",
   Arg.String
     (fun s ->
       let h, p = split_addr s in
       cfg.host <- h;
       cfg.port <- p),
   "<name:port>  Set host name and port")::
  begin
    if cfg.clients < 0 then []
    else
      ["-clients",
       Arg.Int (fun i -> cfg.clients <- i),
       "<n>  Set number of clients to launch";
       
       "-forked-program",
       Arg.String (fun s -> cfg.forked_program <- String.copy s),
       "<name>  Set executable for clients" ]
  end


(* Client-related functions *)

type 'a lookup_function = Join.Ns.t -> string -> 'a

let lookup_once = Join.Ns.lookup

let rec lookup_times n w ns k =
  try
    lookup_once ns k
  with
  | Not_found ->
      if n = 1 then
        raise Not_found
      else begin
        Thread.delay w;
        let n' = if n = min_int then min_int else pred n in
        lookup_times n' w ns k
      end

type at_fail_chan = unit Join.chan

let do_at_fail f =
  def ch () = (try f () with _ -> ()); 0 in
  ch

def do_nothing_at_fail () = 0

let exit_at_fail_with_code c =
  def exit_at_fail () = exit c; 0 in
  exit_at_fail

let exit_at_fail = exit_at_fail_with_code 0

let rec get_site n addr =
  try Join.Site.there addr with
  | Join.Exit ->
      error (Connect,"remote site is dead")
  | Failure _ ->
      if n > 1 then begin
	Thread.delay 0.5 ; get_site (n-1) addr
      end else
      error (Connect,"remote site is absent")
        

let check_magic ns cfg =
  let lookup_magic = lookup_times ~-1 1.0 in
  let magic : string = lookup_magic ns cfg.magic_id in
  if magic <> cfg.magic_value then
    error (Magic,sprintf "%s <> %s" cfg.magic_value magic)
  
let get_inet_addr host =
  try (Unix.gethostbyname host).Unix.h_addr_list.(0)
  with Not_found ->
    error (Connect, sprintf "unknown host: %s" host)

let connect cfg =
  let inet_addr = get_inet_addr cfg.host in
  let server_addr = Unix.ADDR_INET (inet_addr, cfg.port) in
  let server_site = get_site 128 server_addr in
  let ns = Join.Ns.of_site server_site in
  check_magic ns cfg ;
  server_site,Join.Ns.of_site server_site


let init_client ?(at_fail=do_nothing_at_fail) cfg =
  let server_site,ns = connect cfg in
  Join.Site.at_fail server_site at_fail;
  let pids = do_forks cfg.forked_program cfg.fork_args cfg.clients in
  ns,pids

let init_client_with_lookup ?(at_fail=do_nothing_at_fail) ?(lookup=(lookup_times ~-1 1.0)) cfg id =
  let ns,pids = init_client ~at_fail:at_fail cfg in
  let v = lookup ns id in
  ns, pids,v


(* Server-related functions *)

let listen cfg =
  let inet_addr = get_inet_addr cfg.host in
  let server_addr = Unix.ADDR_INET (inet_addr, cfg.port) in
  Join.Ns.register Join.Ns.here cfg.magic_id (cfg.magic_value : string) ;
  begin try Join.Site.listen server_addr;
  with Failure msg -> error (Connect,msg) end ;
  ()

let init_server cfg =
  listen cfg ;
  let pids = do_forks cfg.forked_program cfg.fork_args (max 0 cfg.clients) in
  Join.Ns.here, pids

let init_server_with_register cfg id v =
  let ns, pids = init_server cfg in
  Join.Ns.register ns id v;
  ns, pids


(* Miscellaneous functions *)

let wait_forever () =
  def wait() & _never_sent() = reply to wait in
  wait ();
  exit 0 (* never reached *)