-
Notifications
You must be signed in to change notification settings - Fork 18
Simplify build log and let store set its location #90
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,21 +4,13 @@ let max_chunk_size = 4096 | |
|
|
||
| type t = { | ||
| mutable state : [ | ||
| | `Open of Lwt_unix.file_descr * unit Lwt_condition.t (* Fires after writing more data. *) | ||
| | `Open of string * Lwt_unix.file_descr * unit Lwt_condition.t (* Fires after writing more data. *) | ||
| | `Readonly of string | ||
| | `Empty | ||
| | `Finished | ||
| ]; | ||
| mutable len : int; | ||
| } | ||
|
|
||
| let with_dup fd fn = | ||
| let fd = Lwt_unix.dup fd in | ||
| Lwt_unix.set_close_on_exec fd; | ||
| Lwt.finalize | ||
| (fun () -> fn fd) | ||
| (fun () -> Lwt_unix.close fd) | ||
|
|
||
| let catch_cancel fn = | ||
| Lwt.catch fn | ||
| (function | ||
|
|
@@ -27,34 +19,39 @@ let catch_cancel fn = | |
| ) | ||
|
|
||
| let tail ?switch t dst = | ||
| match t.state with | ||
| | `Finished -> invalid_arg "tail: log is finished!" | ||
| | `Readonly path -> | ||
| let readonly_tail path buf i = | ||
| Lwt_io.(with_file ~mode:input) path @@ fun ch -> | ||
| let buf = Bytes.create max_chunk_size in | ||
| Lwt_io.set_position ch (Int64.of_int i) >>= fun () -> | ||
| let rec aux () = | ||
| Lwt_io.read_into ch buf 0 max_chunk_size >>= function | ||
| | 0 -> Lwt_result.return () | ||
| | n -> dst (Bytes.sub_string buf 0 n); aux () | ||
| in | ||
| aux () | ||
| in | ||
| match t.state with | ||
| | `Readonly path -> | ||
| let buf = Bytes.create max_chunk_size in | ||
| catch_cancel @@ fun () -> | ||
| let th = aux () in | ||
| let th = readonly_tail path buf 0 in | ||
| Lwt_switch.add_hook_or_exec switch (fun () -> Lwt.cancel th; Lwt.return_unit) >>= fun () -> | ||
| th | ||
| | `Empty -> Lwt_result.return () | ||
| | `Open (fd, cond) -> | ||
| (* Dup [fd], which can still work after [fd] is closed. *) | ||
| with_dup fd @@ fun fd -> | ||
| | `Open (_, fd, cond) -> | ||
| let buf = Bytes.create max_chunk_size in | ||
| let rec aux i = | ||
| match switch with | ||
| | Some sw when not (Lwt_switch.is_on sw) -> Lwt_result.fail `Cancelled | ||
| | _ -> | ||
| let avail = min (t.len - i) max_chunk_size in | ||
| if avail > 0 then ( | ||
| Lwt_unix.pread fd ~file_offset:i buf 0 avail >>= fun n -> | ||
| dst (Bytes.sub_string buf 0 n); | ||
| aux (i + avail) | ||
| match t.state with | ||
| | `Open _ -> | ||
| Lwt_unix.pread fd ~file_offset:i buf 0 avail >>= fun n -> | ||
| dst (Bytes.sub_string buf 0 n); | ||
| aux (i + avail) | ||
| | `Readonly path -> readonly_tail path buf i | ||
| | _ -> Lwt_result.return () | ||
| ) else ( | ||
| match t.state with | ||
| | `Open _ -> Lwt_condition.wait cond >>= fun () -> aux i | ||
|
|
@@ -70,28 +67,25 @@ let create path = | |
| Lwt_unix.openfile path Lwt_unix.[O_CREAT; O_TRUNC; O_RDWR; O_CLOEXEC] 0o666 >|= fun fd -> | ||
| let cond = Lwt_condition.create () in | ||
| { | ||
| state = `Open (fd, cond); | ||
| state = `Open (path, fd, cond); | ||
| len = 0; | ||
| } | ||
|
|
||
| let finish t = | ||
| match t.state with | ||
| | `Finished -> invalid_arg "Log is already finished!" | ||
| | `Open (fd, cond) -> | ||
| t.state <- `Finished; | ||
| | `Open (path, fd, cond) -> | ||
| t.state <- `Readonly path; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this has the same problem: a reader will open the path in the temporary clone, preventing it from being removed (unless a read-only FD is OK somehow?). Instead, I think this needs to transition to a new |
||
| Lwt_unix.close fd >|= fun () -> | ||
| Lwt_condition.broadcast cond () | ||
| | `Readonly _ -> | ||
| t.state <- `Finished; | ||
| Lwt.return_unit | ||
| | `Empty -> | ||
| Lwt.return_unit (* Empty can be reused *) | ||
|
|
||
| let write t data = | ||
| match t.state with | ||
| | `Finished -> invalid_arg "write: log is finished!" | ||
| | `Readonly _ | `Empty -> invalid_arg "Log is read-only!" | ||
| | `Open (fd, cond) -> | ||
| | `Open (_, fd, cond) -> | ||
| let len = String.length data in | ||
| Os.write_all fd (Bytes.of_string data) 0 len >>= fun () -> | ||
| t.len <- t.len + len; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -31,9 +31,13 @@ module type STORE = sig | |
| val delete : t -> id -> unit Lwt.t | ||
| (** [delete t id] removes [id] from the store, if present. *) | ||
|
|
||
| val result : t -> id -> string option | ||
| val result : t -> id -> string option Lwt.t | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why does this need to be async? It seems useful to have an atomic way of finding out whether something exists in the store. Otherwise, how do we know the result is still valid by the time it has returned?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With the Docker backend I'm making a call to Docker to check whether the result as a Docker image exists. I use functions from
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's move that to a separate PR then. It doesn't have anything to do with the build log problem. |
||
| (** [result t id] is the path of the build result for [id], if present. *) | ||
|
|
||
| val log_file : t -> id -> string Lwt.t | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we want a different log location for each store? |
||
| (** [log_file t id] is the path of the build logs for [id]. The file may | ||
| not exist if the build has never been run, or failed. *) | ||
|
|
||
| val state_dir : t -> string | ||
| (** [state_dir] is the path of a directory which can be used to store mutable | ||
| state related to this store (e.g. an sqlite3 database). *) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.