Skip to content
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

Add an Eio backend #25

Merged
merged 4 commits into from
Apr 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,21 @@ jobs:
- 4.03.x
- 4.08.x
- 4.12.x
- 5.1.x
include:
- packages: [ {name: inotify, opam-file: inotify.opam} ]
- packages: [ {name: inotify, opam-file: inotify.opam}, { name: inotify-eio, opam-file: inotify.opam } ]
ocaml-compiler: 5.1.x
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- uses: ocaml/setup-ocaml@v2
with:
ocaml-compiler: ${{ matrix.ocaml-compiler }}
opam-local-packages: ${{ join(matrix.packages.*.opam-file, ' ') }}
- run: opam pin -n .
- run: opam depext -yt inotify
- run: opam install -t . --deps-only
- run: opam exec -- dune build
- run: opam depext -yt ${{ join(matrix.packages.*.name, ' ') }}
- run: opam install -t ${{ join(matrix.packages.*.name, ' ') }} --deps-only
- run: opam exec -- dune build --only-packages ${{ join(matrix.packages.*.name, ',') }}
- run: opam exec -- dune runtest
if: ${{ matrix.os == 'ubuntu-latest'}}
4 changes: 4 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@

## Unreleased

add an Eio backend in a new inotify-eio package

## 2.5

add @since tags
Expand Down
17 changes: 15 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@ The bindings are available via [OPAM](https://opam.ocaml.org):

Alternatively, you can do it manually:

# If you want to lwt_inotify
# If you want to use lwt_inotify
$ opam install lwt
# If you want to use eio_inotify
$ opam install eio
$ opam install .

Usage
Expand All @@ -42,7 +44,18 @@ Lwt_main.run (
(* watch=1 cookie=0 events=CREATE "file" *)
```

Note that Lwt-style interface returns events one-by-one, but the Unix-style one returns
Eio-style interface (findlib package `inotify-eio`):

``` ocaml
Eio_main.run @@ fun _env ->
let inotify = Eio_inotify.create () in
let _watch = Eio_inotify.add_watch inotify "dir" [Inotify.S_Create] in
let event = Eio_inotify.read inotify in
print_endline (Inotify.string_of_event event)
(* watch=1 cookie=0 events=CREATE "file" *)
```

Note that Lwt-style & Eio-style interfaces returns events one-by-one, but the Unix-style one returns
them in small batches.

Documentation
Expand Down
17 changes: 16 additions & 1 deletion dune-project
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
(lang dune 2.9)
(lang dune 3.9)
(generate_opam_files true)
(version 2.5)

Expand All @@ -25,3 +25,18 @@
(odoc :with-doc))
(depopts
lwt))

(package
(name inotify-eio)
(synopsis "Inotify backend for eio")
(description "Inotify backend for eio")
(depends
base-unix
base-bytes
(fileutils (and :with-test (>= 0.4.4)))
(ounit2 (and :with-test (>= 2.0)))
(ocaml (>= 5.0))
(odoc :with-doc)
eio
(eio_main :with-test)
iomux))
39 changes: 39 additions & 0 deletions inotify-eio.opam
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# This file is generated by dune, edit dune-project instead
opam-version: "2.0"
version: "2.5"
synopsis: "Inotify backend for eio"
description: "Inotify backend for eio"
maintainer: ["whitequark <[email protected]>"]
authors: ["whitequark <[email protected]>"]
license: "LGPL-2.1-only WITH OCaml-LGPL-linking-exception"
homepage: "https://github.com/whitequark/ocaml-inotify"
doc: "https://whitequark.github.io/ocaml-inotify"
bug-reports: "https://github.com/whitequark/ocaml-inotify/issues"
depends: [
"dune" {>= "3.9"}
"base-unix"
"base-bytes"
"fileutils" {with-test & >= "0.4.4"}
"ounit2" {with-test & >= "2.0"}
"ocaml" {>= "5.0"}
"odoc" {with-doc}
"eio"
"eio_main" {with-test}
"iomux"
]
build: [
["dune" "subst"] {dev}
[
"dune"
"build"
"-p"
name
"-j"
jobs
"@install"
"@runtest" {with-test}
"@doc" {with-doc}
]
]
dev-repo: "git+https://github.com/whitequark/ocaml-inotify.git"
available: os="linux"
1 change: 1 addition & 0 deletions inotify-eio.opam.template
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
available: os="linux"
4 changes: 1 addition & 3 deletions inotify.opam
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ homepage: "https://github.com/whitequark/ocaml-inotify"
doc: "https://whitequark.github.io/ocaml-inotify"
bug-reports: "https://github.com/whitequark/ocaml-inotify/issues"
depends: [
"dune" {>= "2.9"}
"dune" {>= "3.9"}
"base-unix"
"base-bytes"
"fileutils" {with-test & >= "0.4.4"}
Expand All @@ -29,12 +29,10 @@ build: [
name
"-j"
jobs
"--promote-install-files=false"
"@install"
"@runtest" {with-test}
"@doc" {with-doc}
]
["dune" "install" "-p" name "--create-install-files" name]
]
dev-repo: "git+https://github.com/whitequark/ocaml-inotify.git"
available: os="linux"
7 changes: 7 additions & 0 deletions lib/dune
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,10 @@
(flags (:standard -w -32-39))
(libraries inotify lwt lwt.unix)
(modules lwt_inotify))

(library
(name eio_inotify)
(public_name inotify-eio)
(flags (:standard -w -32-39))
(libraries inotify eio.unix iomux)
(modules eio_inotify))
46 changes: 46 additions & 0 deletions lib/eio_inotify.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
type t = {
queue : Inotify.event Queue.t;
unix_fd : Unix.file_descr;
poll : Iomux.Poll.t
}

let create () =
try
let unix_fd = Inotify.create () in
let poll = Iomux.Poll.create () in
Iomux.Poll.set_index poll 0 unix_fd (Iomux.Poll.Flags.pollin);
{
queue = Queue.create ();
unix_fd;
poll
}
with exn ->
raise exn

let add_watch inotify path selector =
Inotify.add_watch inotify.unix_fd path selector

let rm_watch inotify wd =
Inotify.rm_watch inotify.unix_fd wd

let rec read inotify =
try
Queue.take inotify.queue
with Queue.Empty ->
Eio_unix.await_readable inotify.unix_fd;
let events = Inotify.read inotify.unix_fd in
List.iter (fun event -> Queue.push event inotify.queue) events;
read inotify

let with_timeout d = Eio.Fiber.first (fun () -> Eio_unix.sleep d; Error `Timeout)

let rec try_read inotify =
try
Some (Queue.take inotify.queue)
with Queue.Empty ->
match Iomux.Poll.ppoll_or_poll inotify.poll (Iomux.Util.fd_of_unix inotify.unix_fd) Nowait with
| r when r > 0 -> Some (read inotify)
| _ -> None

let close inotify =
Unix.close inotify.unix_fd
24 changes: 24 additions & 0 deletions lib/eio_inotify.mli
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
(** An [Lwt] wrapper for {!Inotify} module *)

(** Type of inotify descriptors. *)
type t

(** [create ()] returns a new inotify descriptor. *)
val create : unit -> t

(** [add_watch desc path events] sets up [desc] to watch for [events] occuring
to [path], and returns a watch descriptor. *)
val add_watch : t -> string -> Inotify.selector list -> Inotify.watch

(** [rm_watch desc watch] stops [desc] from watching [watch]. *)
val rm_watch : t -> Inotify.watch -> unit

(** [read desc] waits for an event to occur at [desc]. *)
val read : t -> Inotify.event

(** [try_read desc] returns [Some event] if [desc] has queued events,
or [None] otherwise. *)
val try_read : t -> Inotify.event option

(** [close desc] frees [desc]. *)
val close : t -> unit
18 changes: 12 additions & 6 deletions lib_test/dune
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
(test
(name test_inotify)
(libraries ounit2 inotify fileutils threads)
(modules test_inotify))
(name test_inotify)
(libraries ounit2 inotify fileutils threads)
(modules test_inotify))

(test
(name test_inotify_lwt)
(libraries ounit2 inotify.lwt fileutils)
(modules test_inotify_lwt))
(name test_inotify_lwt)
(libraries ounit2 inotify.lwt fileutils)
(modules test_inotify_lwt))

(test
(name test_inotify_eio)
(build_if %{lib-available:eio_main})
(libraries ounit2 inotify-eio eio_main fileutils)
(modules test_inotify_eio))
46 changes: 46 additions & 0 deletions lib_test/test_inotify_eio.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
open OUnit2

let test_read ctxt =
let tmpdir = bracket_tmpdir ctxt in
Eio_main.run @@ fun _env ->
let inotify = Eio_inotify.create () in
let watch = Eio_inotify.add_watch inotify tmpdir [Inotify.S_Create] in
FileUtil.touch (Printf.sprintf "%s/test" tmpdir);
let result = Eio_inotify.read inotify in
assert_equal (watch, [Inotify.Create], 0l, Some "test") result

let test_try_read ctxt =
let tmpdir = bracket_tmpdir ctxt in
Eio_main.run @@ fun _env ->
let inotify = Eio_inotify.create () in
let watch = Eio_inotify.add_watch inotify tmpdir [Inotify.S_Create] in

let result = Eio_inotify.try_read inotify in
OUnit.assert_equal None result;

FileUtil.touch (Printf.sprintf "%s/test" tmpdir);
let result = Eio_inotify.try_read inotify in
assert_equal (Some (watch, [Inotify.Create], 0l, Some "test")) result;

let result = Eio_inotify.try_read inotify in
OUnit.assert_equal None result

let test_error ctxt =
let tmpdir = bracket_tmpdir ctxt in
Eio_main.run @@ fun _env ->
let inotify = Eio_inotify.create () in
let tmpfile = Printf.sprintf "%s/nonexistent" tmpdir in
try
let _watch = Eio_inotify.add_watch inotify tmpfile [Inotify.S_Modify] in
assert_failure "must raise"
with
| ex -> assert_equal (Unix.Unix_error (Unix.ENOENT, "inotify_add_watch", tmpfile)) ex

let suite = "Test Eio_inotify" >::: [
"Test read" >:: test_read;
"Test try_read" >:: test_try_read;
"Test error handling" >:: test_error;
]

let _ =
run_test_tt_main suite
Loading