## Async Warmup
Read the following code:
```
open Async.Std
(* do some meaningless computation for about 5 seconds,
depending on machine speed *)
let busywait =
let ctr = ref 0 in fun () ->
ctr := 0;
for i = 1 to 500_000_000 do
incr ctr
done
(* helper function to print a string *)
let output s = Printf.printf "%s\n" s
let _ = output "A"
let d = return 42
let _ = output "B"
let _ = upon d (fun n -> output "C")
let _ = output "D"
```
**Exercise:** Before trying to run that code, first think about
an answer to this question: in what order will the four strings
be output? Write down a guess before proceeding.
**Exercise:** Now put the above code in a file named `warmup.ml`.
Compile and run it with
```
$ cs3110 compile -t -p async warmup.ml
$ cs3110 run warmup
```
What output do you get? Does it match what you guessed?
**Exercise:**
The reason why `C` is missing is that the scheduler is not running.
The callback that is registered with `upon` gets to run only if the scheduler
actually schedules it. So add this as the last line of the file:
```
let _ = Scheduler.go()
```
Recompile and run. What happens?
**Exercise:**
The reason you don't get any output has to do with *buffering*. The output
has been sent off to be printed, but is still temporarily sitting in a buffer
that hasn't yet been printed. To force the buffer to be cleared, change
the format specifier passed to `printf` to be `"%s\n%!"`. The `%!` forces
the buffer to be cleared. Recompile and run. What happens?
**Exercise:**
You should now see all four strings output. But they might be in a different
order than you guessed. The callback that outputs `C` is registered, then
`D` is output, then the scheduler starts. Only at that point can the callback
be scheduled and run. So `D` is printed before `C`. Then the scheduler keeps
running, though it has nothing more to do. To terminate the program, you'll
have to enter Control-C.
**Exercise:**
Add a call to `busywait ()` in various places in the program, recompile, and run,
to see what effects it has. Try at least these places:
* `busywait(); output "A"`
* `return (busywait (); 42)`
* `busywait(); output "C"`
* `busywait(); Scheduler.go()`
Explain in words what you observe and why.
## Bind
So far we've seen one way of registering a callback with the Async
scheduler: use
```
upon : 'a Deferred.t -> ('a -> unit) -> unit
```
The callback gets to use the contents of the deferred, but the callback
doesn't return anything interesting—just `()`, which is consumed
by the scheduler. What if you want the callback to produce some
interesting value that can be used by the rest of the computation?
There's a way to register that kind of callback with a function
```
Deferred.bind : 'a Deferred.t -> ('a -> 'b Deferred.t) -> 'b Deferred.t
```
This function is used to schedule a deferred computation to take place
*after* an existing deferred computation finishes.
The function `bind` takes two inputs:
a deferred `d : 'a Deferred.t`, and a callback `c : ('a -> 'b Deferred.t)`.
The expression `bind d c` immediately returns with a new deferred `d'`.
Sometime after `d` is determined (if ever), the scheduler runs `c` on
the contents of `d`. The callback `c` itself produces a new deferred,
which if it ever becomes determined, also causes `d'` to be determined
with the same value.
**Exercise:** Enter the following code in a file:
```
open Async.Std
let output s = Printf.printf "%s\n%!" s
let fc = Reader.file_contents Sys.argv.(1)
let uc = Deferred.bind fc (fun s -> return (String.uppercase s))
let _ = upon uc (fun s -> output s)
let _ = Scheduler.go ()
```
The code creates a deferred `fc` that will become determined after the
contents of a file (named by the first command-line argument)
have been read. The code then registers a callback to be run
when those contents are available. That callback returns
a deferred that is immediately determined and is the upper-cased
version of the file. The code finally registers another callback
to print those uppercased contents.
**Exercise:** Remove the `return` function call from the above code and
attempt to compile it. Why does compilation fail? Why is `return` needed?
**Exercise:** Once again, you will need to exit the above program
with Control-C. That's because the scheduler is still running, even
though there's nothing left to do. Change the call to `output` as follows:
```
output s; ignore(exit 0)
```
Recompile and run. The program should now terminate when the file contents
have been uppercased and printed. The function `exit : int -> 'a Deferred.t`
is provided by `Async.Std` and causes execution to terminate. The function
`ignore : 'a -> unit` is provided by `Pervasives`.
## Threads
* Write a program that creates a single thread, which prints `"Goodbye, OCaml"`.
Hint: if your program does not produce any output, you probably forgot to `join`
the thread.
* Modify the previous program to create three threads, each of which prints
`"Goodbye, OCaml, from thread n"`, where `n` is the thread's *id*
as determined by `Thread.id`. See the
[`Thread` module documentation][Thread] for more details.
Each thread's id should be unique.
Run the program many times to see that the output is nondeterministic.
Consider the following code:
```
let ctr = ref 0
let inc_lots () =
for i=1 to 100_000_000 do
incr ctr
done
let print_ctr () =
Printf.printf "Counter = %d" (!ctr)
```
This code creates a counter named `ctr`, and provides two functions:
`inc_lots`, which increments the counter many times, and `print_ctr`,
which prints the value of the counter.
* Using the above code, write a program that creates two threads,
one of which calls `inc_lots`, and the other calls `print_ctr`.
Run the program many times to see that the output is nondeterministic.
* Modify the previous program to use a `Mutex` to ensure that the output
is always either `0` or `100_000_000` but never anything in between.
[Thread]: http://caml.inria.fr/pub/docs/manual-ocaml/libref/Thread.html
## * Monitor a file
Write an Async program that monitors the contents of a log file.
Specifically, your program should open the file, continually
read a line from the file, and as each line becomes available,
print the line to stdout. When you reach the end of the file (EOF),
your program should terminate.
To get started, open a new terminal window and enter the following
commands:
```
$ mkfifo log
$ cat >log
```
Now anything you type into the terminal window (after pressing return)
will be added to the log file. This will enable you to test your program
interactively.
Next, enter the following code into a file named `monitor.ml`:
```
open Async.Std
let rec monitor file =
(* hint: use Reader.read_line *)
let _ = Reader.open_file "log"
... (* hint: use bind and monitor *)
let _ = Scheduler.go ()
```
Complete the code by filling in the sections marked `...`.
[`Reader.read_line` and `Reader.open_file` are documented
in the Async documentation][reader].
To pattern match against the result of `Reader.read_line`, you need
to write a pattern like the following:
```
| `Ok line -> (* do something with the line, which is a string *)
| `Eof -> (* do something because the end of file is reached *)
```
[reader]: https://ocaml.janestreet.com/ocaml-core/111.28.00/doc/async/#Std.Reader
[async-doc]: https://ocaml.janestreet.com/ocaml-core/111.28.00/doc/async/#Std