|
| 1 | +open Lwt_io |
| 2 | + |
| 3 | +(*****************************************************************************) |
| 4 | +(* *) |
| 5 | +(* Asynchronous Programming (and Concurrency) *) |
| 6 | +(* *) |
| 7 | +(* Concurrency in OCaml is done via Threads, and it can also leverage the *) |
| 8 | +(* [Domain] data structure for parallel programming. However there are 2 *) |
| 9 | +(* main libraries to handle and wrap async operations: Lwt (acronym for *) |
| 10 | +(* "lightweight threads") and Async. *) |
| 11 | +(* *) |
| 12 | +(* Lwt is a promise-like library that wraps IO and Unix operations, among *) |
| 13 | +(* other tasks that may need to be done concurrently. Many libraries are *) |
| 14 | +(* built with or on top of it; [cohttp] (HTTP library) is an example. *) |
| 15 | +(* A new library is on development called Riot which promises Erlang *) |
| 16 | +(* -like actor concurrency that can be run on multiple cores. But for this *) |
| 17 | +(* exercise I'm gonna use 3 {e opam} packages: *) |
| 18 | +(* *) |
| 19 | +(* 1. [lwt]: The main Lwt package that contains many modules. One of them *) |
| 20 | +(* is the [Lwt_io] module that wraps your usual IO operations (print, *) |
| 21 | +(* read_char, read_line, assert, etc). And the core [Lwt] module that *) |
| 22 | +(* contains the glue functions like [join], [return], [pick], [bind]. *) |
| 23 | +(* 2. [lwt.unix]: Unix functions wrapped by promises, like [sleep]. *) |
| 24 | +(* 3. [lwt_ppx]: Syntax sugar for [bind] (and other) operations. Example: *) |
| 25 | +(* [let%lwt ln = Lwt_io.read_line () in printf "I got: %s" ln] instead *) |
| 26 | +(* of [Lwt_io.read_line () >>= fun ln -> Lwt_io.printf "I got %s" ln]. *) |
| 27 | +(* *) |
| 28 | +(*****************************************************************************) |
| 29 | + |
| 30 | +let time_now () = |
| 31 | + let open Core.Time_float in |
| 32 | + now () |> to_ofday ~zone:Zone.utc |> Ofday.to_sec_string |
| 33 | +;; |
| 34 | + |
| 35 | +let delay_function ?(seconds = 1.0) ~name f = |
| 36 | + let%lwt () = |
| 37 | + printf "[%s] will run after %.2f seconds | %s\n" name seconds (time_now ()) |
| 38 | + in |
| 39 | + let%lwt () = Lwt_unix.sleep seconds in |
| 40 | + f (); |
| 41 | + printf "[%s] has been called | %s\n" name (time_now ()) |
| 42 | +;; |
| 43 | + |
| 44 | +(* Output: |
| 45 | +
|
| 46 | + [Ejercicio] will run after 4.00 seconds | 21:22:36 |
| 47 | + The answer is 42! |
| 48 | + [Ejercicio] has been called | 21:22:40 |
| 49 | +*) |
| 50 | +let _ = |
| 51 | + Lwt_main.run |
| 52 | + @@ delay_function ~name:"Ejercicio" ~seconds:4.0 (fun () -> |
| 53 | + print_endline "The answer is 42!") |
| 54 | +;; |
| 55 | + |
| 56 | +(****************************************************************************) |
| 57 | +(* *) |
| 58 | +(* Dificultad Extra (opcional) *) |
| 59 | +(* *) |
| 60 | +(* Utilizando el concepto de asincronía y la función anterior, crea el *) |
| 61 | +(* siguiente programa que ejecuta en este orden: *) |
| 62 | +(* *) |
| 63 | +(* - Una función C que dura 3 segundos. *) |
| 64 | +(* - Una función B que dura 2 segundos. *) |
| 65 | +(* - Una función A que dura 1 segundo. *) |
| 66 | +(* - Una función D que dura 1 segundo. *) |
| 67 | +(* - Las funciones C, B, y A se ejecutan en paralelo. *) |
| 68 | +(* - La función D comienza su ejecición cuando las 3 anteriores finalicén. *) |
| 69 | +(* *) |
| 70 | +(****************************************************************************) |
| 71 | + |
| 72 | +(* Output: |
| 73 | +
|
| 74 | + [C] will run after 3.00 seconds | 22:02:10 |
| 75 | + [B] will run after 2.00 seconds | 22:02:10 |
| 76 | + [A] will run after 1.00 seconds | 22:02:10 |
| 77 | + Uploaded image family.jpg |
| 78 | + [A] has been called | 22:02:11 |
| 79 | + Response from server: Error 404 |
| 80 | + [B] has been called | 22:02:12 |
| 81 | + Successfully downloaded avengers.mp4 |
| 82 | + [C] has been called | 22:02:13 |
| 83 | + [D] will run after 1.00 seconds | 22:02:13 |
| 84 | + Processed results, goodbye! |
| 85 | + [D] has been called | 22:02:14 |
| 86 | +*) |
| 87 | +let _ = |
| 88 | + let open Lwt in |
| 89 | + let c = |
| 90 | + delay_function ~name:"C" ~seconds:3.0 (fun () -> |
| 91 | + print_endline "Successfully downloaded avengers.mp4") |
| 92 | + in |
| 93 | + let b = |
| 94 | + delay_function ~name:"B" ~seconds:2.0 (fun () -> |
| 95 | + print_endline "Response from server: Error 404") |
| 96 | + in |
| 97 | + let a = |
| 98 | + delay_function ~name:"A" (fun () -> |
| 99 | + print_endline "Uploaded image family.jpg") |
| 100 | + in |
| 101 | + (* If I create the promise and store it in a variable, nothing stops it from |
| 102 | + starting right away so I have to defer its creation until it's needed. *) |
| 103 | + let d () = |
| 104 | + delay_function ~name:"D" (fun () -> |
| 105 | + print_endline "Processed results, goodbye!") |
| 106 | + in |
| 107 | + Lwt_main.run (join [ c; b; a ] >>= d) |
| 108 | +;; |
| 109 | + |
| 110 | +(* Lastly, there are other ways of handling this. What about threads? I could |
| 111 | + have used the [Thead] module with these functions: [Thread.create] and |
| 112 | + [Thread.join]; and [Thread.delay] to block each thread for N seconds. *) |
0 commit comments