mirror of
https://github.com/ocaml-multicore/eio.git
synced 2025-06-23 00:01:19 -04:00
Compare commits
89 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
8f7f82d2c1 | ||
|
d3cb04a67e | ||
|
c78db1a757 | ||
|
4a19b2eea9 | ||
|
fdd2593e33 | ||
|
c39449a631 | ||
|
f195295e3d | ||
|
17665689f5 | ||
|
f26d70d642 | ||
|
730033c0f1 | ||
|
cc0274dd2f | ||
|
b971fa1cca | ||
|
2d9e24a67f | ||
|
ae0eaa2dfd | ||
|
681400c18e | ||
|
b41d4d8a49 | ||
|
9b939abedd | ||
|
61f738df51 | ||
|
eb8fe3ef82 | ||
|
debe35621e | ||
|
dd13303a4a | ||
|
216ccff845 | ||
|
a65fd2e612 | ||
|
f8c9441852 | ||
|
8364428147 | ||
|
d2a3e21bc4 | ||
|
424f5e8c96 | ||
|
89019ddd3d | ||
|
534f89cf0e | ||
|
9ca440ac5c | ||
|
e2dc1d79e5 | ||
|
0c414327d1 | ||
|
0f6b65dea2 | ||
|
cc2cd3da32 | ||
|
d47b5e29f7 | ||
|
c2d314b930 | ||
|
33d4e01a30 | ||
|
c45978252a | ||
|
37840760b1 | ||
|
8cb86a703e | ||
|
12721820e4 | ||
|
32e501a198 | ||
|
a21b507de8 | ||
|
54df8fdd62 | ||
|
2cd09925d0 | ||
|
642bdbef7e | ||
|
574afc103a | ||
|
17562f24fe | ||
|
8ef76f06bf | ||
|
77d881014d | ||
|
e32331a835 | ||
|
3e67f7d4cb | ||
|
3b2a67966e | ||
|
2c5eb612db | ||
|
ab12b0b77c | ||
|
c023b2e750 | ||
|
687017078a | ||
|
2146c8a181 | ||
|
7d718405a6 | ||
|
d834d7391f | ||
|
d26184dbf7 | ||
|
73f913c108 | ||
|
62c1925dfe | ||
|
b126756eb8 | ||
|
3c93b4405c | ||
|
bd2c92e7ba | ||
|
a0cb744256 | ||
|
49c9774ee3 | ||
|
d3f30696c2 | ||
|
c1c2d634de | ||
|
7c9396ef19 | ||
|
58aa3f6663 | ||
|
b8a99fa036 | ||
|
b128edce66 | ||
|
3be614e86f | ||
|
321bc093b4 | ||
|
1776925870 | ||
|
94ab6cb65f | ||
|
c53d897cbd | ||
|
f2ce0c26ed | ||
|
7608cbaa60 | ||
|
911ccc8f6a | ||
|
12530ebeca | ||
|
14ae3cfee3 | ||
|
6ae124ecc5 | ||
|
34b650bd47 | ||
|
240e04f761 | ||
|
15f2047323 | ||
|
9bbd4e0108 |
5
.github/workflows/main.yml
vendored
5
.github/workflows/main.yml
vendored
@ -12,7 +12,7 @@ jobs:
|
|||||||
os:
|
os:
|
||||||
- macos-latest
|
- macos-latest
|
||||||
ocaml-compiler:
|
ocaml-compiler:
|
||||||
- 5.1.x
|
- 5.2.x
|
||||||
local-packages:
|
local-packages:
|
||||||
- eio eio_posix eio_main
|
- eio eio_posix eio_main
|
||||||
|
|
||||||
@ -44,7 +44,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
opam-pin: false
|
opam-pin: false
|
||||||
opam-depext: false
|
opam-depext: false
|
||||||
ocaml-compiler: ocaml.5.1.0,ocaml-option-mingw
|
ocaml-compiler: ocaml.5.2.0,ocaml-option-mingw
|
||||||
opam-repositories: |
|
opam-repositories: |
|
||||||
dra27: https://github.com/dra27/opam-repository.git#windows-5.0
|
dra27: https://github.com/dra27/opam-repository.git#windows-5.0
|
||||||
normal: https://github.com/ocaml/opam-repository.git
|
normal: https://github.com/ocaml/opam-repository.git
|
||||||
@ -57,6 +57,7 @@ jobs:
|
|||||||
- run: opam exec -- dune build
|
- run: opam exec -- dune build
|
||||||
- run: opam exec -- dune runtest
|
- run: opam exec -- dune runtest
|
||||||
- run: opam exec -- dune exec -- ./examples/net/main.exe
|
- run: opam exec -- dune exec -- ./examples/net/main.exe
|
||||||
|
- run: opam exec -- dune exec -- ./examples/fs/main.exe
|
||||||
docker:
|
docker:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
105
CHANGES.md
105
CHANGES.md
@ -1,3 +1,108 @@
|
|||||||
|
## v1.2
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
|
||||||
|
- Make `fork_action.h` a public header (@patricoferris #769, reviewed by @talex5).
|
||||||
|
Allows other libraries to add new actions.
|
||||||
|
|
||||||
|
- Record trace event when spawning processes (@talex5 #749).
|
||||||
|
Spawning a subprocess can take a long time in some cases, so show it clearly in the traces.
|
||||||
|
|
||||||
|
- Eio_unix.Net: make some return types more polymorphic (@talex5 #744).
|
||||||
|
|
||||||
|
Bug fixes:
|
||||||
|
|
||||||
|
- Preserve backtraces in `fork_daemon` (@talex5 #779).
|
||||||
|
|
||||||
|
- Eio.Path: always use "/" as separator (@talex5 #743).
|
||||||
|
|
||||||
|
Linux backend:
|
||||||
|
|
||||||
|
- Allow `alloc_fixed_or_wait` to be cancelled (@talex5 #753).
|
||||||
|
|
||||||
|
- Avoid triggering a (harmless) TSan warning (@talex5 #754, reported by @avsm).
|
||||||
|
|
||||||
|
Windows backend:
|
||||||
|
|
||||||
|
- Unregister FDs on cancel (@talex5 #756).
|
||||||
|
Fixes `exception Unix.Unix_error(Unix.ENOTSOCK, "select", "")`.
|
||||||
|
|
||||||
|
- Work around problems in `Unix.getaddrinfo` (@talex5 #780).
|
||||||
|
Fixes e.g. `No addresses found for host name "127.0.0.1"`.
|
||||||
|
|
||||||
|
- Group `ECONNABORTED` with other connection reset errors (@talex5 #783).
|
||||||
|
|
||||||
|
- Check `has_symlink` for tests (@create2000 #771, reviewed by @patricoferris and @talex5).
|
||||||
|
|
||||||
|
- Improve `openat` error handling (@talex5 #742, reported by @kentookura).
|
||||||
|
Fixes `exception Unix.Unix_error(Unix.ENOENT, "openat", "")`.
|
||||||
|
|
||||||
|
Documentation:
|
||||||
|
|
||||||
|
- examples/fs: show how to read files while scanning (@talex5 #745).
|
||||||
|
|
||||||
|
- Add example to `Buf_read.seq` documentation (@talex5 #739, requested by @darrenldl and @rizo).
|
||||||
|
|
||||||
|
Build and test:
|
||||||
|
|
||||||
|
- Fix tests on OpenBSD (@talex5 #782).
|
||||||
|
|
||||||
|
- Add advice about using AI for code generation (@patricoferris #765, reviewed by @avsm and @talex5).
|
||||||
|
|
||||||
|
- Minor code cleanups (@talex5 #755).
|
||||||
|
|
||||||
|
- Define `struct clone_args` for linux-lts versions that don't have it (@copy #741, reviewed by @talex5).
|
||||||
|
|
||||||
|
- eio_linux: refactor fixed buffer code (@talex5 #752).
|
||||||
|
|
||||||
|
## v1.1
|
||||||
|
|
||||||
|
New features:
|
||||||
|
|
||||||
|
- Add `Eio.Path.symlink` (@patricoferris #715, reviewed by @talex5).
|
||||||
|
|
||||||
|
- Add `Eio.Pool.use ~never_block` (@SGrondin #657, reviewed by @talex5).
|
||||||
|
|
||||||
|
- Add `Eio_unix.Net.import_socket_listening` (@alyssais #733).
|
||||||
|
|
||||||
|
- Add `Eio.Time.Timeout.sleep` (@talex5 #726).
|
||||||
|
|
||||||
|
Documentation:
|
||||||
|
|
||||||
|
- Add `examples/fs` showing how to walk a directory tree (@talex5 #730).
|
||||||
|
|
||||||
|
- README: explain that `read_all` reads until shutdown (@talex5 #717, reported by @Wenke-D).
|
||||||
|
|
||||||
|
- Use long dash in README title (@lucperkins #718).
|
||||||
|
|
||||||
|
Linux backend:
|
||||||
|
|
||||||
|
- Require Linux >= 5.15 (@talex5 #720, reviewed by @SGrondin and @avsm).
|
||||||
|
Removes a work-around that required checking whether every flow was a tty.
|
||||||
|
|
||||||
|
- Don't call submit immediately before wait (@talex5 #728).
|
||||||
|
This is slightly faster and makes the traces clearer.
|
||||||
|
|
||||||
|
- Don't record submit events when there's nothing to submit (@talex5 #729).
|
||||||
|
Makes the traces a bit clearer.
|
||||||
|
|
||||||
|
- Split flow into its own file (@talex5 #727).
|
||||||
|
|
||||||
|
- Add work-around for signals race (@talex5 #734).
|
||||||
|
|
||||||
|
POSIX backend:
|
||||||
|
|
||||||
|
- Add `_BSD_SOURCE` flag to fix build on OpenBSD (@prgbln #722).
|
||||||
|
|
||||||
|
- Fix sandboxed path resolution on OpenBSD (@jebrosen #723, reviewed by @talex5).
|
||||||
|
OpenBSD uses `ELOOP` when opening a symlink with `O_NOFOLLOW`.
|
||||||
|
|
||||||
|
Build and test:
|
||||||
|
|
||||||
|
- Benchmarks: record uname, Eio backend, and number of cores (@talex5 #719).
|
||||||
|
|
||||||
|
- Update to MDX 2.4.1 for OCaml 5.2 (@talex5 #712).
|
||||||
|
|
||||||
## v1.0
|
## v1.0
|
||||||
|
|
||||||
New features:
|
New features:
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
FROM ocaml/opam:debian-11-ocaml-5.1
|
FROM ocaml/opam:debian-11-ocaml-5.2
|
||||||
# Make sure we're using opam-2.1:
|
# Make sure we're using opam-2.1:
|
||||||
RUN sudo ln -sf /usr/bin/opam-2.1 /usr/bin/opam
|
RUN sudo ln -sf /usr/bin/opam-2.1 /usr/bin/opam
|
||||||
# Ensure opam-repository is up-to-date:
|
# Ensure opam-repository is up-to-date:
|
||||||
RUN cd opam-repository && git pull -q origin 0ac3fc79fd11ee365dd46119d43e9763cf57da52 && opam update
|
RUN cd opam-repository && git pull -q origin 97de3378749cf8d2d70a5d710d310e5cc17c9dea && opam update
|
||||||
# Install utop for interactive use:
|
# Install utop for interactive use:
|
||||||
RUN opam install utop fmt
|
RUN opam install utop fmt
|
||||||
# Install Eio's dependencies (adding just the opam files first to help with caching):
|
# Install Eio's dependencies (adding just the opam files first to help with caching):
|
||||||
|
11
HACKING.md
11
HACKING.md
@ -135,4 +135,15 @@ Try to avoid making unnecessary changes; this makes review harder and clutters u
|
|||||||
`ocamlformat` may be useful to get badly messed up code to a baseline unformatted state,
|
`ocamlformat` may be useful to get badly messed up code to a baseline unformatted state,
|
||||||
from which human formatting can be added where needed.
|
from which human formatting can be added where needed.
|
||||||
|
|
||||||
|
## AI-generated Code
|
||||||
|
|
||||||
|
Contributing to Eio should not be done _solely_ using "AI tools" such as ChatGPT. This is for a few reasons:
|
||||||
|
|
||||||
|
1. **It obfuscates how you think**. Purely AI-generated code tells us little about how you think and the problems you might be having. This makes it harder to provide good feedback on PRs and issues.
|
||||||
|
2. **It is often more work to review**. Particularly for the OCaml ecosystem and libraries like Eio, it seems that these tools are not very good and generate a lot of believable code that is in actual fact completely wrong. PR comments and the code submitted with them can say completely different things.
|
||||||
|
3. **It is a grey area for licensing**. Models like ChatGPT have been trained on lots of code with different licenses and has been known to simply copy code as an answer to a prompt. We would like to avoid this headache as best we can.
|
||||||
|
|
||||||
|
Use AI tools, if you wish, to help you understand OCaml and Eio. Do not offload all of the work of a PR or a comment to these tools.
|
||||||
|
|
||||||
[dscheck]: https://github.com/ocaml-multicore/dscheck
|
[dscheck]: https://github.com/ocaml-multicore/dscheck
|
||||||
|
|
||||||
|
109
README.md
109
README.md
@ -1,6 +1,6 @@
|
|||||||
[API reference][Eio API] | [#eio Matrix chat](https://matrix.to/#/#eio:roscidus.com) | [Dev meetings][]
|
[API reference][Eio API] | [#eio Matrix chat](https://matrix.to/#/#eio:roscidus.com) | [Dev meetings][]
|
||||||
|
|
||||||
# Eio -- Effects-Based Parallel IO for OCaml
|
# Eio — Effects-Based Parallel IO for OCaml
|
||||||
|
|
||||||
Eio provides an effects-based direct-style IO stack for OCaml 5.
|
Eio provides an effects-based direct-style IO stack for OCaml 5.
|
||||||
For example, you can use Eio to read and write files, make network connections,
|
For example, you can use Eio to read and write files, make network connections,
|
||||||
@ -15,9 +15,8 @@ Eio replaces existing concurrency libraries such as Lwt
|
|||||||
<!-- vim-markdown-toc GFM -->
|
<!-- vim-markdown-toc GFM -->
|
||||||
|
|
||||||
* [Motivation](#motivation)
|
* [Motivation](#motivation)
|
||||||
* [Current Status](#current-status)
|
* [Eio packages](#eio-packages)
|
||||||
* [Structure of the Code](#structure-of-the-code)
|
* [Getting OCaml](#getting-ocaml)
|
||||||
* [Getting OCaml 5.1](#getting-ocaml-51)
|
|
||||||
* [Getting Eio](#getting-eio)
|
* [Getting Eio](#getting-eio)
|
||||||
* [Running Eio](#running-eio)
|
* [Running Eio](#running-eio)
|
||||||
* [Testing with Mocks](#testing-with-mocks)
|
* [Testing with Mocks](#testing-with-mocks)
|
||||||
@ -80,24 +79,19 @@ Additionally, modern operating systems provide high-performance alternatives to
|
|||||||
For example, Linux's io_uring system has applications write the operations they want to perform to a ring buffer,
|
For example, Linux's io_uring system has applications write the operations they want to perform to a ring buffer,
|
||||||
which Linux handles asynchronously, and Eio can take advantage of this.
|
which Linux handles asynchronously, and Eio can take advantage of this.
|
||||||
|
|
||||||
## Current Status
|
You can always [fall back to using Lwt libraries](#lwt) to provide missing features if necessary.
|
||||||
|
See [Awesome Multicore OCaml][] for links to other projects using Eio.
|
||||||
|
|
||||||
See [Eio 1.0 progress tracking](https://github.com/ocaml-multicore/eio/issues/388) for the current status.
|
## Eio packages
|
||||||
Please try porting your programs to use Eio and submit PRs or open issues when you find problems.
|
|
||||||
Remember that you can always [fall back to using Lwt libraries](#lwt) to provide missing features if necessary.
|
|
||||||
|
|
||||||
See [Awesome Multicore OCaml][] for links to work migrating other projects to Eio.
|
|
||||||
|
|
||||||
## Structure of the Code
|
|
||||||
|
|
||||||
- [Eio][] provides concurrency primitives (promises, etc.) and a high-level, cross-platform OS API.
|
- [Eio][] provides concurrency primitives (promises, etc.) and a high-level, cross-platform OS API.
|
||||||
- [Eio_posix][] provides a cross-platform backend for these APIs for POSIX-type systems.
|
- [Eio_posix][] provides a cross-platform backend for these APIs for POSIX-type systems.
|
||||||
- [Eio_linux][] provides a Linux io-uring backend for these APIs,
|
- [Eio_linux][] provides a Linux io_uring backend for these APIs.
|
||||||
plus a low-level API that can be used directly (in non-portable code).
|
|
||||||
- [Eio_windows][] is for use on Windows (incomplete - [help wanted](https://github.com/ocaml-multicore/eio/issues/125)).
|
- [Eio_windows][] is for use on Windows (incomplete - [help wanted](https://github.com/ocaml-multicore/eio/issues/125)).
|
||||||
- [Eio_main][] selects an appropriate backend (e.g. `eio_linux` or `eio_posix`), depending on your platform.
|
- [Eio_main][] selects an appropriate backend (e.g. `eio_linux` or `eio_posix`), depending on your platform.
|
||||||
|
- [Eio_js][] allows Eio code to run in the browser, using `js_of_ocaml`.
|
||||||
|
|
||||||
## Getting OCaml 5.1
|
## Getting OCaml
|
||||||
|
|
||||||
You'll need OCaml 5.1.0 or later.
|
You'll need OCaml 5.1.0 or later.
|
||||||
You can either install it yourself or build the included [Dockerfile](./Dockerfile).
|
You can either install it yourself or build the included [Dockerfile](./Dockerfile).
|
||||||
@ -109,7 +103,7 @@ To install it yourself:
|
|||||||
2. Use opam to install OCaml:
|
2. Use opam to install OCaml:
|
||||||
|
|
||||||
```
|
```
|
||||||
opam switch create 5.1.1
|
opam switch create 5.2.0
|
||||||
```
|
```
|
||||||
|
|
||||||
## Getting Eio
|
## Getting Eio
|
||||||
@ -135,18 +129,18 @@ prompt and return after each line.)
|
|||||||
# open Eio.Std;;
|
# open Eio.Std;;
|
||||||
```
|
```
|
||||||
|
|
||||||
This function writes a greeting to `stdout` using [Eio.Flow][]:
|
This function writes a greeting to `out` using [Eio.Flow][]:
|
||||||
|
|
||||||
```ocaml
|
```ocaml
|
||||||
let main ~stdout =
|
let main out =
|
||||||
Eio.Flow.copy_string "Hello, world!\n" stdout
|
Eio.Flow.copy_string "Hello, world!\n" out
|
||||||
```
|
```
|
||||||
|
|
||||||
We use [Eio_main.run][] to run the event loop and call `main` from there:
|
We use [Eio_main.run][] to run the event loop and call `main` from there:
|
||||||
|
|
||||||
```ocaml
|
```ocaml
|
||||||
# Eio_main.run @@ fun env ->
|
# Eio_main.run @@ fun env ->
|
||||||
main ~stdout:(Eio.Stdenv.stdout env);;
|
main (Eio.Stdenv.stdout env);;
|
||||||
Hello, world!
|
Hello, world!
|
||||||
- : unit = ()
|
- : unit = ()
|
||||||
```
|
```
|
||||||
@ -156,7 +150,7 @@ Note that:
|
|||||||
- The `env` argument represents the standard environment of a Unix process, allowing it to interact with the outside world.
|
- The `env` argument represents the standard environment of a Unix process, allowing it to interact with the outside world.
|
||||||
A program will typically start by extracting from `env` whatever things the program will need and then calling `main` with them.
|
A program will typically start by extracting from `env` whatever things the program will need and then calling `main` with them.
|
||||||
|
|
||||||
- The type of the `main` function here tells us that this program only interacts via `stdout`.
|
- The type of the `main` function here tells us that this program only interacts via the `out` flow.
|
||||||
|
|
||||||
- `Eio_main.run` automatically calls the appropriate run function for your platform.
|
- `Eio_main.run` automatically calls the appropriate run function for your platform.
|
||||||
For example, on Linux this will call `Eio_linux.run`. For non-portable code you can use the platform-specific library directly.
|
For example, on Linux this will call `Eio_linux.run`. For non-portable code you can use the platform-specific library directly.
|
||||||
@ -171,7 +165,7 @@ For example, instead of giving `main` the real standard output, we can have it w
|
|||||||
```ocaml
|
```ocaml
|
||||||
# Eio_main.run @@ fun _env ->
|
# Eio_main.run @@ fun _env ->
|
||||||
let buffer = Buffer.create 20 in
|
let buffer = Buffer.create 20 in
|
||||||
main ~stdout:(Eio.Flow.buffer_sink buffer);
|
main (Eio.Flow.buffer_sink buffer);
|
||||||
traceln "Main would print %S" (Buffer.contents buffer);;
|
traceln "Main would print %S" (Buffer.contents buffer);;
|
||||||
+Main would print "Hello, world!\n"
|
+Main would print "Hello, world!\n"
|
||||||
- : unit = ()
|
- : unit = ()
|
||||||
@ -185,8 +179,7 @@ The [Eio_mock][] library provides some convenient pre-built mocks:
|
|||||||
```ocaml
|
```ocaml
|
||||||
# #require "eio.mock";;
|
# #require "eio.mock";;
|
||||||
# Eio_main.run @@ fun _env ->
|
# Eio_main.run @@ fun _env ->
|
||||||
let mock_stdout = Eio_mock.Flow.make "mock-stdout" in
|
main (Eio_mock.Flow.make "mock-stdout");;
|
||||||
main ~stdout:mock_stdout;;
|
|
||||||
+mock-stdout: wrote "Hello, world!\n"
|
+mock-stdout: wrote "Hello, world!\n"
|
||||||
- : unit = ()
|
- : unit = ()
|
||||||
```
|
```
|
||||||
@ -238,12 +231,7 @@ The green segments show when each fiber is running.
|
|||||||
Note that the output from `traceln` appears in the trace as well as on the console.
|
Note that the output from `traceln` appears in the trace as well as on the console.
|
||||||
In the eio-trace window, scrolling with the mouse or touchpad will zoom in or out of the diagram.
|
In the eio-trace window, scrolling with the mouse or touchpad will zoom in or out of the diagram.
|
||||||
|
|
||||||
There are various third-party tools that can also consume this data
|
Third-party tools, such as [Olly][], can also consume this data.
|
||||||
(but may currently require patches to support the new system):
|
|
||||||
|
|
||||||
- [Meio][] (Monitoring for Eio) provides an interactive console-based UI for exploring running fibers.
|
|
||||||
- [Olly][] can save Perfetto traces and report statistics.
|
|
||||||
|
|
||||||
[examples/trace](./examples/trace/) shows how to consume the events manually.
|
[examples/trace](./examples/trace/) shows how to consume the events manually.
|
||||||
|
|
||||||
## Cancellation
|
## Cancellation
|
||||||
@ -317,22 +305,23 @@ For example:
|
|||||||
```ocaml
|
```ocaml
|
||||||
# Eio_main.run @@ fun _env ->
|
# Eio_main.run @@ fun _env ->
|
||||||
Switch.run (fun sw ->
|
Switch.run (fun sw ->
|
||||||
Fiber.fork ~sw
|
for i = 1 to 3 do
|
||||||
(fun () -> for i = 1 to 3 do traceln "i = %d" i; Fiber.yield () done);
|
Fiber.fork ~sw (fun () ->
|
||||||
traceln "First thread forked";
|
traceln "Job %d starting" i;
|
||||||
Fiber.fork ~sw
|
Fiber.yield ();
|
||||||
(fun () -> for j = 1 to 3 do traceln "j = %d" j; Fiber.yield () done);
|
traceln "%d done" i;
|
||||||
traceln "Second thread forked; top-level code is finished"
|
);
|
||||||
);
|
done;
|
||||||
|
traceln "All child fibers forked";
|
||||||
|
);
|
||||||
traceln "Switch is finished";;
|
traceln "Switch is finished";;
|
||||||
+i = 1
|
+Job 1 starting
|
||||||
+First thread forked
|
+Job 2 starting
|
||||||
+j = 1
|
+Job 3 starting
|
||||||
+Second thread forked; top-level code is finished
|
+All child fibers forked
|
||||||
+i = 2
|
+1 done
|
||||||
+j = 2
|
+2 done
|
||||||
+i = 3
|
+3 done
|
||||||
+j = 3
|
|
||||||
+Switch is finished
|
+Switch is finished
|
||||||
- : unit = ()
|
- : unit = ()
|
||||||
```
|
```
|
||||||
@ -397,6 +386,7 @@ let run_client ~net ~addr =
|
|||||||
Switch.run ~name:"client" @@ fun sw ->
|
Switch.run ~name:"client" @@ fun sw ->
|
||||||
traceln "Client: connecting to server";
|
traceln "Client: connecting to server";
|
||||||
let flow = Eio.Net.connect ~sw net addr in
|
let flow = Eio.Net.connect ~sw net addr in
|
||||||
|
(* Read all data until end-of-stream (shutdown): *)
|
||||||
traceln "Client: received %S" (Eio.Flow.read_all flow)
|
traceln "Client: received %S" (Eio.Flow.read_all flow)
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -934,6 +924,9 @@ The mock backend provides a mock clock that advances automatically where there i
|
|||||||
- : unit = ()
|
- : unit = ()
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Note: You could also just use `Eio_unix.sleep 5.0` if you don't want to pass a clock around.
|
||||||
|
This is especially useful if you need to insert a delay for some quick debugging.
|
||||||
|
|
||||||
## Multicore Support
|
## Multicore Support
|
||||||
|
|
||||||
OCaml allows a program to create multiple *domains* in which to run code, allowing multiple CPUs to be used at once.
|
OCaml allows a program to create multiple *domains* in which to run code, allowing multiple CPUs to be used at once.
|
||||||
@ -1017,12 +1010,8 @@ Usually you will only want one pool for an entire application, so the pool is ty
|
|||||||
let () =
|
let () =
|
||||||
Eio_main.run @@ fun env ->
|
Eio_main.run @@ fun env ->
|
||||||
Switch.run @@ fun sw ->
|
Switch.run @@ fun sw ->
|
||||||
let pool =
|
let dm = Eio.Stdenv.domain_mgr env in
|
||||||
Eio.Executor_pool.create
|
main ~pool:(Eio.Executor_pool.create ~sw ~domain_count:2 dm)
|
||||||
~sw (Eio.Stdenv.domain_mgr env)
|
|
||||||
~domain_count:4
|
|
||||||
in
|
|
||||||
main ~pool
|
|
||||||
```
|
```
|
||||||
|
|
||||||
The pool starts its domain workers immediately upon creation.
|
The pool starts its domain workers immediately upon creation.
|
||||||
@ -1036,11 +1025,7 @@ The total number of domains should not exceed `Domain.recommended_domain_count`
|
|||||||
We can run the previous example using an Executor Pool like this:
|
We can run the previous example using an Executor Pool like this:
|
||||||
|
|
||||||
```ocaml
|
```ocaml
|
||||||
let main ~domain_mgr =
|
let main ~pool =
|
||||||
Switch.run @@ fun sw ->
|
|
||||||
let pool =
|
|
||||||
Eio.Executor_pool.create ~sw domain_mgr ~domain_count:4
|
|
||||||
in
|
|
||||||
let test n =
|
let test n =
|
||||||
traceln "sum 1..%d = %d" n
|
traceln "sum 1..%d = %d" n
|
||||||
(Eio.Executor_pool.submit_exn pool ~weight:1.0
|
(Eio.Executor_pool.submit_exn pool ~weight:1.0
|
||||||
@ -1054,7 +1039,9 @@ let main ~domain_mgr =
|
|||||||
<!-- $MDX non-deterministic=output -->
|
<!-- $MDX non-deterministic=output -->
|
||||||
```ocaml
|
```ocaml
|
||||||
# Eio_main.run @@ fun env ->
|
# Eio_main.run @@ fun env ->
|
||||||
main ~domain_mgr:(Eio.Stdenv.domain_mgr env);;
|
Switch.run @@ fun sw ->
|
||||||
|
let dm = Eio.Stdenv.domain_mgr env in
|
||||||
|
main ~pool:(Eio.Executor_pool.create ~sw ~domain_count:2 dm)
|
||||||
+Starting CPU-intensive task...
|
+Starting CPU-intensive task...
|
||||||
+Starting CPU-intensive task...
|
+Starting CPU-intensive task...
|
||||||
+Finished
|
+Finished
|
||||||
@ -1593,6 +1580,9 @@ In particular, if you test your code by providing (deterministic) mocks then the
|
|||||||
An easy way to write tests is by having the mocks call `traceln` and then comparing the trace output with the expected output.
|
An easy way to write tests is by having the mocks call `traceln` and then comparing the trace output with the expected output.
|
||||||
See Eio's own tests for examples, e.g., [tests/switch.md](tests/switch.md).
|
See Eio's own tests for examples, e.g., [tests/switch.md](tests/switch.md).
|
||||||
|
|
||||||
|
Note: this only applies to the high-level APIs in the `Eio` module.
|
||||||
|
Programs can behave non-deterministically when using `Eio_unix` or the various `Low_level` APIs provided by the backends.
|
||||||
|
|
||||||
## Provider Interfaces
|
## Provider Interfaces
|
||||||
|
|
||||||
Eio applications use resources by calling functions (such as `Eio.Flow.write`).
|
Eio applications use resources by calling functions (such as `Eio.Flow.write`).
|
||||||
@ -1631,7 +1621,8 @@ It can then be used like any other Eio flow:
|
|||||||
## Example Applications
|
## Example Applications
|
||||||
|
|
||||||
- [gemini-eio][] is a simple Gemini browser. It shows how to integrate Eio with `ocaml-tls` and `notty`.
|
- [gemini-eio][] is a simple Gemini browser. It shows how to integrate Eio with `ocaml-tls` and `notty`.
|
||||||
- [ocaml-multicore/retro-httpaf-bench](https://github.com/ocaml-multicore/retro-httpaf-bench) includes a simple HTTP server using Eio. It shows how to use Eio with `httpaf`, and how to use multiple domains for increased performance.
|
- [cohttp-eio/examples](https://github.com/mirage/ocaml-cohttp/tree/master/cohttp-eio/examples) shows how to use Eio with HTTP.
|
||||||
|
- [capnp-rpc](https://github.com/mirage/capnp-rpc) shows how to use Eio with Cap'n Proto.
|
||||||
- [Awesome Multicore OCaml][] lists many other projects.
|
- [Awesome Multicore OCaml][] lists many other projects.
|
||||||
|
|
||||||
## Integrations
|
## Integrations
|
||||||
@ -1905,10 +1896,10 @@ Some background about the effects system can be found in:
|
|||||||
[Eio.Condition]: https://ocaml-multicore.github.io/eio/eio/Eio/Condition/index.html
|
[Eio.Condition]: https://ocaml-multicore.github.io/eio/eio/Eio/Condition/index.html
|
||||||
[Domainslib]: https://github.com/ocaml-multicore/domainslib
|
[Domainslib]: https://github.com/ocaml-multicore/domainslib
|
||||||
[kcas]: https://github.com/ocaml-multicore/kcas
|
[kcas]: https://github.com/ocaml-multicore/kcas
|
||||||
[Meio]: https://github.com/tarides/meio
|
|
||||||
[Lambda Capabilities]: https://roscidus.com/blog/blog/2023/04/26/lambda-capabilities/
|
[Lambda Capabilities]: https://roscidus.com/blog/blog/2023/04/26/lambda-capabilities/
|
||||||
[Eio.Process]: https://ocaml-multicore.github.io/eio/eio/Eio/Process/index.html
|
[Eio.Process]: https://ocaml-multicore.github.io/eio/eio/Eio/Process/index.html
|
||||||
[Dev meetings]: https://docs.google.com/document/d/1ZBfbjAkvEkv9ldumpZV5VXrEc_HpPeYjHPW_TiwJe4Q
|
[Dev meetings]: https://docs.google.com/document/d/1ZBfbjAkvEkv9ldumpZV5VXrEc_HpPeYjHPW_TiwJe4Q
|
||||||
[Olly]: https://github.com/tarides/runtime_events_tools
|
[Olly]: https://github.com/tarides/runtime_events_tools
|
||||||
[eio-trace]: https://github.com/ocaml-multicore/eio-trace
|
[eio-trace]: https://github.com/ocaml-multicore/eio-trace
|
||||||
[cap_enter]: https://man.freebsd.org/cgi/man.cgi?query=cap_enter
|
[cap_enter]: https://man.freebsd.org/cgi/man.cgi?query=cap_enter
|
||||||
|
[eio_js]: https://github.com/ocaml-multicore/eio_js
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
FROM ocaml/opam:debian-11-ocaml-5.1
|
FROM ocaml/opam:debian-11-ocaml-5.2
|
||||||
# Make sure we're using opam-2.1:
|
# Make sure we're using opam-2.1:
|
||||||
RUN sudo ln -sf /usr/bin/opam-2.1 /usr/bin/opam
|
RUN sudo ln -sf /usr/bin/opam-2.1 /usr/bin/opam
|
||||||
# Ensure opam-repository is up-to-date:
|
# Ensure opam-repository is up-to-date:
|
||||||
RUN cd opam-repository && git pull -q origin 0ac3fc79fd11ee365dd46119d43e9763cf57da52 && opam update
|
RUN cd opam-repository && git pull -q origin 97de3378749cf8d2d70a5d710d310e5cc17c9dea && opam update
|
||||||
# Install Eio's dependencies (adding just the opam files first to help with caching):
|
# Install Eio's dependencies (adding just the opam files first to help with caching):
|
||||||
RUN mkdir eio
|
RUN mkdir eio
|
||||||
WORKDIR eio
|
WORKDIR eio
|
||||||
|
@ -45,6 +45,19 @@ let () =
|
|||||||
"metrics", `List metrics;
|
"metrics", `List metrics;
|
||||||
]
|
]
|
||||||
in
|
in
|
||||||
|
(* The benchmark machine runs an old Docker that blocks pidfd_open *)
|
||||||
|
(* let uname = Eio.Process.parse_out env#process_mgr Eio.Buf_read.take_all ["uname"; "-a"] in *)
|
||||||
|
let uname =
|
||||||
|
let ch = Unix.open_process_in "uname -a" in
|
||||||
|
let x = input_line ch in
|
||||||
|
close_in ch;
|
||||||
|
x
|
||||||
|
in
|
||||||
Fmt.pr "%a@." (Yojson.Safe.pretty_print ~std:true) @@ `Assoc [
|
Fmt.pr "%a@." (Yojson.Safe.pretty_print ~std:true) @@ `Assoc [
|
||||||
|
"config", `Assoc [
|
||||||
|
"uname", `String uname;
|
||||||
|
"backend", `String env#backend_id;
|
||||||
|
"recommended_domain_count", `Int (Domain.recommended_domain_count ());
|
||||||
|
];
|
||||||
"results", `List (List.map run benchmarks);
|
"results", `List (List.map run benchmarks);
|
||||||
]
|
]
|
||||||
|
Binary file not shown.
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1280pt" height="152pt" viewBox="0 0 1280 152" version="1.1">
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1280pt" height="184pt" viewBox="0 0 1280 184" version="1.1">
|
||||||
<defs>
|
<defs>
|
||||||
<g>
|
<g>
|
||||||
<symbol overflow="visible" id="glyph0-0">
|
<symbol overflow="visible" id="glyph0-0">
|
||||||
@ -81,489 +81,450 @@
|
|||||||
<path style="stroke:none;" d="M 0.40625 1.421875 L 0.40625 -5.640625 L 4.40625 -5.640625 L 4.40625 1.421875 Z M 0.84375 0.96875 L 3.953125 0.96875 L 3.953125 -5.1875 L 0.84375 -5.1875 Z M 0.84375 0.96875 "/>
|
<path style="stroke:none;" d="M 0.40625 1.421875 L 0.40625 -5.640625 L 4.40625 -5.640625 L 4.40625 1.421875 Z M 0.84375 0.96875 L 3.953125 0.96875 L 3.953125 -5.1875 L 0.84375 -5.1875 Z M 0.84375 0.96875 "/>
|
||||||
</symbol>
|
</symbol>
|
||||||
<symbol overflow="visible" id="glyph1-1">
|
<symbol overflow="visible" id="glyph1-1">
|
||||||
<path style="stroke:none;" d="M 0.75 -4.375 L 1.46875 -4.375 L 1.46875 0 L 0.75 0 Z M 0.75 -6.078125 L 1.46875 -6.078125 L 1.46875 -5.171875 L 0.75 -5.171875 Z M 0.75 -6.078125 "/>
|
<path style="stroke:none;" d="M 0.78125 -5.828125 L 1.578125 -5.828125 L 1.578125 -0.40625 C 1.578125 0.300781 1.441406 0.8125 1.171875 1.125 C 0.910156 1.445312 0.484375 1.609375 -0.109375 1.609375 L -0.421875 1.609375 L -0.421875 0.9375 L -0.171875 0.9375 C 0.179688 0.9375 0.425781 0.835938 0.5625 0.640625 C 0.707031 0.453125 0.78125 0.101562 0.78125 -0.40625 Z M 0.78125 -5.828125 "/>
|
||||||
</symbol>
|
</symbol>
|
||||||
<symbol overflow="visible" id="glyph1-2">
|
<symbol overflow="visible" id="glyph1-2">
|
||||||
<path style="stroke:none;" d=""/>
|
|
||||||
</symbol>
|
|
||||||
<symbol overflow="visible" id="glyph1-3">
|
|
||||||
<path style="stroke:none;" d="M 0.84375 -3.640625 L 5.859375 -3.640625 L 5.859375 -2.984375 L 0.84375 -2.984375 Z M 0.84375 -2.046875 L 5.859375 -2.046875 L 5.859375 -1.375 L 0.84375 -1.375 Z M 0.84375 -2.046875 "/>
|
|
||||||
</symbol>
|
|
||||||
<symbol overflow="visible" id="glyph1-4">
|
|
||||||
<path style="stroke:none;" d="M 1 -0.671875 L 2.28125 -0.671875 L 2.28125 -5.109375 L 0.875 -4.828125 L 0.875 -5.546875 L 2.28125 -5.828125 L 3.0625 -5.828125 L 3.0625 -0.671875 L 4.359375 -0.671875 L 4.359375 0 L 1 0 Z M 1 -0.671875 "/>
|
|
||||||
</symbol>
|
|
||||||
<symbol overflow="visible" id="glyph1-5">
|
|
||||||
<path style="stroke:none;" d="M 1.53125 -0.671875 L 4.296875 -0.671875 L 4.296875 0 L 0.59375 0 L 0.59375 -0.671875 C 0.882812 -0.972656 1.289062 -1.382812 1.8125 -1.90625 C 2.332031 -2.4375 2.65625 -2.773438 2.78125 -2.921875 C 3.039062 -3.203125 3.21875 -3.441406 3.3125 -3.640625 C 3.414062 -3.835938 3.46875 -4.03125 3.46875 -4.21875 C 3.46875 -4.53125 3.359375 -4.785156 3.140625 -4.984375 C 2.921875 -5.179688 2.640625 -5.28125 2.296875 -5.28125 C 2.046875 -5.28125 1.78125 -5.234375 1.5 -5.140625 C 1.226562 -5.054688 0.9375 -4.925781 0.625 -4.75 L 0.625 -5.546875 C 0.945312 -5.679688 1.242188 -5.78125 1.515625 -5.84375 C 1.796875 -5.90625 2.050781 -5.9375 2.28125 -5.9375 C 2.882812 -5.9375 3.363281 -5.785156 3.71875 -5.484375 C 4.082031 -5.179688 4.265625 -4.78125 4.265625 -4.28125 C 4.265625 -4.039062 4.21875 -3.8125 4.125 -3.59375 C 4.03125 -3.375 3.867188 -3.117188 3.640625 -2.828125 C 3.566406 -2.753906 3.351562 -2.535156 3 -2.171875 C 2.65625 -1.816406 2.164062 -1.316406 1.53125 -0.671875 Z M 1.53125 -0.671875 "/>
|
|
||||||
</symbol>
|
|
||||||
<symbol overflow="visible" id="glyph1-6">
|
|
||||||
<path style="stroke:none;" d="M 3.25 -3.140625 C 3.625 -3.066406 3.914062 -2.898438 4.125 -2.640625 C 4.34375 -2.390625 4.453125 -2.078125 4.453125 -1.703125 C 4.453125 -1.117188 4.253906 -0.671875 3.859375 -0.359375 C 3.460938 -0.046875 2.898438 0.109375 2.171875 0.109375 C 1.921875 0.109375 1.664062 0.0820312 1.40625 0.03125 C 1.15625 -0.0078125 0.890625 -0.078125 0.609375 -0.171875 L 0.609375 -0.9375 C 0.828125 -0.8125 1.066406 -0.710938 1.328125 -0.640625 C 1.585938 -0.578125 1.859375 -0.546875 2.140625 -0.546875 C 2.640625 -0.546875 3.019531 -0.644531 3.28125 -0.84375 C 3.539062 -1.039062 3.671875 -1.328125 3.671875 -1.703125 C 3.671875 -2.046875 3.546875 -2.3125 3.296875 -2.5 C 3.054688 -2.695312 2.722656 -2.796875 2.296875 -2.796875 L 1.625 -2.796875 L 1.625 -3.4375 L 2.328125 -3.4375 C 2.710938 -3.4375 3.007812 -3.515625 3.21875 -3.671875 C 3.425781 -3.828125 3.53125 -4.050781 3.53125 -4.34375 C 3.53125 -4.644531 3.421875 -4.875 3.203125 -5.03125 C 2.992188 -5.195312 2.691406 -5.28125 2.296875 -5.28125 C 2.078125 -5.28125 1.84375 -5.253906 1.59375 -5.203125 C 1.351562 -5.160156 1.082031 -5.085938 0.78125 -4.984375 L 0.78125 -5.6875 C 1.082031 -5.769531 1.363281 -5.832031 1.625 -5.875 C 1.882812 -5.914062 2.132812 -5.9375 2.375 -5.9375 C 2.96875 -5.9375 3.4375 -5.800781 3.78125 -5.53125 C 4.132812 -5.257812 4.3125 -4.890625 4.3125 -4.421875 C 4.3125 -4.097656 4.21875 -3.828125 4.03125 -3.609375 C 3.851562 -3.390625 3.59375 -3.234375 3.25 -3.140625 Z M 3.25 -3.140625 "/>
|
|
||||||
</symbol>
|
|
||||||
<symbol overflow="visible" id="glyph1-7">
|
|
||||||
<path style="stroke:none;" d="M 0.78125 -5.828125 L 4.140625 -5.828125 L 4.140625 -5.171875 L 1.578125 -5.171875 L 1.578125 -3.453125 L 3.890625 -3.453125 L 3.890625 -2.78125 L 1.578125 -2.78125 L 1.578125 0 L 0.78125 0 Z M 0.78125 -5.828125 "/>
|
|
||||||
</symbol>
|
|
||||||
<symbol overflow="visible" id="glyph1-8">
|
|
||||||
<path style="stroke:none;" d="M 3.296875 -3.703125 C 3.210938 -3.753906 3.125 -3.789062 3.03125 -3.8125 C 2.9375 -3.832031 2.832031 -3.84375 2.71875 -3.84375 C 2.3125 -3.84375 2 -3.707031 1.78125 -3.4375 C 1.5625 -3.175781 1.453125 -2.800781 1.453125 -2.3125 L 1.453125 0 L 0.734375 0 L 0.734375 -4.375 L 1.453125 -4.375 L 1.453125 -3.703125 C 1.597656 -3.960938 1.789062 -4.15625 2.03125 -4.28125 C 2.28125 -4.414062 2.578125 -4.484375 2.921875 -4.484375 C 2.972656 -4.484375 3.023438 -4.476562 3.078125 -4.46875 C 3.140625 -4.46875 3.207031 -4.457031 3.28125 -4.4375 Z M 3.296875 -3.703125 "/>
|
|
||||||
</symbol>
|
|
||||||
<symbol overflow="visible" id="glyph1-9">
|
|
||||||
<path style="stroke:none;" d="M 3.546875 -4.25 L 3.546875 -3.5625 C 3.335938 -3.664062 3.125 -3.742188 2.90625 -3.796875 C 2.6875 -3.847656 2.460938 -3.875 2.234375 -3.875 C 1.878906 -3.875 1.609375 -3.816406 1.421875 -3.703125 C 1.242188 -3.597656 1.15625 -3.4375 1.15625 -3.21875 C 1.15625 -3.050781 1.21875 -2.921875 1.34375 -2.828125 C 1.476562 -2.734375 1.738281 -2.644531 2.125 -2.5625 L 2.375 -2.5 C 2.882812 -2.394531 3.242188 -2.242188 3.453125 -2.046875 C 3.671875 -1.847656 3.78125 -1.566406 3.78125 -1.203125 C 3.78125 -0.796875 3.617188 -0.472656 3.296875 -0.234375 C 2.972656 -0.00390625 2.53125 0.109375 1.96875 0.109375 C 1.738281 0.109375 1.492188 0.0820312 1.234375 0.03125 C 0.984375 -0.0078125 0.71875 -0.0703125 0.4375 -0.15625 L 0.4375 -0.90625 C 0.695312 -0.769531 0.957031 -0.664062 1.21875 -0.59375 C 1.476562 -0.519531 1.734375 -0.484375 1.984375 -0.484375 C 2.328125 -0.484375 2.585938 -0.539062 2.765625 -0.65625 C 2.953125 -0.78125 3.046875 -0.945312 3.046875 -1.15625 C 3.046875 -1.351562 2.976562 -1.503906 2.84375 -1.609375 C 2.707031 -1.710938 2.421875 -1.8125 1.984375 -1.90625 L 1.734375 -1.96875 C 1.285156 -2.0625 0.960938 -2.203125 0.765625 -2.390625 C 0.566406 -2.585938 0.46875 -2.851562 0.46875 -3.1875 C 0.46875 -3.601562 0.613281 -3.921875 0.90625 -4.140625 C 1.195312 -4.367188 1.609375 -4.484375 2.140625 -4.484375 C 2.410156 -4.484375 2.660156 -4.460938 2.890625 -4.421875 C 3.128906 -4.378906 3.347656 -4.320312 3.546875 -4.25 Z M 3.546875 -4.25 "/>
|
|
||||||
</symbol>
|
|
||||||
<symbol overflow="visible" id="glyph1-10">
|
|
||||||
<path style="stroke:none;" d="M 1.46875 -5.625 L 1.46875 -4.375 L 2.953125 -4.375 L 2.953125 -3.8125 L 1.46875 -3.8125 L 1.46875 -1.4375 C 1.46875 -1.082031 1.515625 -0.851562 1.609375 -0.75 C 1.710938 -0.65625 1.910156 -0.609375 2.203125 -0.609375 L 2.953125 -0.609375 L 2.953125 0 L 2.203125 0 C 1.648438 0 1.269531 -0.101562 1.0625 -0.3125 C 0.851562 -0.519531 0.75 -0.894531 0.75 -1.4375 L 0.75 -3.8125 L 0.21875 -3.8125 L 0.21875 -4.375 L 0.75 -4.375 L 0.75 -5.625 Z M 1.46875 -5.625 "/>
|
|
||||||
</symbol>
|
|
||||||
<symbol overflow="visible" id="glyph1-11">
|
|
||||||
<path style="stroke:none;" d="M 4.390625 -2.640625 L 4.390625 0 L 3.671875 0 L 3.671875 -2.625 C 3.671875 -3.03125 3.585938 -3.335938 3.421875 -3.546875 C 3.265625 -3.753906 3.023438 -3.859375 2.703125 -3.859375 C 2.316406 -3.859375 2.007812 -3.734375 1.78125 -3.484375 C 1.5625 -3.234375 1.453125 -2.894531 1.453125 -2.46875 L 1.453125 0 L 0.734375 0 L 0.734375 -6.078125 L 1.453125 -6.078125 L 1.453125 -3.703125 C 1.617188 -3.960938 1.816406 -4.15625 2.046875 -4.28125 C 2.285156 -4.414062 2.554688 -4.484375 2.859375 -4.484375 C 3.367188 -4.484375 3.75 -4.328125 4 -4.015625 C 4.257812 -3.703125 4.390625 -3.242188 4.390625 -2.640625 Z M 4.390625 -2.640625 "/>
|
|
||||||
</symbol>
|
|
||||||
<symbol overflow="visible" id="glyph1-12">
|
|
||||||
<path style="stroke:none;" d="M 4.5 -2.375 L 4.5 -2.015625 L 1.1875 -2.015625 C 1.21875 -1.523438 1.367188 -1.148438 1.640625 -0.890625 C 1.910156 -0.628906 2.28125 -0.5 2.75 -0.5 C 3.03125 -0.5 3.300781 -0.53125 3.5625 -0.59375 C 3.820312 -0.664062 4.078125 -0.769531 4.328125 -0.90625 L 4.328125 -0.21875 C 4.066406 -0.113281 3.800781 -0.0351562 3.53125 0.015625 C 3.257812 0.078125 2.988281 0.109375 2.71875 0.109375 C 2.019531 0.109375 1.460938 -0.09375 1.046875 -0.5 C 0.640625 -0.90625 0.4375 -1.453125 0.4375 -2.140625 C 0.4375 -2.859375 0.628906 -3.425781 1.015625 -3.84375 C 1.410156 -4.269531 1.9375 -4.484375 2.59375 -4.484375 C 3.175781 -4.484375 3.640625 -4.289062 3.984375 -3.90625 C 4.328125 -3.53125 4.5 -3.019531 4.5 -2.375 Z M 3.78125 -2.578125 C 3.769531 -2.972656 3.65625 -3.285156 3.4375 -3.515625 C 3.226562 -3.753906 2.945312 -3.875 2.59375 -3.875 C 2.195312 -3.875 1.875 -3.757812 1.625 -3.53125 C 1.382812 -3.300781 1.25 -2.984375 1.21875 -2.578125 Z M 3.78125 -2.578125 "/>
|
|
||||||
</symbol>
|
|
||||||
<symbol overflow="visible" id="glyph1-13">
|
|
||||||
<path style="stroke:none;" d="M 2.75 -2.203125 C 2.164062 -2.203125 1.757812 -2.132812 1.53125 -2 C 1.3125 -1.863281 1.203125 -1.640625 1.203125 -1.328125 C 1.203125 -1.066406 1.285156 -0.859375 1.453125 -0.703125 C 1.617188 -0.554688 1.847656 -0.484375 2.140625 -0.484375 C 2.535156 -0.484375 2.851562 -0.625 3.09375 -0.90625 C 3.332031 -1.195312 3.453125 -1.578125 3.453125 -2.046875 L 3.453125 -2.203125 Z M 4.171875 -2.5 L 4.171875 0 L 3.453125 0 L 3.453125 -0.671875 C 3.296875 -0.398438 3.09375 -0.203125 2.84375 -0.078125 C 2.601562 0.046875 2.304688 0.109375 1.953125 0.109375 C 1.503906 0.109375 1.144531 -0.015625 0.875 -0.265625 C 0.613281 -0.515625 0.484375 -0.851562 0.484375 -1.28125 C 0.484375 -1.769531 0.644531 -2.140625 0.96875 -2.390625 C 1.300781 -2.640625 1.796875 -2.765625 2.453125 -2.765625 L 3.453125 -2.765625 L 3.453125 -2.828125 C 3.453125 -3.160156 3.34375 -3.414062 3.125 -3.59375 C 2.914062 -3.78125 2.613281 -3.875 2.21875 -3.875 C 1.96875 -3.875 1.722656 -3.84375 1.484375 -3.78125 C 1.242188 -3.71875 1.015625 -3.628906 0.796875 -3.515625 L 0.796875 -4.171875 C 1.066406 -4.273438 1.320312 -4.351562 1.5625 -4.40625 C 1.8125 -4.457031 2.054688 -4.484375 2.296875 -4.484375 C 2.921875 -4.484375 3.390625 -4.316406 3.703125 -3.984375 C 4.015625 -3.660156 4.171875 -3.164062 4.171875 -2.5 Z M 4.171875 -2.5 "/>
|
|
||||||
</symbol>
|
|
||||||
<symbol overflow="visible" id="glyph1-14">
|
|
||||||
<path style="stroke:none;" d="M 3.640625 -3.71875 L 3.640625 -6.078125 L 4.359375 -6.078125 L 4.359375 0 L 3.640625 0 L 3.640625 -0.65625 C 3.484375 -0.394531 3.289062 -0.203125 3.0625 -0.078125 C 2.832031 0.046875 2.554688 0.109375 2.234375 0.109375 C 1.703125 0.109375 1.269531 -0.0976562 0.9375 -0.515625 C 0.601562 -0.941406 0.4375 -1.5 0.4375 -2.1875 C 0.4375 -2.875 0.601562 -3.425781 0.9375 -3.84375 C 1.269531 -4.269531 1.703125 -4.484375 2.234375 -4.484375 C 2.554688 -4.484375 2.832031 -4.421875 3.0625 -4.296875 C 3.289062 -4.171875 3.484375 -3.976562 3.640625 -3.71875 Z M 1.1875 -2.1875 C 1.1875 -1.65625 1.296875 -1.238281 1.515625 -0.9375 C 1.734375 -0.632812 2.03125 -0.484375 2.40625 -0.484375 C 2.789062 -0.484375 3.09375 -0.632812 3.3125 -0.9375 C 3.53125 -1.238281 3.640625 -1.65625 3.640625 -2.1875 C 3.640625 -2.71875 3.53125 -3.128906 3.3125 -3.421875 C 3.09375 -3.722656 2.789062 -3.875 2.40625 -3.875 C 2.03125 -3.875 1.734375 -3.722656 1.515625 -3.421875 C 1.296875 -3.128906 1.1875 -2.71875 1.1875 -2.1875 Z M 1.1875 -2.1875 "/>
|
|
||||||
</symbol>
|
|
||||||
<symbol overflow="visible" id="glyph1-15">
|
|
||||||
<path style="stroke:none;" d="M 2.96875 -6.078125 L 2.96875 -5.484375 L 2.28125 -5.484375 C 2.019531 -5.484375 1.835938 -5.429688 1.734375 -5.328125 C 1.640625 -5.222656 1.59375 -5.035156 1.59375 -4.765625 L 1.59375 -4.375 L 2.78125 -4.375 L 2.78125 -3.8125 L 1.59375 -3.8125 L 1.59375 0 L 0.875 0 L 0.875 -3.8125 L 0.1875 -3.8125 L 0.1875 -4.375 L 0.875 -4.375 L 0.875 -4.6875 C 0.875 -5.164062 0.984375 -5.515625 1.203125 -5.734375 C 1.429688 -5.960938 1.796875 -6.078125 2.296875 -6.078125 Z M 2.96875 -6.078125 "/>
|
|
||||||
</symbol>
|
|
||||||
<symbol overflow="visible" id="glyph1-16">
|
|
||||||
<path style="stroke:none;" d="M 2.453125 -3.875 C 2.066406 -3.875 1.757812 -3.722656 1.53125 -3.421875 C 1.3125 -3.117188 1.203125 -2.707031 1.203125 -2.1875 C 1.203125 -1.664062 1.3125 -1.253906 1.53125 -0.953125 C 1.757812 -0.648438 2.066406 -0.5 2.453125 -0.5 C 2.835938 -0.5 3.140625 -0.648438 3.359375 -0.953125 C 3.585938 -1.253906 3.703125 -1.664062 3.703125 -2.1875 C 3.703125 -2.707031 3.585938 -3.117188 3.359375 -3.421875 C 3.140625 -3.722656 2.835938 -3.875 2.453125 -3.875 Z M 2.453125 -4.484375 C 3.078125 -4.484375 3.566406 -4.28125 3.921875 -3.875 C 4.273438 -3.46875 4.453125 -2.90625 4.453125 -2.1875 C 4.453125 -1.46875 4.273438 -0.90625 3.921875 -0.5 C 3.566406 -0.09375 3.078125 0.109375 2.453125 0.109375 C 1.828125 0.109375 1.332031 -0.09375 0.96875 -0.5 C 0.613281 -0.90625 0.4375 -1.46875 0.4375 -2.1875 C 0.4375 -2.90625 0.613281 -3.46875 0.96875 -3.875 C 1.332031 -4.28125 1.828125 -4.484375 2.453125 -4.484375 Z M 2.453125 -4.484375 "/>
|
<path style="stroke:none;" d="M 2.453125 -3.875 C 2.066406 -3.875 1.757812 -3.722656 1.53125 -3.421875 C 1.3125 -3.117188 1.203125 -2.707031 1.203125 -2.1875 C 1.203125 -1.664062 1.3125 -1.253906 1.53125 -0.953125 C 1.757812 -0.648438 2.066406 -0.5 2.453125 -0.5 C 2.835938 -0.5 3.140625 -0.648438 3.359375 -0.953125 C 3.585938 -1.253906 3.703125 -1.664062 3.703125 -2.1875 C 3.703125 -2.707031 3.585938 -3.117188 3.359375 -3.421875 C 3.140625 -3.722656 2.835938 -3.875 2.453125 -3.875 Z M 2.453125 -4.484375 C 3.078125 -4.484375 3.566406 -4.28125 3.921875 -3.875 C 4.273438 -3.46875 4.453125 -2.90625 4.453125 -2.1875 C 4.453125 -1.46875 4.273438 -0.90625 3.921875 -0.5 C 3.566406 -0.09375 3.078125 0.109375 2.453125 0.109375 C 1.828125 0.109375 1.332031 -0.09375 0.96875 -0.5 C 0.613281 -0.90625 0.4375 -1.46875 0.4375 -2.1875 C 0.4375 -2.90625 0.613281 -3.46875 0.96875 -3.875 C 1.332031 -4.28125 1.828125 -4.484375 2.453125 -4.484375 Z M 2.453125 -4.484375 "/>
|
||||||
</symbol>
|
</symbol>
|
||||||
<symbol overflow="visible" id="glyph1-17">
|
<symbol overflow="visible" id="glyph1-3">
|
||||||
<path style="stroke:none;" d="M 0.734375 -6.078125 L 1.453125 -6.078125 L 1.453125 -2.484375 L 3.59375 -4.375 L 4.515625 -4.375 L 2.1875 -2.328125 L 4.609375 0 L 3.671875 0 L 1.453125 -2.140625 L 1.453125 0 L 0.734375 0 Z M 0.734375 -6.078125 "/>
|
<path style="stroke:none;" d="M 3.890625 -2.1875 C 3.890625 -2.71875 3.78125 -3.128906 3.5625 -3.421875 C 3.351562 -3.722656 3.054688 -3.875 2.671875 -3.875 C 2.296875 -3.875 2 -3.722656 1.78125 -3.421875 C 1.5625 -3.128906 1.453125 -2.71875 1.453125 -2.1875 C 1.453125 -1.65625 1.5625 -1.238281 1.78125 -0.9375 C 2 -0.632812 2.296875 -0.484375 2.671875 -0.484375 C 3.054688 -0.484375 3.351562 -0.632812 3.5625 -0.9375 C 3.78125 -1.238281 3.890625 -1.65625 3.890625 -2.1875 Z M 1.453125 -3.71875 C 1.597656 -3.976562 1.785156 -4.171875 2.015625 -4.296875 C 2.253906 -4.421875 2.53125 -4.484375 2.84375 -4.484375 C 3.375 -4.484375 3.804688 -4.269531 4.140625 -3.84375 C 4.472656 -3.425781 4.640625 -2.875 4.640625 -2.1875 C 4.640625 -1.5 4.472656 -0.941406 4.140625 -0.515625 C 3.804688 -0.0976562 3.375 0.109375 2.84375 0.109375 C 2.53125 0.109375 2.253906 0.046875 2.015625 -0.078125 C 1.785156 -0.203125 1.597656 -0.394531 1.453125 -0.65625 L 1.453125 0 L 0.734375 0 L 0.734375 -6.078125 L 1.453125 -6.078125 Z M 1.453125 -3.71875 "/>
|
||||||
</symbol>
|
</symbol>
|
||||||
<symbol overflow="visible" id="glyph1-18">
|
<symbol overflow="visible" id="glyph1-4">
|
||||||
<path style="stroke:none;" d="M 0.75 -4.375 L 1.46875 -4.375 L 1.46875 0.078125 C 1.46875 0.640625 1.363281 1.046875 1.15625 1.296875 C 0.945312 1.546875 0.601562 1.671875 0.125 1.671875 L -0.140625 1.671875 L -0.140625 1.0625 L 0.046875 1.0625 C 0.316406 1.0625 0.5 0.992188 0.59375 0.859375 C 0.695312 0.734375 0.75 0.472656 0.75 0.078125 Z M 0.75 -6.078125 L 1.46875 -6.078125 L 1.46875 -5.171875 L 0.75 -5.171875 Z M 0.75 -6.078125 "/>
|
<path style="stroke:none;" d=""/>
|
||||||
</symbol>
|
</symbol>
|
||||||
<symbol overflow="visible" id="glyph1-19">
|
<symbol overflow="visible" id="glyph1-5">
|
||||||
<path style="stroke:none;" d="M 4.28125 -5.640625 L 4.28125 -4.875 C 3.976562 -5.019531 3.691406 -5.125 3.421875 -5.1875 C 3.160156 -5.257812 2.910156 -5.296875 2.671875 -5.296875 C 2.234375 -5.296875 1.898438 -5.210938 1.671875 -5.046875 C 1.441406 -4.878906 1.328125 -4.644531 1.328125 -4.34375 C 1.328125 -4.082031 1.398438 -3.882812 1.546875 -3.75 C 1.703125 -3.625 2 -3.519531 2.4375 -3.4375 L 2.90625 -3.34375 C 3.5 -3.226562 3.9375 -3.03125 4.21875 -2.75 C 4.5 -2.46875 4.640625 -2.085938 4.640625 -1.609375 C 4.640625 -1.046875 4.445312 -0.617188 4.0625 -0.328125 C 3.6875 -0.0351562 3.132812 0.109375 2.40625 0.109375 C 2.125 0.109375 1.828125 0.078125 1.515625 0.015625 C 1.203125 -0.046875 0.878906 -0.140625 0.546875 -0.265625 L 0.546875 -1.078125 C 0.867188 -0.890625 1.179688 -0.75 1.484375 -0.65625 C 1.796875 -0.570312 2.101562 -0.53125 2.40625 -0.53125 C 2.851562 -0.53125 3.195312 -0.617188 3.4375 -0.796875 C 3.6875 -0.972656 3.8125 -1.222656 3.8125 -1.546875 C 3.8125 -1.835938 3.722656 -2.0625 3.546875 -2.21875 C 3.367188 -2.382812 3.082031 -2.507812 2.6875 -2.59375 L 2.203125 -2.6875 C 1.609375 -2.800781 1.179688 -2.984375 0.921875 -3.234375 C 0.660156 -3.484375 0.53125 -3.832031 0.53125 -4.28125 C 0.53125 -4.789062 0.710938 -5.191406 1.078125 -5.484375 C 1.441406 -5.785156 1.941406 -5.9375 2.578125 -5.9375 C 2.847656 -5.9375 3.125 -5.910156 3.40625 -5.859375 C 3.695312 -5.816406 3.988281 -5.742188 4.28125 -5.640625 Z M 4.28125 -5.640625 "/>
|
<path style="stroke:none;" d="M 1 -0.671875 L 2.28125 -0.671875 L 2.28125 -5.109375 L 0.875 -4.828125 L 0.875 -5.546875 L 2.28125 -5.828125 L 3.0625 -5.828125 L 3.0625 -0.671875 L 4.359375 -0.671875 L 4.359375 0 L 1 0 Z M 1 -0.671875 "/>
|
||||||
</symbol>
|
</symbol>
|
||||||
<symbol overflow="visible" id="glyph1-20">
|
<symbol overflow="visible" id="glyph1-6">
|
||||||
<path style="stroke:none;" d="M 3.90625 -4.203125 L 3.90625 -3.53125 C 3.695312 -3.644531 3.488281 -3.726562 3.28125 -3.78125 C 3.082031 -3.84375 2.878906 -3.875 2.671875 -3.875 C 2.203125 -3.875 1.835938 -3.722656 1.578125 -3.421875 C 1.328125 -3.128906 1.203125 -2.71875 1.203125 -2.1875 C 1.203125 -1.65625 1.328125 -1.238281 1.578125 -0.9375 C 1.835938 -0.644531 2.203125 -0.5 2.671875 -0.5 C 2.878906 -0.5 3.082031 -0.523438 3.28125 -0.578125 C 3.488281 -0.640625 3.695312 -0.722656 3.90625 -0.828125 L 3.90625 -0.171875 C 3.707031 -0.078125 3.5 -0.0078125 3.28125 0.03125 C 3.0625 0.0820312 2.832031 0.109375 2.59375 0.109375 C 1.9375 0.109375 1.410156 -0.09375 1.015625 -0.5 C 0.628906 -0.914062 0.4375 -1.476562 0.4375 -2.1875 C 0.4375 -2.894531 0.632812 -3.453125 1.03125 -3.859375 C 1.425781 -4.273438 1.960938 -4.484375 2.640625 -4.484375 C 2.859375 -4.484375 3.070312 -4.457031 3.28125 -4.40625 C 3.5 -4.363281 3.707031 -4.296875 3.90625 -4.203125 Z M 3.90625 -4.203125 "/>
|
<path style="stroke:none;" d="M 3.546875 -4.25 L 3.546875 -3.5625 C 3.335938 -3.664062 3.125 -3.742188 2.90625 -3.796875 C 2.6875 -3.847656 2.460938 -3.875 2.234375 -3.875 C 1.878906 -3.875 1.609375 -3.816406 1.421875 -3.703125 C 1.242188 -3.597656 1.15625 -3.4375 1.15625 -3.21875 C 1.15625 -3.050781 1.21875 -2.921875 1.34375 -2.828125 C 1.476562 -2.734375 1.738281 -2.644531 2.125 -2.5625 L 2.375 -2.5 C 2.882812 -2.394531 3.242188 -2.242188 3.453125 -2.046875 C 3.671875 -1.847656 3.78125 -1.566406 3.78125 -1.203125 C 3.78125 -0.796875 3.617188 -0.472656 3.296875 -0.234375 C 2.972656 -0.00390625 2.53125 0.109375 1.96875 0.109375 C 1.738281 0.109375 1.492188 0.0820312 1.234375 0.03125 C 0.984375 -0.0078125 0.71875 -0.0703125 0.4375 -0.15625 L 0.4375 -0.90625 C 0.695312 -0.769531 0.957031 -0.664062 1.21875 -0.59375 C 1.476562 -0.519531 1.734375 -0.484375 1.984375 -0.484375 C 2.328125 -0.484375 2.585938 -0.539062 2.765625 -0.65625 C 2.953125 -0.78125 3.046875 -0.945312 3.046875 -1.15625 C 3.046875 -1.351562 2.976562 -1.503906 2.84375 -1.609375 C 2.707031 -1.710938 2.421875 -1.8125 1.984375 -1.90625 L 1.734375 -1.96875 C 1.285156 -2.0625 0.960938 -2.203125 0.765625 -2.390625 C 0.566406 -2.585938 0.46875 -2.851562 0.46875 -3.1875 C 0.46875 -3.601562 0.613281 -3.921875 0.90625 -4.140625 C 1.195312 -4.367188 1.609375 -4.484375 2.140625 -4.484375 C 2.410156 -4.484375 2.660156 -4.460938 2.890625 -4.421875 C 3.128906 -4.378906 3.347656 -4.320312 3.546875 -4.25 Z M 3.546875 -4.25 "/>
|
||||||
</symbol>
|
</symbol>
|
||||||
<symbol overflow="visible" id="glyph1-21">
|
<symbol overflow="visible" id="glyph1-7">
|
||||||
|
<path style="stroke:none;" d="M 1.46875 -5.625 L 1.46875 -4.375 L 2.953125 -4.375 L 2.953125 -3.8125 L 1.46875 -3.8125 L 1.46875 -1.4375 C 1.46875 -1.082031 1.515625 -0.851562 1.609375 -0.75 C 1.710938 -0.65625 1.910156 -0.609375 2.203125 -0.609375 L 2.953125 -0.609375 L 2.953125 0 L 2.203125 0 C 1.648438 0 1.269531 -0.101562 1.0625 -0.3125 C 0.851562 -0.519531 0.75 -0.894531 0.75 -1.4375 L 0.75 -3.8125 L 0.21875 -3.8125 L 0.21875 -4.375 L 0.75 -4.375 L 0.75 -5.625 Z M 1.46875 -5.625 "/>
|
||||||
|
</symbol>
|
||||||
|
<symbol overflow="visible" id="glyph1-8">
|
||||||
|
<path style="stroke:none;" d="M 2.75 -2.203125 C 2.164062 -2.203125 1.757812 -2.132812 1.53125 -2 C 1.3125 -1.863281 1.203125 -1.640625 1.203125 -1.328125 C 1.203125 -1.066406 1.285156 -0.859375 1.453125 -0.703125 C 1.617188 -0.554688 1.847656 -0.484375 2.140625 -0.484375 C 2.535156 -0.484375 2.851562 -0.625 3.09375 -0.90625 C 3.332031 -1.195312 3.453125 -1.578125 3.453125 -2.046875 L 3.453125 -2.203125 Z M 4.171875 -2.5 L 4.171875 0 L 3.453125 0 L 3.453125 -0.671875 C 3.296875 -0.398438 3.09375 -0.203125 2.84375 -0.078125 C 2.601562 0.046875 2.304688 0.109375 1.953125 0.109375 C 1.503906 0.109375 1.144531 -0.015625 0.875 -0.265625 C 0.613281 -0.515625 0.484375 -0.851562 0.484375 -1.28125 C 0.484375 -1.769531 0.644531 -2.140625 0.96875 -2.390625 C 1.300781 -2.640625 1.796875 -2.765625 2.453125 -2.765625 L 3.453125 -2.765625 L 3.453125 -2.828125 C 3.453125 -3.160156 3.34375 -3.414062 3.125 -3.59375 C 2.914062 -3.78125 2.613281 -3.875 2.21875 -3.875 C 1.96875 -3.875 1.722656 -3.84375 1.484375 -3.78125 C 1.242188 -3.71875 1.015625 -3.628906 0.796875 -3.515625 L 0.796875 -4.171875 C 1.066406 -4.273438 1.320312 -4.351562 1.5625 -4.40625 C 1.8125 -4.457031 2.054688 -4.484375 2.296875 -4.484375 C 2.921875 -4.484375 3.390625 -4.316406 3.703125 -3.984375 C 4.015625 -3.660156 4.171875 -3.164062 4.171875 -2.5 Z M 4.171875 -2.5 "/>
|
||||||
|
</symbol>
|
||||||
|
<symbol overflow="visible" id="glyph1-9">
|
||||||
|
<path style="stroke:none;" d="M 3.296875 -3.703125 C 3.210938 -3.753906 3.125 -3.789062 3.03125 -3.8125 C 2.9375 -3.832031 2.832031 -3.84375 2.71875 -3.84375 C 2.3125 -3.84375 2 -3.707031 1.78125 -3.4375 C 1.5625 -3.175781 1.453125 -2.800781 1.453125 -2.3125 L 1.453125 0 L 0.734375 0 L 0.734375 -4.375 L 1.453125 -4.375 L 1.453125 -3.703125 C 1.597656 -3.960938 1.789062 -4.15625 2.03125 -4.28125 C 2.28125 -4.414062 2.578125 -4.484375 2.921875 -4.484375 C 2.972656 -4.484375 3.023438 -4.476562 3.078125 -4.46875 C 3.140625 -4.46875 3.207031 -4.457031 3.28125 -4.4375 Z M 3.296875 -3.703125 "/>
|
||||||
|
</symbol>
|
||||||
|
<symbol overflow="visible" id="glyph1-10">
|
||||||
|
<path style="stroke:none;" d="M 0.75 -4.375 L 1.46875 -4.375 L 1.46875 0 L 0.75 0 Z M 0.75 -6.078125 L 1.46875 -6.078125 L 1.46875 -5.171875 L 0.75 -5.171875 Z M 0.75 -6.078125 "/>
|
||||||
|
</symbol>
|
||||||
|
<symbol overflow="visible" id="glyph1-11">
|
||||||
<path style="stroke:none;" d="M 4.390625 -2.640625 L 4.390625 0 L 3.671875 0 L 3.671875 -2.625 C 3.671875 -3.03125 3.585938 -3.335938 3.421875 -3.546875 C 3.265625 -3.753906 3.023438 -3.859375 2.703125 -3.859375 C 2.316406 -3.859375 2.007812 -3.734375 1.78125 -3.484375 C 1.5625 -3.234375 1.453125 -2.894531 1.453125 -2.46875 L 1.453125 0 L 0.734375 0 L 0.734375 -4.375 L 1.453125 -4.375 L 1.453125 -3.703125 C 1.617188 -3.960938 1.816406 -4.15625 2.046875 -4.28125 C 2.285156 -4.414062 2.554688 -4.484375 2.859375 -4.484375 C 3.367188 -4.484375 3.75 -4.328125 4 -4.015625 C 4.257812 -3.703125 4.390625 -3.242188 4.390625 -2.640625 Z M 4.390625 -2.640625 "/>
|
<path style="stroke:none;" d="M 4.390625 -2.640625 L 4.390625 0 L 3.671875 0 L 3.671875 -2.625 C 3.671875 -3.03125 3.585938 -3.335938 3.421875 -3.546875 C 3.265625 -3.753906 3.023438 -3.859375 2.703125 -3.859375 C 2.316406 -3.859375 2.007812 -3.734375 1.78125 -3.484375 C 1.5625 -3.234375 1.453125 -2.894531 1.453125 -2.46875 L 1.453125 0 L 0.734375 0 L 0.734375 -4.375 L 1.453125 -4.375 L 1.453125 -3.703125 C 1.617188 -3.960938 1.816406 -4.15625 2.046875 -4.28125 C 2.285156 -4.414062 2.554688 -4.484375 2.859375 -4.484375 C 3.367188 -4.484375 3.75 -4.328125 4 -4.015625 C 4.257812 -3.703125 4.390625 -3.242188 4.390625 -2.640625 Z M 4.390625 -2.640625 "/>
|
||||||
</symbol>
|
</symbol>
|
||||||
<symbol overflow="visible" id="glyph1-22">
|
<symbol overflow="visible" id="glyph1-12">
|
||||||
<path style="stroke:none;" d="M 0.9375 -4.140625 L 1.765625 -4.140625 L 1.765625 -3.140625 L 0.9375 -3.140625 Z M 0.9375 -1 L 1.765625 -1 L 1.765625 -0.328125 L 1.125 0.9375 L 0.625 0.9375 L 0.9375 -0.328125 Z M 0.9375 -1 "/>
|
<path style="stroke:none;" d="M 3.640625 -2.234375 C 3.640625 -2.753906 3.53125 -3.15625 3.3125 -3.4375 C 3.09375 -3.726562 2.789062 -3.875 2.40625 -3.875 C 2.019531 -3.875 1.71875 -3.726562 1.5 -3.4375 C 1.289062 -3.15625 1.1875 -2.753906 1.1875 -2.234375 C 1.1875 -1.722656 1.289062 -1.320312 1.5 -1.03125 C 1.71875 -0.75 2.019531 -0.609375 2.40625 -0.609375 C 2.789062 -0.609375 3.09375 -0.75 3.3125 -1.03125 C 3.53125 -1.320312 3.640625 -1.722656 3.640625 -2.234375 Z M 4.359375 -0.546875 C 4.359375 0.203125 4.191406 0.757812 3.859375 1.125 C 3.523438 1.488281 3.019531 1.671875 2.34375 1.671875 C 2.082031 1.671875 1.835938 1.648438 1.609375 1.609375 C 1.390625 1.566406 1.175781 1.507812 0.96875 1.4375 L 0.96875 0.734375 C 1.175781 0.847656 1.382812 0.929688 1.59375 0.984375 C 1.800781 1.046875 2.007812 1.078125 2.21875 1.078125 C 2.695312 1.078125 3.050781 0.953125 3.28125 0.703125 C 3.519531 0.453125 3.640625 0.078125 3.640625 -0.421875 L 3.640625 -0.765625 C 3.484375 -0.515625 3.289062 -0.320312 3.0625 -0.1875 C 2.832031 -0.0625 2.554688 0 2.234375 0 C 1.691406 0 1.253906 -0.203125 0.921875 -0.609375 C 0.597656 -1.023438 0.4375 -1.566406 0.4375 -2.234375 C 0.4375 -2.910156 0.597656 -3.453125 0.921875 -3.859375 C 1.253906 -4.273438 1.691406 -4.484375 2.234375 -4.484375 C 2.554688 -4.484375 2.832031 -4.414062 3.0625 -4.28125 C 3.289062 -4.15625 3.484375 -3.96875 3.640625 -3.71875 L 3.640625 -4.375 L 4.359375 -4.375 Z M 4.359375 -0.546875 "/>
|
||||||
</symbol>
|
</symbol>
|
||||||
<symbol overflow="visible" id="glyph1-23">
|
<symbol overflow="visible" id="glyph1-13">
|
||||||
<path style="stroke:none;" d="M 1.453125 -0.65625 L 1.453125 1.671875 L 0.734375 1.671875 L 0.734375 -4.375 L 1.453125 -4.375 L 1.453125 -3.71875 C 1.597656 -3.976562 1.785156 -4.171875 2.015625 -4.296875 C 2.253906 -4.421875 2.53125 -4.484375 2.84375 -4.484375 C 3.375 -4.484375 3.804688 -4.269531 4.140625 -3.84375 C 4.472656 -3.425781 4.640625 -2.875 4.640625 -2.1875 C 4.640625 -1.5 4.472656 -0.941406 4.140625 -0.515625 C 3.804688 -0.0976562 3.375 0.109375 2.84375 0.109375 C 2.53125 0.109375 2.253906 0.046875 2.015625 -0.078125 C 1.785156 -0.203125 1.597656 -0.394531 1.453125 -0.65625 Z M 3.890625 -2.1875 C 3.890625 -2.71875 3.78125 -3.128906 3.5625 -3.421875 C 3.351562 -3.722656 3.054688 -3.875 2.671875 -3.875 C 2.296875 -3.875 2 -3.722656 1.78125 -3.421875 C 1.5625 -3.128906 1.453125 -2.71875 1.453125 -2.1875 C 1.453125 -1.65625 1.5625 -1.238281 1.78125 -0.9375 C 2 -0.632812 2.296875 -0.484375 2.671875 -0.484375 C 3.054688 -0.484375 3.351562 -0.632812 3.5625 -0.9375 C 3.78125 -1.238281 3.890625 -1.65625 3.890625 -2.1875 Z M 3.890625 -2.1875 "/>
|
<path style="stroke:none;" d="M 3.640625 -3.71875 L 3.640625 -6.078125 L 4.359375 -6.078125 L 4.359375 0 L 3.640625 0 L 3.640625 -0.65625 C 3.484375 -0.394531 3.289062 -0.203125 3.0625 -0.078125 C 2.832031 0.046875 2.554688 0.109375 2.234375 0.109375 C 1.703125 0.109375 1.269531 -0.0976562 0.9375 -0.515625 C 0.601562 -0.941406 0.4375 -1.5 0.4375 -2.1875 C 0.4375 -2.875 0.601562 -3.425781 0.9375 -3.84375 C 1.269531 -4.269531 1.703125 -4.484375 2.234375 -4.484375 C 2.554688 -4.484375 2.832031 -4.421875 3.0625 -4.296875 C 3.289062 -4.171875 3.484375 -3.976562 3.640625 -3.71875 Z M 1.1875 -2.1875 C 1.1875 -1.65625 1.296875 -1.238281 1.515625 -0.9375 C 1.734375 -0.632812 2.03125 -0.484375 2.40625 -0.484375 C 2.789062 -0.484375 3.09375 -0.632812 3.3125 -0.9375 C 3.53125 -1.238281 3.640625 -1.65625 3.640625 -2.1875 C 3.640625 -2.71875 3.53125 -3.128906 3.3125 -3.421875 C 3.09375 -3.722656 2.789062 -3.875 2.40625 -3.875 C 2.03125 -3.875 1.734375 -3.722656 1.515625 -3.421875 C 1.296875 -3.128906 1.1875 -2.71875 1.1875 -2.1875 Z M 1.1875 -2.1875 "/>
|
||||||
</symbol>
|
</symbol>
|
||||||
<symbol overflow="visible" id="glyph1-24">
|
<symbol overflow="visible" id="glyph1-14">
|
||||||
<path style="stroke:none;" d="M 0.390625 -2.515625 L 2.5 -2.515625 L 2.5 -1.875 L 0.390625 -1.875 Z M 0.390625 -2.515625 "/>
|
<path style="stroke:none;" d="M 4.5 -2.375 L 4.5 -2.015625 L 1.1875 -2.015625 C 1.21875 -1.523438 1.367188 -1.148438 1.640625 -0.890625 C 1.910156 -0.628906 2.28125 -0.5 2.75 -0.5 C 3.03125 -0.5 3.300781 -0.53125 3.5625 -0.59375 C 3.820312 -0.664062 4.078125 -0.769531 4.328125 -0.90625 L 4.328125 -0.21875 C 4.066406 -0.113281 3.800781 -0.0351562 3.53125 0.015625 C 3.257812 0.078125 2.988281 0.109375 2.71875 0.109375 C 2.019531 0.109375 1.460938 -0.09375 1.046875 -0.5 C 0.640625 -0.90625 0.4375 -1.453125 0.4375 -2.140625 C 0.4375 -2.859375 0.628906 -3.425781 1.015625 -3.84375 C 1.410156 -4.269531 1.9375 -4.484375 2.59375 -4.484375 C 3.175781 -4.484375 3.640625 -4.289062 3.984375 -3.90625 C 4.328125 -3.53125 4.5 -3.019531 4.5 -2.375 Z M 3.78125 -2.578125 C 3.769531 -2.972656 3.65625 -3.285156 3.4375 -3.515625 C 3.226562 -3.753906 2.945312 -3.875 2.59375 -3.875 C 2.195312 -3.875 1.875 -3.757812 1.625 -3.53125 C 1.382812 -3.300781 1.25 -2.984375 1.21875 -2.578125 Z M 3.78125 -2.578125 "/>
|
||||||
</symbol>
|
</symbol>
|
||||||
<symbol overflow="visible" id="glyph1-25">
|
<symbol overflow="visible" id="glyph1-15">
|
||||||
|
<path style="stroke:none;" d="M 1.53125 -0.671875 L 4.296875 -0.671875 L 4.296875 0 L 0.59375 0 L 0.59375 -0.671875 C 0.882812 -0.972656 1.289062 -1.382812 1.8125 -1.90625 C 2.332031 -2.4375 2.65625 -2.773438 2.78125 -2.921875 C 3.039062 -3.203125 3.21875 -3.441406 3.3125 -3.640625 C 3.414062 -3.835938 3.46875 -4.03125 3.46875 -4.21875 C 3.46875 -4.53125 3.359375 -4.785156 3.140625 -4.984375 C 2.921875 -5.179688 2.640625 -5.28125 2.296875 -5.28125 C 2.046875 -5.28125 1.78125 -5.234375 1.5 -5.140625 C 1.226562 -5.054688 0.9375 -4.925781 0.625 -4.75 L 0.625 -5.546875 C 0.945312 -5.679688 1.242188 -5.78125 1.515625 -5.84375 C 1.796875 -5.90625 2.050781 -5.9375 2.28125 -5.9375 C 2.882812 -5.9375 3.363281 -5.785156 3.71875 -5.484375 C 4.082031 -5.179688 4.265625 -4.78125 4.265625 -4.28125 C 4.265625 -4.039062 4.21875 -3.8125 4.125 -3.59375 C 4.03125 -3.375 3.867188 -3.117188 3.640625 -2.828125 C 3.566406 -2.753906 3.351562 -2.535156 3 -2.171875 C 2.65625 -1.816406 2.164062 -1.316406 1.53125 -0.671875 Z M 1.53125 -0.671875 "/>
|
||||||
|
</symbol>
|
||||||
|
<symbol overflow="visible" id="glyph1-16">
|
||||||
|
<path style="stroke:none;" d="M 3.25 -3.140625 C 3.625 -3.066406 3.914062 -2.898438 4.125 -2.640625 C 4.34375 -2.390625 4.453125 -2.078125 4.453125 -1.703125 C 4.453125 -1.117188 4.253906 -0.671875 3.859375 -0.359375 C 3.460938 -0.046875 2.898438 0.109375 2.171875 0.109375 C 1.921875 0.109375 1.664062 0.0820312 1.40625 0.03125 C 1.15625 -0.0078125 0.890625 -0.078125 0.609375 -0.171875 L 0.609375 -0.9375 C 0.828125 -0.8125 1.066406 -0.710938 1.328125 -0.640625 C 1.585938 -0.578125 1.859375 -0.546875 2.140625 -0.546875 C 2.640625 -0.546875 3.019531 -0.644531 3.28125 -0.84375 C 3.539062 -1.039062 3.671875 -1.328125 3.671875 -1.703125 C 3.671875 -2.046875 3.546875 -2.3125 3.296875 -2.5 C 3.054688 -2.695312 2.722656 -2.796875 2.296875 -2.796875 L 1.625 -2.796875 L 1.625 -3.4375 L 2.328125 -3.4375 C 2.710938 -3.4375 3.007812 -3.515625 3.21875 -3.671875 C 3.425781 -3.828125 3.53125 -4.050781 3.53125 -4.34375 C 3.53125 -4.644531 3.421875 -4.875 3.203125 -5.03125 C 2.992188 -5.195312 2.691406 -5.28125 2.296875 -5.28125 C 2.078125 -5.28125 1.84375 -5.253906 1.59375 -5.203125 C 1.351562 -5.160156 1.082031 -5.085938 0.78125 -4.984375 L 0.78125 -5.6875 C 1.082031 -5.769531 1.363281 -5.832031 1.625 -5.875 C 1.882812 -5.914062 2.132812 -5.9375 2.375 -5.9375 C 2.96875 -5.9375 3.4375 -5.800781 3.78125 -5.53125 C 4.132812 -5.257812 4.3125 -4.890625 4.3125 -4.421875 C 4.3125 -4.097656 4.21875 -3.828125 4.03125 -3.609375 C 3.851562 -3.390625 3.59375 -3.234375 3.25 -3.140625 Z M 3.25 -3.140625 "/>
|
||||||
|
</symbol>
|
||||||
|
<symbol overflow="visible" id="glyph1-17">
|
||||||
|
<path style="stroke:none;" d="M 2.734375 -5.0625 L 1.671875 -2.15625 L 3.8125 -2.15625 Z M 2.296875 -5.828125 L 3.1875 -5.828125 L 5.40625 0 L 4.59375 0 L 4.0625 -1.5 L 1.421875 -1.5 L 0.890625 0 L 0.0625 0 Z M 2.296875 -5.828125 "/>
|
||||||
|
</symbol>
|
||||||
|
<symbol overflow="visible" id="glyph1-18">
|
||||||
<path style="stroke:none;" d="M 0.75 -6.078125 L 1.46875 -6.078125 L 1.46875 0 L 0.75 0 Z M 0.75 -6.078125 "/>
|
<path style="stroke:none;" d="M 0.75 -6.078125 L 1.46875 -6.078125 L 1.46875 0 L 0.75 0 Z M 0.75 -6.078125 "/>
|
||||||
</symbol>
|
</symbol>
|
||||||
<symbol overflow="visible" id="glyph1-26">
|
<symbol overflow="visible" id="glyph1-19">
|
||||||
<path style="stroke:none;" d="M 0.234375 -4.375 L 1 -4.375 L 2.375 -0.703125 L 3.734375 -4.375 L 4.5 -4.375 L 2.859375 0 L 1.875 0 Z M 0.234375 -4.375 "/>
|
<path style="stroke:none;" d="M 3.90625 -4.203125 L 3.90625 -3.53125 C 3.695312 -3.644531 3.488281 -3.726562 3.28125 -3.78125 C 3.082031 -3.84375 2.878906 -3.875 2.671875 -3.875 C 2.203125 -3.875 1.835938 -3.722656 1.578125 -3.421875 C 1.328125 -3.128906 1.203125 -2.71875 1.203125 -2.1875 C 1.203125 -1.65625 1.328125 -1.238281 1.578125 -0.9375 C 1.835938 -0.644531 2.203125 -0.5 2.671875 -0.5 C 2.878906 -0.5 3.082031 -0.523438 3.28125 -0.578125 C 3.488281 -0.640625 3.695312 -0.722656 3.90625 -0.828125 L 3.90625 -0.171875 C 3.707031 -0.078125 3.5 -0.0078125 3.28125 0.03125 C 3.0625 0.0820312 2.832031 0.109375 2.59375 0.109375 C 1.9375 0.109375 1.410156 -0.09375 1.015625 -0.5 C 0.628906 -0.914062 0.4375 -1.476562 0.4375 -2.1875 C 0.4375 -2.894531 0.632812 -3.453125 1.03125 -3.859375 C 1.425781 -4.273438 1.960938 -4.484375 2.640625 -4.484375 C 2.859375 -4.484375 3.070312 -4.457031 3.28125 -4.40625 C 3.5 -4.363281 3.707031 -4.296875 3.90625 -4.203125 Z M 3.90625 -4.203125 "/>
|
||||||
</symbol>
|
</symbol>
|
||||||
<symbol overflow="visible" id="glyph1-27">
|
<symbol overflow="visible" id="glyph1-20">
|
||||||
|
<path style="stroke:none;" d="M 4.390625 -2.640625 L 4.390625 0 L 3.671875 0 L 3.671875 -2.625 C 3.671875 -3.03125 3.585938 -3.335938 3.421875 -3.546875 C 3.265625 -3.753906 3.023438 -3.859375 2.703125 -3.859375 C 2.316406 -3.859375 2.007812 -3.734375 1.78125 -3.484375 C 1.5625 -3.234375 1.453125 -2.894531 1.453125 -2.46875 L 1.453125 0 L 0.734375 0 L 0.734375 -6.078125 L 1.453125 -6.078125 L 1.453125 -3.703125 C 1.617188 -3.960938 1.816406 -4.15625 2.046875 -4.28125 C 2.285156 -4.414062 2.554688 -4.484375 2.859375 -4.484375 C 3.367188 -4.484375 3.75 -4.328125 4 -4.015625 C 4.257812 -3.703125 4.390625 -3.242188 4.390625 -2.640625 Z M 4.390625 -2.640625 "/>
|
||||||
|
</symbol>
|
||||||
|
<symbol overflow="visible" id="glyph1-21">
|
||||||
|
<path style="stroke:none;" d="M 2.96875 -6.078125 L 2.96875 -5.484375 L 2.28125 -5.484375 C 2.019531 -5.484375 1.835938 -5.429688 1.734375 -5.328125 C 1.640625 -5.222656 1.59375 -5.035156 1.59375 -4.765625 L 1.59375 -4.375 L 2.78125 -4.375 L 2.78125 -3.8125 L 1.59375 -3.8125 L 1.59375 0 L 0.875 0 L 0.875 -3.8125 L 0.1875 -3.8125 L 0.1875 -4.375 L 0.875 -4.375 L 0.875 -4.6875 C 0.875 -5.164062 0.984375 -5.515625 1.203125 -5.734375 C 1.429688 -5.960938 1.796875 -6.078125 2.296875 -6.078125 Z M 2.96875 -6.078125 "/>
|
||||||
|
</symbol>
|
||||||
|
<symbol overflow="visible" id="glyph1-22">
|
||||||
|
<path style="stroke:none;" d="M 0.734375 -6.078125 L 1.453125 -6.078125 L 1.453125 -2.484375 L 3.59375 -4.375 L 4.515625 -4.375 L 2.1875 -2.328125 L 4.609375 0 L 3.671875 0 L 1.453125 -2.140625 L 1.453125 0 L 0.734375 0 Z M 0.734375 -6.078125 "/>
|
||||||
|
</symbol>
|
||||||
|
<symbol overflow="visible" id="glyph1-23">
|
||||||
<path style="stroke:none;" d="M 0.34375 -4.375 L 1.0625 -4.375 L 1.953125 -0.96875 L 2.84375 -4.375 L 3.703125 -4.375 L 4.59375 -0.96875 L 5.484375 -4.375 L 6.203125 -4.375 L 5.0625 0 L 4.21875 0 L 3.28125 -3.59375 L 2.328125 0 L 1.484375 0 Z M 0.34375 -4.375 "/>
|
<path style="stroke:none;" d="M 0.34375 -4.375 L 1.0625 -4.375 L 1.953125 -0.96875 L 2.84375 -4.375 L 3.703125 -4.375 L 4.59375 -0.96875 L 5.484375 -4.375 L 6.203125 -4.375 L 5.0625 0 L 4.21875 0 L 3.28125 -3.59375 L 2.328125 0 L 1.484375 0 Z M 0.34375 -4.375 "/>
|
||||||
</symbol>
|
</symbol>
|
||||||
|
<symbol overflow="visible" id="glyph1-24">
|
||||||
|
<path style="stroke:none;" d="M 4.28125 -5.640625 L 4.28125 -4.875 C 3.976562 -5.019531 3.691406 -5.125 3.421875 -5.1875 C 3.160156 -5.257812 2.910156 -5.296875 2.671875 -5.296875 C 2.234375 -5.296875 1.898438 -5.210938 1.671875 -5.046875 C 1.441406 -4.878906 1.328125 -4.644531 1.328125 -4.34375 C 1.328125 -4.082031 1.398438 -3.882812 1.546875 -3.75 C 1.703125 -3.625 2 -3.519531 2.4375 -3.4375 L 2.90625 -3.34375 C 3.5 -3.226562 3.9375 -3.03125 4.21875 -2.75 C 4.5 -2.46875 4.640625 -2.085938 4.640625 -1.609375 C 4.640625 -1.046875 4.445312 -0.617188 4.0625 -0.328125 C 3.6875 -0.0351562 3.132812 0.109375 2.40625 0.109375 C 2.125 0.109375 1.828125 0.078125 1.515625 0.015625 C 1.203125 -0.046875 0.878906 -0.140625 0.546875 -0.265625 L 0.546875 -1.078125 C 0.867188 -0.890625 1.179688 -0.75 1.484375 -0.65625 C 1.796875 -0.570312 2.101562 -0.53125 2.40625 -0.53125 C 2.851562 -0.53125 3.195312 -0.617188 3.4375 -0.796875 C 3.6875 -0.972656 3.8125 -1.222656 3.8125 -1.546875 C 3.8125 -1.835938 3.722656 -2.0625 3.546875 -2.21875 C 3.367188 -2.382812 3.082031 -2.507812 2.6875 -2.59375 L 2.203125 -2.6875 C 1.609375 -2.800781 1.179688 -2.984375 0.921875 -3.234375 C 0.660156 -3.484375 0.53125 -3.832031 0.53125 -4.28125 C 0.53125 -4.789062 0.710938 -5.191406 1.078125 -5.484375 C 1.441406 -5.785156 1.941406 -5.9375 2.578125 -5.9375 C 2.847656 -5.9375 3.125 -5.910156 3.40625 -5.859375 C 3.695312 -5.816406 3.988281 -5.742188 4.28125 -5.640625 Z M 4.28125 -5.640625 "/>
|
||||||
|
</symbol>
|
||||||
</g>
|
</g>
|
||||||
<clipPath id="clip1">
|
<clipPath id="clip1">
|
||||||
<path d="M 683.339844 48 L 725 48 L 725 59 L 683.339844 59 Z M 683.339844 48 "/>
|
<path d="M 1149.457031 48 L 1191 48 L 1191 59 L 1149.457031 59 Z M 1149.457031 48 "/>
|
||||||
</clipPath>
|
</clipPath>
|
||||||
<clipPath id="clip2">
|
<clipPath id="clip2">
|
||||||
<path d="M 812.351562 48 L 847.65625 48 L 847.65625 59 L 812.351562 59 Z M 812.351562 48 "/>
|
<path d="M 168.484375 80 L 210 80 L 210 91 L 168.484375 91 Z M 168.484375 80 "/>
|
||||||
</clipPath>
|
</clipPath>
|
||||||
<clipPath id="clip3">
|
<clipPath id="clip3">
|
||||||
<path d="M 1068.0625 48 L 1099.449219 48 L 1099.449219 59 L 1068.0625 59 Z M 1068.0625 48 "/>
|
<path d="M 945.597656 80 L 987 80 L 987 91 L 945.597656 91 Z M 945.597656 80 "/>
|
||||||
</clipPath>
|
</clipPath>
|
||||||
<clipPath id="clip4">
|
<clipPath id="clip4">
|
||||||
<path d="M 865.34375 80 L 898 80 L 898 91 L 865.34375 91 Z M 865.34375 80 "/>
|
<path d="M 250.011719 69 L 303 69 L 303 78 L 250.011719 78 Z M 250.011719 69 "/>
|
||||||
</clipPath>
|
</clipPath>
|
||||||
<clipPath id="clip5">
|
<clipPath id="clip5">
|
||||||
<path d="M 951.027344 80 L 981.15625 80 L 981.15625 91 L 951.027344 91 Z M 951.027344 80 "/>
|
<path d="M 979.003906 69 L 1003.683594 69 L 1003.683594 77 L 979.003906 77 Z M 979.003906 69 "/>
|
||||||
</clipPath>
|
</clipPath>
|
||||||
<clipPath id="clip6">
|
<clipPath id="clip6">
|
||||||
<path d="M 764.328125 112 L 801.253906 112 L 801.253906 123 L 764.328125 123 Z M 764.328125 112 "/>
|
<path d="M 1018.117188 112 L 1060 112 L 1060 123 L 1018.117188 123 Z M 1018.117188 112 "/>
|
||||||
</clipPath>
|
</clipPath>
|
||||||
<clipPath id="clip7">
|
<clipPath id="clip7">
|
||||||
<path d="M 910 112 L 939.957031 112 L 939.957031 123 L 910 123 Z M 910 112 "/>
|
<path d="M 583.375 101 L 637 101 L 637 110 L 583.375 110 Z M 583.375 101 "/>
|
||||||
</clipPath>
|
</clipPath>
|
||||||
<clipPath id="clip8">
|
<clipPath id="clip8">
|
||||||
<path d="M 1007.089844 112 L 1022.230469 112 L 1022.230469 123 L 1007.089844 123 Z M 1007.089844 112 "/>
|
<path d="M 1049.300781 101 L 1070.394531 101 L 1070.394531 109 L 1049.300781 109 Z M 1049.300781 101 "/>
|
||||||
</clipPath>
|
</clipPath>
|
||||||
<clipPath id="clip9">
|
<clipPath id="clip9">
|
||||||
<path d="M 789.792969 101 L 809 101 L 809 110 L 789.792969 110 Z M 789.792969 101 "/>
|
<path d="M 1082.304688 144 L 1124 144 L 1124 155 L 1082.304688 155 Z M 1082.304688 144 "/>
|
||||||
</clipPath>
|
</clipPath>
|
||||||
<clipPath id="clip10">
|
<clipPath id="clip10">
|
||||||
<path d="M 931.160156 101 L 950 101 L 950 110 L 931.160156 110 Z M 931.160156 101 "/>
|
<path d="M 819.433594 133 L 873 133 L 873 142 L 819.433594 142 Z M 819.433594 133 "/>
|
||||||
</clipPath>
|
</clipPath>
|
||||||
<clipPath id="clip11">
|
<clipPath id="clip11">
|
||||||
<path d="M 1013.980469 101 L 1033 101 L 1033 110 L 1013.980469 110 Z M 1013.980469 101 "/>
|
<path d="M 1112 133 L 1132.316406 133 L 1132.316406 141 L 1112 141 Z M 1112 133 "/>
|
||||||
</clipPath>
|
</clipPath>
|
||||||
<clipPath id="clip12">
|
<clipPath id="clip12">
|
||||||
<path d="M 836.167969 37 L 1028 37 L 1028 46 L 836.167969 46 Z M 836.167969 37 "/>
|
<path d="M 901.039062 37 L 985 37 L 985 45 L 901.039062 45 Z M 901.039062 37 "/>
|
||||||
</clipPath>
|
</clipPath>
|
||||||
<clipPath id="clip13">
|
<clipPath id="clip13">
|
||||||
<path d="M 1088.320312 37 L 1158 37 L 1158 45 L 1088.320312 45 Z M 1088.320312 37 "/>
|
<path d="M 1179.296875 37 L 1200.945312 37 L 1200.945312 45 L 1179.296875 45 Z M 1179.296875 37 "/>
|
||||||
</clipPath>
|
</clipPath>
|
||||||
</defs>
|
</defs>
|
||||||
<g id="surface2">
|
<g id="surface2">
|
||||||
<rect x="0" y="0" width="1280" height="152" style="fill:rgb(90%,90%,90%);fill-opacity:1;stroke:none;"/>
|
<rect x="0" y="0" width="1280" height="184" style="fill:rgb(90%,90%,90%);fill-opacity:1;stroke:none;"/>
|
||||||
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(70%,70%,70%);stroke-opacity:1;stroke-miterlimit:10;" d="M 4 0 L 4 152 "/>
|
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(70%,70%,70%);stroke-opacity:1;stroke-miterlimit:10;" d="M 4 0 L 4 184 "/>
|
||||||
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(70%,70%,70%);stroke-opacity:1;stroke-miterlimit:10;" d="M 155.40625 0 L 155.40625 152 "/>
|
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(70%,70%,70%);stroke-opacity:1;stroke-miterlimit:10;" d="M 195.652344 0 L 195.652344 184 "/>
|
||||||
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(70%,70%,70%);stroke-opacity:1;stroke-miterlimit:10;" d="M 306.808594 0 L 306.808594 152 "/>
|
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(70%,70%,70%);stroke-opacity:1;stroke-miterlimit:10;" d="M 387.304688 0 L 387.304688 184 "/>
|
||||||
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(70%,70%,70%);stroke-opacity:1;stroke-miterlimit:10;" d="M 458.214844 0 L 458.214844 152 "/>
|
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(70%,70%,70%);stroke-opacity:1;stroke-miterlimit:10;" d="M 578.957031 0 L 578.957031 184 "/>
|
||||||
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(70%,70%,70%);stroke-opacity:1;stroke-miterlimit:10;" d="M 609.621094 0 L 609.621094 152 "/>
|
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(70%,70%,70%);stroke-opacity:1;stroke-miterlimit:10;" d="M 770.613281 0 L 770.613281 184 "/>
|
||||||
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(70%,70%,70%);stroke-opacity:1;stroke-miterlimit:10;" d="M 761.027344 0 L 761.027344 152 "/>
|
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(70%,70%,70%);stroke-opacity:1;stroke-miterlimit:10;" d="M 962.265625 0 L 962.265625 184 "/>
|
||||||
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(70%,70%,70%);stroke-opacity:1;stroke-miterlimit:10;" d="M 912.429688 0 L 912.429688 152 "/>
|
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(70%,70%,70%);stroke-opacity:1;stroke-miterlimit:10;" d="M 1153.917969 0 L 1153.917969 184 "/>
|
||||||
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(70%,70%,70%);stroke-opacity:1;stroke-miterlimit:10;" d="M 1063.835938 0 L 1063.835938 152 "/>
|
|
||||||
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(70%,70%,70%);stroke-opacity:1;stroke-miterlimit:10;" d="M 1215.242188 0 L 1215.242188 152 "/>
|
|
||||||
<g style="fill:rgb(40%,40%,40%);fill-opacity:1;">
|
<g style="fill:rgb(40%,40%,40%);fill-opacity:1;">
|
||||||
<use xlink:href="#glyph0-1" x="4" y="147.503906"/>
|
<use xlink:href="#glyph0-1" x="4" y="179.503906"/>
|
||||||
<use xlink:href="#glyph0-2" x="11.582031" y="147.503906"/>
|
<use xlink:href="#glyph0-2" x="11.582031" y="179.503906"/>
|
||||||
<use xlink:href="#glyph0-3" x="18.935547" y="147.503906"/>
|
<use xlink:href="#glyph0-3" x="18.935547" y="179.503906"/>
|
||||||
<use xlink:href="#glyph0-4" x="25.533203" y="147.503906"/>
|
<use xlink:href="#glyph0-4" x="25.533203" y="179.503906"/>
|
||||||
<use xlink:href="#glyph0-5" x="33.138672" y="147.503906"/>
|
<use xlink:href="#glyph0-5" x="33.138672" y="179.503906"/>
|
||||||
<use xlink:href="#glyph0-6" x="36.953125" y="147.503906"/>
|
<use xlink:href="#glyph0-6" x="36.953125" y="179.503906"/>
|
||||||
<use xlink:href="#glyph0-7" x="44.570312" y="147.503906"/>
|
<use xlink:href="#glyph0-7" x="44.570312" y="179.503906"/>
|
||||||
<use xlink:href="#glyph0-8" x="49.503906" y="147.503906"/>
|
<use xlink:href="#glyph0-8" x="49.503906" y="179.503906"/>
|
||||||
<use xlink:href="#glyph0-9" x="52.837891" y="147.503906"/>
|
<use xlink:href="#glyph0-9" x="52.837891" y="179.503906"/>
|
||||||
<use xlink:href="#glyph0-5" x="60.455078" y="147.503906"/>
|
<use xlink:href="#glyph0-5" x="60.455078" y="179.503906"/>
|
||||||
<use xlink:href="#glyph0-9" x="64.269531" y="147.503906"/>
|
<use xlink:href="#glyph0-9" x="64.269531" y="179.503906"/>
|
||||||
<use xlink:href="#glyph0-8" x="71.886719" y="147.503906"/>
|
<use xlink:href="#glyph0-8" x="71.886719" y="179.503906"/>
|
||||||
<use xlink:href="#glyph0-10" x="75.220703" y="147.503906"/>
|
<use xlink:href="#glyph0-10" x="75.220703" y="179.503906"/>
|
||||||
<use xlink:href="#glyph0-8" x="82.322266" y="147.503906"/>
|
<use xlink:href="#glyph0-8" x="82.322266" y="179.503906"/>
|
||||||
<use xlink:href="#glyph0-11" x="85.65625" y="147.503906"/>
|
<use xlink:href="#glyph0-11" x="85.65625" y="179.503906"/>
|
||||||
<use xlink:href="#glyph0-8" x="91.908203" y="147.503906"/>
|
<use xlink:href="#glyph0-8" x="91.908203" y="179.503906"/>
|
||||||
<use xlink:href="#glyph0-12" x="95.242188" y="147.503906"/>
|
<use xlink:href="#glyph0-12" x="95.242188" y="179.503906"/>
|
||||||
<use xlink:href="#glyph0-13" x="102.583984" y="147.503906"/>
|
<use xlink:href="#glyph0-13" x="102.583984" y="179.503906"/>
|
||||||
<use xlink:href="#glyph0-14" x="110.189453" y="147.503906"/>
|
<use xlink:href="#glyph0-14" x="110.189453" y="179.503906"/>
|
||||||
<use xlink:href="#glyph0-5" x="114.232422" y="147.503906"/>
|
<use xlink:href="#glyph0-5" x="114.232422" y="179.503906"/>
|
||||||
<use xlink:href="#glyph0-15" x="118.046875" y="147.503906"/>
|
<use xlink:href="#glyph0-15" x="118.046875" y="179.503906"/>
|
||||||
<use xlink:href="#glyph0-16" x="125.681641" y="147.503906"/>
|
<use xlink:href="#glyph0-16" x="125.681641" y="179.503906"/>
|
||||||
<use xlink:href="#glyph0-5" x="133.316406" y="147.503906"/>
|
<use xlink:href="#glyph0-5" x="133.316406" y="179.503906"/>
|
||||||
<use xlink:href="#glyph0-17" x="137.130859" y="147.503906"/>
|
<use xlink:href="#glyph0-17" x="137.130859" y="179.503906"/>
|
||||||
<use xlink:href="#glyph0-11" x="144.736328" y="147.503906"/>
|
<use xlink:href="#glyph0-11" x="144.736328" y="179.503906"/>
|
||||||
</g>
|
</g>
|
||||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 11.480469 46 L 18.324219 46 L 18.324219 60 L 11.480469 60 Z M 11.480469 46 "/>
|
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 10.746094 46 L 18.640625 46 L 18.640625 60 L 10.746094 60 Z M 10.746094 46 "/>
|
||||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 18.324219 46 L 93.210938 46 L 93.210938 60 L 18.324219 60 Z M 18.324219 46 "/>
|
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 18.640625 46 L 59.882812 46 L 59.882812 60 L 18.640625 60 Z M 18.640625 46 "/>
|
||||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,40%,40%);fill-opacity:1;" d="M 93.207031 46 L 671.605469 46 L 671.605469 60 L 93.207031 60 Z M 93.207031 46 "/>
|
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,40%,40%);fill-opacity:1;" d="M 59.886719 46 L 491.21875 46 L 491.21875 60 L 59.886719 60 Z M 59.886719 46 "/>
|
||||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 671.605469 46 L 681.339844 46 L 681.339844 60 L 671.605469 60 Z M 671.605469 46 "/>
|
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 491.21875 46 L 523.03125 46 L 523.03125 60 L 491.21875 60 Z M 491.21875 46 "/>
|
||||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(50%,90%,50%);fill-opacity:1;" d="M 681.339844 46 L 732.652344 46 L 732.652344 60 L 681.339844 60 Z M 681.339844 46 "/>
|
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,40%,40%);fill-opacity:1;" d="M 523.035156 46 L 618.25 46 L 618.25 60 L 523.035156 60 Z M 523.035156 46 "/>
|
||||||
|
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 618.246094 46 L 621.214844 46 L 621.214844 60 L 618.246094 60 Z M 618.246094 46 "/>
|
||||||
|
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,40%,40%);fill-opacity:1;" d="M 621.21875 46 L 864.0625 46 L 864.0625 60 L 621.21875 60 Z M 621.21875 46 "/>
|
||||||
|
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 864.0625 46 L 871 46 L 871 60 L 864.0625 60 Z M 864.0625 46 "/>
|
||||||
|
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(50%,90%,50%);fill-opacity:1;" d="M 871 46 L 918.414062 46 L 918.414062 60 L 871 60 Z M 871 46 "/>
|
||||||
|
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
|
||||||
|
<use xlink:href="#glyph0-18" x="873" y="58"/>
|
||||||
|
<use xlink:href="#glyph0-7" x="877.705078" y="58"/>
|
||||||
|
<use xlink:href="#glyph0-2" x="882.638672" y="58"/>
|
||||||
|
<use xlink:href="#glyph0-3" x="889.992188" y="58"/>
|
||||||
|
<use xlink:href="#glyph0-19" x="896.589844" y="58"/>
|
||||||
|
<use xlink:href="#glyph0-20" x="903.972656" y="58"/>
|
||||||
|
<use xlink:href="#glyph0-13" x="907.306641" y="58"/>
|
||||||
|
</g>
|
||||||
|
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 918.414062 46 L 927.882812 46 L 927.882812 60 L 918.414062 60 Z M 918.414062 46 "/>
|
||||||
|
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,40%,40%);fill-opacity:1;" d="M 927.882812 46 L 1134.214844 46 L 1134.214844 60 L 927.882812 60 Z M 927.882812 46 "/>
|
||||||
|
<g style="fill:rgb(100%,100%,100%);fill-opacity:1;">
|
||||||
|
<use xlink:href="#glyph0-21" x="929.882812" y="58"/>
|
||||||
|
<use xlink:href="#glyph0-22" x="937.5" y="58"/>
|
||||||
|
<use xlink:href="#glyph0-8" x="947.314453" y="58"/>
|
||||||
|
<use xlink:href="#glyph0-18" x="950.648438" y="58"/>
|
||||||
|
<use xlink:href="#glyph0-3" x="955.353516" y="58"/>
|
||||||
|
<use xlink:href="#glyph0-4" x="961.951172" y="58"/>
|
||||||
|
<use xlink:href="#glyph0-23" x="969.556641" y="58"/>
|
||||||
|
<use xlink:href="#glyph0-2" x="973.371094" y="58"/>
|
||||||
|
<use xlink:href="#glyph0-22" x="980.724609" y="58"/>
|
||||||
|
<use xlink:href="#glyph0-2" x="990.539062" y="58"/>
|
||||||
|
<use xlink:href="#glyph0-8" x="997.892578" y="58"/>
|
||||||
|
<use xlink:href="#glyph0-18" x="1001.226562" y="58"/>
|
||||||
|
<use xlink:href="#glyph0-24" x="1005.931641" y="58"/>
|
||||||
|
<use xlink:href="#glyph0-8" x="1011.931641" y="58"/>
|
||||||
|
<use xlink:href="#glyph0-9" x="1015.265625" y="58"/>
|
||||||
|
<use xlink:href="#glyph0-20" x="1022.882812" y="58"/>
|
||||||
|
<use xlink:href="#glyph0-19" x="1026.216797" y="58"/>
|
||||||
|
</g>
|
||||||
|
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 1134.214844 46 L 1147.457031 46 L 1147.457031 60 L 1134.214844 60 Z M 1134.214844 46 "/>
|
||||||
|
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(50%,90%,50%);fill-opacity:1;" d="M 1147.457031 46 L 1196.730469 46 L 1196.730469 60 L 1147.457031 60 Z M 1147.457031 46 "/>
|
||||||
<g clip-path="url(#clip1)" clip-rule="nonzero">
|
<g clip-path="url(#clip1)" clip-rule="nonzero">
|
||||||
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
|
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
|
||||||
<use xlink:href="#glyph0-18" x="683.339844" y="58"/>
|
<use xlink:href="#glyph0-18" x="1149.457031" y="58"/>
|
||||||
<use xlink:href="#glyph0-7" x="688.044922" y="58"/>
|
<use xlink:href="#glyph0-7" x="1154.162109" y="58"/>
|
||||||
<use xlink:href="#glyph0-2" x="692.978516" y="58"/>
|
<use xlink:href="#glyph0-2" x="1159.095703" y="58"/>
|
||||||
<use xlink:href="#glyph0-3" x="700.332031" y="58"/>
|
<use xlink:href="#glyph0-3" x="1166.449219" y="58"/>
|
||||||
<use xlink:href="#glyph0-19" x="706.929688" y="58"/>
|
<use xlink:href="#glyph0-19" x="1173.046875" y="58"/>
|
||||||
<use xlink:href="#glyph0-20" x="714.3125" y="58"/>
|
<use xlink:href="#glyph0-20" x="1180.429688" y="58"/>
|
||||||
<use xlink:href="#glyph0-13" x="717.646484" y="58"/>
|
<use xlink:href="#glyph0-13" x="1183.763672" y="58"/>
|
||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 732.652344 46 L 740.554688 46 L 740.554688 60 L 732.652344 60 Z M 732.652344 46 "/>
|
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 1196.730469 46 L 1200.945312 46 L 1200.945312 60 L 1196.730469 60 Z M 1196.730469 46 "/>
|
||||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,40%,40%);fill-opacity:1;" d="M 740.554688 46 L 805.128906 46 L 805.128906 60 L 740.554688 60 Z M 740.554688 46 "/>
|
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 59.886719 78 L 62.914062 78 L 62.914062 92 L 59.886719 92 Z M 59.886719 78 "/>
|
||||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 805.128906 46 L 810.351562 46 L 810.351562 60 L 805.128906 60 Z M 805.128906 46 "/>
|
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 62.914062 78 L 166.484375 78 L 166.484375 92 L 62.914062 92 Z M 62.914062 78 "/>
|
||||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(50%,90%,50%);fill-opacity:1;" d="M 810.351562 46 L 847.65625 46 L 847.65625 60 L 810.351562 60 Z M 810.351562 46 "/>
|
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(50%,90%,50%);fill-opacity:1;" d="M 166.484375 78 L 471.960938 78 L 471.960938 92 L 166.484375 92 Z M 166.484375 78 "/>
|
||||||
<g clip-path="url(#clip2)" clip-rule="nonzero">
|
<g clip-path="url(#clip2)" clip-rule="nonzero">
|
||||||
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
|
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
|
||||||
<use xlink:href="#glyph0-18" x="812.351562" y="58"/>
|
<use xlink:href="#glyph0-18" x="168.484375" y="90"/>
|
||||||
<use xlink:href="#glyph0-7" x="817.056641" y="58"/>
|
<use xlink:href="#glyph0-7" x="173.189453" y="90"/>
|
||||||
<use xlink:href="#glyph0-2" x="821.990234" y="58"/>
|
<use xlink:href="#glyph0-2" x="178.123047" y="90"/>
|
||||||
<use xlink:href="#glyph0-3" x="829.34375" y="58"/>
|
<use xlink:href="#glyph0-3" x="185.476562" y="90"/>
|
||||||
<use xlink:href="#glyph0-19" x="835.941406" y="58"/>
|
<use xlink:href="#glyph0-19" x="192.074219" y="90"/>
|
||||||
<use xlink:href="#glyph0-20" x="843.324219" y="58"/>
|
<use xlink:href="#glyph0-20" x="199.457031" y="90"/>
|
||||||
<use xlink:href="#glyph0-13" x="846.658203" y="58"/>
|
<use xlink:href="#glyph0-13" x="202.791016" y="90"/>
|
||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 847.660156 46 L 852.671875 46 L 852.671875 60 L 847.660156 60 Z M 847.660156 46 "/>
|
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 471.957031 78 L 483.074219 78 L 483.074219 92 L 471.957031 92 Z M 471.957031 78 "/>
|
||||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,40%,40%);fill-opacity:1;" d="M 852.671875 46 L 1053.917969 46 L 1053.917969 60 L 852.671875 60 Z M 852.671875 46 "/>
|
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,40%,40%);fill-opacity:1;" d="M 483.074219 78 L 932.367188 78 L 932.367188 92 L 483.074219 92 Z M 483.074219 78 "/>
|
||||||
<g style="fill:rgb(100%,100%,100%);fill-opacity:1;">
|
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 932.367188 78 L 943.597656 78 L 943.597656 92 L 932.367188 92 Z M 932.367188 78 "/>
|
||||||
<use xlink:href="#glyph0-21" x="854.671875" y="58"/>
|
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(50%,90%,50%);fill-opacity:1;" d="M 943.597656 78 L 996.683594 78 L 996.683594 92 L 943.597656 92 Z M 943.597656 78 "/>
|
||||||
<use xlink:href="#glyph0-22" x="862.289062" y="58"/>
|
|
||||||
<use xlink:href="#glyph0-8" x="872.103516" y="58"/>
|
|
||||||
<use xlink:href="#glyph0-18" x="875.4375" y="58"/>
|
|
||||||
<use xlink:href="#glyph0-3" x="880.142578" y="58"/>
|
|
||||||
<use xlink:href="#glyph0-4" x="886.740234" y="58"/>
|
|
||||||
<use xlink:href="#glyph0-23" x="894.345703" y="58"/>
|
|
||||||
<use xlink:href="#glyph0-2" x="898.160156" y="58"/>
|
|
||||||
<use xlink:href="#glyph0-22" x="905.513672" y="58"/>
|
|
||||||
<use xlink:href="#glyph0-2" x="915.328125" y="58"/>
|
|
||||||
<use xlink:href="#glyph0-8" x="922.681641" y="58"/>
|
|
||||||
<use xlink:href="#glyph0-18" x="926.015625" y="58"/>
|
|
||||||
<use xlink:href="#glyph0-24" x="930.720703" y="58"/>
|
|
||||||
<use xlink:href="#glyph0-8" x="936.720703" y="58"/>
|
|
||||||
<use xlink:href="#glyph0-9" x="940.054688" y="58"/>
|
|
||||||
<use xlink:href="#glyph0-20" x="947.671875" y="58"/>
|
|
||||||
<use xlink:href="#glyph0-19" x="951.005859" y="58"/>
|
|
||||||
</g>
|
|
||||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 1053.917969 46 L 1066.0625 46 L 1066.0625 60 L 1053.917969 60 Z M 1053.917969 46 "/>
|
|
||||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(50%,90%,50%);fill-opacity:1;" d="M 1066.0625 46 L 1099.449219 46 L 1099.449219 60 L 1066.0625 60 Z M 1066.0625 46 "/>
|
|
||||||
<g clip-path="url(#clip3)" clip-rule="nonzero">
|
<g clip-path="url(#clip3)" clip-rule="nonzero">
|
||||||
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
|
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
|
||||||
<use xlink:href="#glyph0-18" x="1068.0625" y="58"/>
|
<use xlink:href="#glyph0-18" x="945.597656" y="90"/>
|
||||||
<use xlink:href="#glyph0-7" x="1072.767578" y="58"/>
|
<use xlink:href="#glyph0-7" x="950.302734" y="90"/>
|
||||||
<use xlink:href="#glyph0-2" x="1077.701172" y="58"/>
|
<use xlink:href="#glyph0-2" x="955.236328" y="90"/>
|
||||||
<use xlink:href="#glyph0-3" x="1085.054688" y="58"/>
|
<use xlink:href="#glyph0-3" x="962.589844" y="90"/>
|
||||||
<use xlink:href="#glyph0-19" x="1091.652344" y="58"/>
|
<use xlink:href="#glyph0-19" x="969.1875" y="90"/>
|
||||||
<use xlink:href="#glyph0-20" x="1099.035156" y="58"/>
|
<use xlink:href="#glyph0-20" x="976.570312" y="90"/>
|
||||||
<use xlink:href="#glyph0-13" x="1102.369141" y="58"/>
|
<use xlink:href="#glyph0-13" x="979.904297" y="90"/>
|
||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 1099.445312 46 L 1190.574219 46 L 1190.574219 60 L 1099.445312 60 Z M 1099.445312 46 "/>
|
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 996.683594 78 L 1006.457031 78 L 1006.457031 92 L 996.683594 92 Z M 996.683594 78 "/>
|
||||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 93.207031 78 L 97.839844 78 L 97.839844 92 L 93.207031 92 Z M 93.207031 78 "/>
|
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,40%,40%);fill-opacity:1;" d="M 1006.460938 78 L 1003.683594 78 L 1003.683594 92 L 1006.460938 92 Z M 1006.460938 78 "/>
|
||||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 97.839844 78 L 311.714844 78 L 311.714844 92 L 97.839844 92 Z M 97.839844 78 "/>
|
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 248.011719 81 L 248.011719 75 "/>
|
||||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(50%,90%,50%);fill-opacity:1;" d="M 311.714844 78 L 657.222656 78 L 657.222656 92 L 311.714844 92 Z M 311.714844 78 "/>
|
|
||||||
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
|
|
||||||
<use xlink:href="#glyph0-18" x="313.714844" y="90"/>
|
|
||||||
<use xlink:href="#glyph0-7" x="318.419922" y="90"/>
|
|
||||||
<use xlink:href="#glyph0-2" x="323.353516" y="90"/>
|
|
||||||
<use xlink:href="#glyph0-3" x="330.707031" y="90"/>
|
|
||||||
<use xlink:href="#glyph0-19" x="337.304688" y="90"/>
|
|
||||||
<use xlink:href="#glyph0-20" x="344.6875" y="90"/>
|
|
||||||
<use xlink:href="#glyph0-13" x="348.021484" y="90"/>
|
|
||||||
</g>
|
|
||||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 657.222656 78 L 662.550781 78 L 662.550781 92 L 657.222656 92 Z M 657.222656 78 "/>
|
|
||||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,40%,40%);fill-opacity:1;" d="M 662.550781 78 L 856.289062 78 L 856.289062 92 L 662.550781 92 Z M 662.550781 78 "/>
|
|
||||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 856.289062 78 L 863.34375 78 L 863.34375 92 L 856.289062 92 Z M 856.289062 78 "/>
|
|
||||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(50%,90%,50%);fill-opacity:1;" d="M 863.34375 78 L 898 78 L 898 92 L 863.34375 92 Z M 863.34375 78 "/>
|
|
||||||
<g clip-path="url(#clip4)" clip-rule="nonzero">
|
<g clip-path="url(#clip4)" clip-rule="nonzero">
|
||||||
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
|
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
|
||||||
<use xlink:href="#glyph0-18" x="865.34375" y="90"/>
|
<use xlink:href="#glyph1-1" x="250.011719" y="76"/>
|
||||||
<use xlink:href="#glyph0-7" x="870.048828" y="90"/>
|
<use xlink:href="#glyph1-2" x="252.371094" y="76"/>
|
||||||
<use xlink:href="#glyph0-2" x="874.982422" y="90"/>
|
<use xlink:href="#glyph1-3" x="257.265625" y="76"/>
|
||||||
<use xlink:href="#glyph0-3" x="882.335938" y="90"/>
|
<use xlink:href="#glyph1-4" x="262.34375" y="76"/>
|
||||||
<use xlink:href="#glyph0-19" x="888.933594" y="90"/>
|
<use xlink:href="#glyph1-5" x="264.886719" y="76"/>
|
||||||
<use xlink:href="#glyph0-20" x="896.316406" y="90"/>
|
<use xlink:href="#glyph1-4" x="269.976562" y="76"/>
|
||||||
<use xlink:href="#glyph0-13" x="899.650391" y="90"/>
|
<use xlink:href="#glyph1-6" x="272.519531" y="76"/>
|
||||||
|
<use xlink:href="#glyph1-7" x="276.6875" y="76"/>
|
||||||
|
<use xlink:href="#glyph1-8" x="279.824219" y="76"/>
|
||||||
|
<use xlink:href="#glyph1-9" x="284.726562" y="76"/>
|
||||||
|
<use xlink:href="#glyph1-7" x="288.015625" y="76"/>
|
||||||
|
<use xlink:href="#glyph1-10" x="291.152344" y="76"/>
|
||||||
|
<use xlink:href="#glyph1-11" x="293.375" y="76"/>
|
||||||
|
<use xlink:href="#glyph1-12" x="298.445312" y="76"/>
|
||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 898 78 L 899.648438 78 L 899.648438 92 L 898 92 Z M 898 78 "/>
|
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 977.003906 81 L 977.003906 75 "/>
|
||||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,40%,40%);fill-opacity:1;" d="M 899.652344 78 L 943.257812 78 L 943.257812 92 L 899.652344 92 Z M 899.652344 78 "/>
|
|
||||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 943.257812 78 L 949.027344 78 L 949.027344 92 L 943.257812 92 Z M 943.257812 78 "/>
|
|
||||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(50%,90%,50%);fill-opacity:1;" d="M 949.027344 78 L 981.15625 78 L 981.15625 92 L 949.027344 92 Z M 949.027344 78 "/>
|
|
||||||
<g clip-path="url(#clip5)" clip-rule="nonzero">
|
<g clip-path="url(#clip5)" clip-rule="nonzero">
|
||||||
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
|
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
|
||||||
<use xlink:href="#glyph0-18" x="951.027344" y="90"/>
|
<use xlink:href="#glyph1-5" x="979.003906" y="76"/>
|
||||||
<use xlink:href="#glyph0-7" x="955.732422" y="90"/>
|
<use xlink:href="#glyph1-4" x="984.09375" y="76"/>
|
||||||
<use xlink:href="#glyph0-2" x="960.666016" y="90"/>
|
<use xlink:href="#glyph1-13" x="986.636719" y="76"/>
|
||||||
<use xlink:href="#glyph0-3" x="968.019531" y="90"/>
|
<use xlink:href="#glyph1-2" x="991.714844" y="76"/>
|
||||||
<use xlink:href="#glyph0-19" x="974.617188" y="90"/>
|
<use xlink:href="#glyph1-11" x="996.609375" y="76"/>
|
||||||
<use xlink:href="#glyph0-20" x="982" y="90"/>
|
<use xlink:href="#glyph1-14" x="1001.679688" y="76"/>
|
||||||
<use xlink:href="#glyph0-13" x="985.333984" y="90"/>
|
|
||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 981.152344 78 L 982.46875 78 L 982.46875 92 L 981.152344 92 Z M 981.152344 78 "/>
|
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(40%,80%,40%);stroke-opacity:1;stroke-miterlimit:10;" d="M 59.886719 46 L 59.886719 92 "/>
|
||||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,40%,40%);fill-opacity:1;" d="M 982.472656 78 L 1041.113281 78 L 1041.113281 92 L 982.472656 92 Z M 982.472656 78 "/>
|
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 523.035156 110 L 525.679688 110 L 525.679688 124 L 523.035156 124 Z M 523.035156 110 "/>
|
||||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 1041.109375 78 L 1048.574219 78 L 1048.574219 92 L 1041.109375 92 Z M 1041.109375 78 "/>
|
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 525.679688 110 L 544.980469 110 L 544.980469 124 L 525.679688 124 Z M 525.679688 110 "/>
|
||||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,40%,40%);fill-opacity:1;" d="M 1048.574219 78 L 1046.953125 78 L 1046.953125 92 L 1048.574219 92 Z M 1048.574219 78 "/>
|
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(50%,90%,50%);fill-opacity:1;" d="M 544.976562 110 L 611.632812 110 L 611.632812 124 L 544.976562 124 Z M 544.976562 110 "/>
|
||||||
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 430.597656 81 L 430.597656 75 "/>
|
|
||||||
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
|
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
|
||||||
<use xlink:href="#glyph1-1" x="432.597656" y="76"/>
|
<use xlink:href="#glyph0-18" x="546.976562" y="122"/>
|
||||||
<use xlink:href="#glyph1-2" x="434.820312" y="76"/>
|
<use xlink:href="#glyph0-7" x="551.681641" y="122"/>
|
||||||
<use xlink:href="#glyph1-3" x="437.363281" y="76"/>
|
<use xlink:href="#glyph0-2" x="556.615234" y="122"/>
|
||||||
<use xlink:href="#glyph1-2" x="444.066406" y="76"/>
|
<use xlink:href="#glyph0-3" x="563.96875" y="122"/>
|
||||||
<use xlink:href="#glyph1-4" x="446.609375" y="76"/>
|
<use xlink:href="#glyph0-19" x="570.566406" y="122"/>
|
||||||
|
<use xlink:href="#glyph0-20" x="577.949219" y="122"/>
|
||||||
|
<use xlink:href="#glyph0-13" x="581.283203" y="122"/>
|
||||||
</g>
|
</g>
|
||||||
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 886.390625 81 L 886.390625 75 "/>
|
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 611.636719 110 L 615.625 110 L 615.625 124 L 611.636719 124 Z M 611.636719 110 "/>
|
||||||
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
|
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,40%,40%);fill-opacity:1;" d="M 615.621094 110 L 1006.457031 110 L 1006.457031 124 L 615.621094 124 Z M 615.621094 110 "/>
|
||||||
<use xlink:href="#glyph1-1" x="888.390625" y="76"/>
|
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 1006.460938 110 L 1016.121094 110 L 1016.121094 124 L 1006.460938 124 Z M 1006.460938 110 "/>
|
||||||
<use xlink:href="#glyph1-2" x="890.613281" y="76"/>
|
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(50%,90%,50%);fill-opacity:1;" d="M 1016.117188 110 L 1065.160156 110 L 1065.160156 124 L 1016.117188 124 Z M 1016.117188 110 "/>
|
||||||
<use xlink:href="#glyph1-3" x="893.15625" y="76"/>
|
|
||||||
<use xlink:href="#glyph1-2" x="899.859375" y="76"/>
|
|
||||||
<use xlink:href="#glyph1-5" x="902.402344" y="76"/>
|
|
||||||
</g>
|
|
||||||
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 970.753906 81 L 970.753906 75 "/>
|
|
||||||
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
|
|
||||||
<use xlink:href="#glyph1-1" x="972.753906" y="76"/>
|
|
||||||
<use xlink:href="#glyph1-2" x="974.976562" y="76"/>
|
|
||||||
<use xlink:href="#glyph1-3" x="977.519531" y="76"/>
|
|
||||||
<use xlink:href="#glyph1-2" x="984.222656" y="76"/>
|
|
||||||
<use xlink:href="#glyph1-6" x="986.765625" y="76"/>
|
|
||||||
</g>
|
|
||||||
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(40%,80%,40%);stroke-opacity:1;stroke-miterlimit:10;" d="M 93.207031 46 L 93.207031 92 "/>
|
|
||||||
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 713.289062 49 L 713.289062 43 "/>
|
|
||||||
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
|
|
||||||
<use xlink:href="#glyph1-7" x="715.289062" y="44"/>
|
|
||||||
<use xlink:href="#glyph1-1" x="719.890625" y="44"/>
|
|
||||||
<use xlink:href="#glyph1-8" x="722.113281" y="44"/>
|
|
||||||
<use xlink:href="#glyph1-9" x="725.402344" y="44"/>
|
|
||||||
<use xlink:href="#glyph1-10" x="729.570312" y="44"/>
|
|
||||||
<use xlink:href="#glyph1-2" x="732.707031" y="44"/>
|
|
||||||
<use xlink:href="#glyph1-10" x="735.25" y="44"/>
|
|
||||||
<use xlink:href="#glyph1-11" x="738.386719" y="44"/>
|
|
||||||
<use xlink:href="#glyph1-8" x="743.457031" y="44"/>
|
|
||||||
<use xlink:href="#glyph1-12" x="746.746094" y="44"/>
|
|
||||||
<use xlink:href="#glyph1-13" x="751.667969" y="44"/>
|
|
||||||
<use xlink:href="#glyph1-14" x="756.570312" y="44"/>
|
|
||||||
<use xlink:href="#glyph1-2" x="761.648438" y="44"/>
|
|
||||||
<use xlink:href="#glyph1-15" x="764.191406" y="44"/>
|
|
||||||
<use xlink:href="#glyph1-16" x="767.007812" y="44"/>
|
|
||||||
<use xlink:href="#glyph1-8" x="771.902344" y="44"/>
|
|
||||||
<use xlink:href="#glyph1-17" x="775.191406" y="44"/>
|
|
||||||
<use xlink:href="#glyph1-12" x="779.824219" y="44"/>
|
|
||||||
<use xlink:href="#glyph1-14" x="784.746094" y="44"/>
|
|
||||||
</g>
|
|
||||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 740.554688 110 L 742.976562 110 L 742.976562 124 L 740.554688 124 Z M 740.554688 110 "/>
|
|
||||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 742.976562 110 L 762.324219 110 L 762.324219 124 L 742.976562 124 Z M 742.976562 110 "/>
|
|
||||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(50%,90%,50%);fill-opacity:1;" d="M 762.328125 110 L 801.253906 110 L 801.253906 124 L 762.328125 124 Z M 762.328125 110 "/>
|
|
||||||
<g clip-path="url(#clip6)" clip-rule="nonzero">
|
<g clip-path="url(#clip6)" clip-rule="nonzero">
|
||||||
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
|
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
|
||||||
<use xlink:href="#glyph0-18" x="764.328125" y="122"/>
|
<use xlink:href="#glyph0-18" x="1018.117188" y="122"/>
|
||||||
<use xlink:href="#glyph0-7" x="769.033203" y="122"/>
|
<use xlink:href="#glyph0-7" x="1022.822266" y="122"/>
|
||||||
<use xlink:href="#glyph0-2" x="773.966797" y="122"/>
|
<use xlink:href="#glyph0-2" x="1027.755859" y="122"/>
|
||||||
<use xlink:href="#glyph0-3" x="781.320312" y="122"/>
|
<use xlink:href="#glyph0-3" x="1035.109375" y="122"/>
|
||||||
<use xlink:href="#glyph0-19" x="787.917969" y="122"/>
|
<use xlink:href="#glyph0-19" x="1041.707031" y="122"/>
|
||||||
<use xlink:href="#glyph0-20" x="795.300781" y="122"/>
|
<use xlink:href="#glyph0-20" x="1049.089844" y="122"/>
|
||||||
<use xlink:href="#glyph0-13" x="798.634766" y="122"/>
|
<use xlink:href="#glyph0-13" x="1052.423828" y="122"/>
|
||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 801.253906 110 L 803.496094 110 L 803.496094 124 L 801.253906 124 Z M 801.253906 110 "/>
|
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 1065.164062 110 L 1071.8125 110 L 1071.8125 124 L 1065.164062 124 Z M 1065.164062 110 "/>
|
||||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,40%,40%);fill-opacity:1;" d="M 803.496094 110 L 901.589844 110 L 901.589844 124 L 803.496094 124 Z M 803.496094 110 "/>
|
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,40%,40%);fill-opacity:1;" d="M 1071.8125 110 L 1070.394531 110 L 1070.394531 124 L 1071.8125 124 Z M 1071.8125 110 "/>
|
||||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 901.589844 110 L 907.84375 110 L 907.84375 124 L 901.589844 124 Z M 901.589844 110 "/>
|
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 581.375 113 L 581.375 107 "/>
|
||||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(50%,90%,50%);fill-opacity:1;" d="M 907.84375 110 L 939.957031 110 L 939.957031 124 L 907.84375 124 Z M 907.84375 110 "/>
|
|
||||||
<g clip-path="url(#clip7)" clip-rule="nonzero">
|
<g clip-path="url(#clip7)" clip-rule="nonzero">
|
||||||
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
|
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
|
||||||
<use xlink:href="#glyph0-18" x="909.84375" y="122"/>
|
<use xlink:href="#glyph1-1" x="583.375" y="108"/>
|
||||||
<use xlink:href="#glyph0-7" x="914.548828" y="122"/>
|
<use xlink:href="#glyph1-2" x="585.734375" y="108"/>
|
||||||
<use xlink:href="#glyph0-2" x="919.482422" y="122"/>
|
<use xlink:href="#glyph1-3" x="590.628906" y="108"/>
|
||||||
<use xlink:href="#glyph0-3" x="926.835938" y="122"/>
|
<use xlink:href="#glyph1-4" x="595.707031" y="108"/>
|
||||||
<use xlink:href="#glyph0-19" x="933.433594" y="122"/>
|
<use xlink:href="#glyph1-15" x="598.25" y="108"/>
|
||||||
<use xlink:href="#glyph0-20" x="940.816406" y="122"/>
|
<use xlink:href="#glyph1-4" x="603.339844" y="108"/>
|
||||||
<use xlink:href="#glyph0-13" x="944.150391" y="122"/>
|
<use xlink:href="#glyph1-6" x="605.882812" y="108"/>
|
||||||
|
<use xlink:href="#glyph1-7" x="610.050781" y="108"/>
|
||||||
|
<use xlink:href="#glyph1-8" x="613.1875" y="108"/>
|
||||||
|
<use xlink:href="#glyph1-9" x="618.089844" y="108"/>
|
||||||
|
<use xlink:href="#glyph1-7" x="621.378906" y="108"/>
|
||||||
|
<use xlink:href="#glyph1-10" x="624.515625" y="108"/>
|
||||||
|
<use xlink:href="#glyph1-11" x="626.738281" y="108"/>
|
||||||
|
<use xlink:href="#glyph1-12" x="631.808594" y="108"/>
|
||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 939.957031 110 L 941.289062 110 L 941.289062 124 L 939.957031 124 Z M 939.957031 110 "/>
|
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 1047.300781 113 L 1047.300781 107 "/>
|
||||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,40%,40%);fill-opacity:1;" d="M 941.289062 110 L 984.167969 110 L 984.167969 124 L 941.289062 124 Z M 941.289062 110 "/>
|
|
||||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 984.167969 110 L 1005.09375 110 L 1005.09375 124 L 984.167969 124 Z M 984.167969 110 "/>
|
|
||||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(50%,90%,50%);fill-opacity:1;" d="M 1005.089844 110 L 1022.230469 110 L 1022.230469 124 L 1005.089844 124 Z M 1005.089844 110 "/>
|
|
||||||
<g clip-path="url(#clip8)" clip-rule="nonzero">
|
<g clip-path="url(#clip8)" clip-rule="nonzero">
|
||||||
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
|
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
|
||||||
<use xlink:href="#glyph0-18" x="1007.089844" y="122"/>
|
<use xlink:href="#glyph1-15" x="1049.300781" y="108"/>
|
||||||
<use xlink:href="#glyph0-7" x="1011.794922" y="122"/>
|
<use xlink:href="#glyph1-4" x="1054.390625" y="108"/>
|
||||||
<use xlink:href="#glyph0-2" x="1016.728516" y="122"/>
|
<use xlink:href="#glyph1-13" x="1056.933594" y="108"/>
|
||||||
<use xlink:href="#glyph0-3" x="1024.082031" y="122"/>
|
<use xlink:href="#glyph1-2" x="1062.011719" y="108"/>
|
||||||
<use xlink:href="#glyph0-19" x="1030.679688" y="122"/>
|
<use xlink:href="#glyph1-11" x="1066.90625" y="108"/>
|
||||||
<use xlink:href="#glyph0-20" x="1038.0625" y="122"/>
|
<use xlink:href="#glyph1-14" x="1071.976562" y="108"/>
|
||||||
<use xlink:href="#glyph0-13" x="1041.396484" y="122"/>
|
|
||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 1022.230469 110 L 1023.820312 110 L 1023.820312 124 L 1022.230469 124 Z M 1022.230469 110 "/>
|
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(40%,80%,40%);stroke-opacity:1;stroke-miterlimit:10;" d="M 523.035156 46 L 523.035156 124 "/>
|
||||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,40%,40%);fill-opacity:1;" d="M 1023.820312 110 L 1048.574219 110 L 1048.574219 124 L 1023.820312 124 Z M 1023.820312 110 "/>
|
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 621.21875 142 L 622.960938 142 L 622.960938 156 L 621.21875 156 Z M 621.21875 142 "/>
|
||||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 1048.574219 110 L 1053.917969 110 L 1053.917969 124 L 1048.574219 124 Z M 1048.574219 110 "/>
|
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 622.960938 142 L 634.785156 142 L 634.785156 156 L 622.960938 156 Z M 622.960938 142 "/>
|
||||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,40%,40%);fill-opacity:1;" d="M 1053.917969 110 L 1052.722656 110 L 1052.722656 124 L 1053.917969 124 Z M 1053.917969 110 "/>
|
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(50%,90%,50%);fill-opacity:1;" d="M 634.785156 142 L 838.511719 142 L 838.511719 156 L 634.785156 156 Z M 634.785156 142 "/>
|
||||||
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 787.792969 113 L 787.792969 107 "/>
|
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
|
||||||
|
<use xlink:href="#glyph0-18" x="636.785156" y="154"/>
|
||||||
|
<use xlink:href="#glyph0-7" x="641.490234" y="154"/>
|
||||||
|
<use xlink:href="#glyph0-2" x="646.423828" y="154"/>
|
||||||
|
<use xlink:href="#glyph0-3" x="653.777344" y="154"/>
|
||||||
|
<use xlink:href="#glyph0-19" x="660.375" y="154"/>
|
||||||
|
<use xlink:href="#glyph0-20" x="667.757812" y="154"/>
|
||||||
|
<use xlink:href="#glyph0-13" x="671.091797" y="154"/>
|
||||||
|
</g>
|
||||||
|
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 838.515625 142 L 861.878906 142 L 861.878906 156 L 838.515625 156 Z M 838.515625 142 "/>
|
||||||
|
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,40%,40%);fill-opacity:1;" d="M 861.875 142 L 1071.8125 142 L 1071.8125 156 L 861.875 156 Z M 861.875 142 "/>
|
||||||
|
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 1071.8125 142 L 1080.300781 142 L 1080.300781 156 L 1071.8125 156 Z M 1071.8125 142 "/>
|
||||||
|
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(50%,90%,50%);fill-opacity:1;" d="M 1080.304688 142 L 1126.570312 142 L 1126.570312 156 L 1080.304688 156 Z M 1080.304688 142 "/>
|
||||||
<g clip-path="url(#clip9)" clip-rule="nonzero">
|
<g clip-path="url(#clip9)" clip-rule="nonzero">
|
||||||
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
|
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
|
||||||
<use xlink:href="#glyph1-18" x="789.792969" y="108"/>
|
<use xlink:href="#glyph0-18" x="1082.304688" y="154"/>
|
||||||
<use xlink:href="#glyph1-2" x="792.015625" y="108"/>
|
<use xlink:href="#glyph0-7" x="1087.009766" y="154"/>
|
||||||
<use xlink:href="#glyph1-3" x="794.558594" y="108"/>
|
<use xlink:href="#glyph0-2" x="1091.943359" y="154"/>
|
||||||
<use xlink:href="#glyph1-2" x="801.261719" y="108"/>
|
<use xlink:href="#glyph0-3" x="1099.296875" y="154"/>
|
||||||
<use xlink:href="#glyph1-4" x="803.804688" y="108"/>
|
<use xlink:href="#glyph0-19" x="1105.894531" y="154"/>
|
||||||
|
<use xlink:href="#glyph0-20" x="1113.277344" y="154"/>
|
||||||
|
<use xlink:href="#glyph0-13" x="1116.611328" y="154"/>
|
||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 929.160156 113 L 929.160156 107 "/>
|
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 1126.566406 142 L 1134.214844 142 L 1134.214844 156 L 1126.566406 156 Z M 1126.566406 142 "/>
|
||||||
|
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,40%,40%);fill-opacity:1;" d="M 1134.214844 142 L 1132.316406 142 L 1132.316406 156 L 1134.214844 156 Z M 1134.214844 142 "/>
|
||||||
|
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 817.433594 145 L 817.433594 139 "/>
|
||||||
<g clip-path="url(#clip10)" clip-rule="nonzero">
|
<g clip-path="url(#clip10)" clip-rule="nonzero">
|
||||||
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
|
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
|
||||||
<use xlink:href="#glyph1-18" x="931.160156" y="108"/>
|
<use xlink:href="#glyph1-1" x="819.433594" y="140"/>
|
||||||
<use xlink:href="#glyph1-2" x="933.382812" y="108"/>
|
<use xlink:href="#glyph1-2" x="821.792969" y="140"/>
|
||||||
<use xlink:href="#glyph1-3" x="935.925781" y="108"/>
|
<use xlink:href="#glyph1-3" x="826.6875" y="140"/>
|
||||||
<use xlink:href="#glyph1-2" x="942.628906" y="108"/>
|
<use xlink:href="#glyph1-4" x="831.765625" y="140"/>
|
||||||
<use xlink:href="#glyph1-5" x="945.171875" y="108"/>
|
<use xlink:href="#glyph1-16" x="834.308594" y="140"/>
|
||||||
|
<use xlink:href="#glyph1-4" x="839.398438" y="140"/>
|
||||||
|
<use xlink:href="#glyph1-6" x="841.941406" y="140"/>
|
||||||
|
<use xlink:href="#glyph1-7" x="846.109375" y="140"/>
|
||||||
|
<use xlink:href="#glyph1-8" x="849.246094" y="140"/>
|
||||||
|
<use xlink:href="#glyph1-9" x="854.148438" y="140"/>
|
||||||
|
<use xlink:href="#glyph1-7" x="857.4375" y="140"/>
|
||||||
|
<use xlink:href="#glyph1-10" x="860.574219" y="140"/>
|
||||||
|
<use xlink:href="#glyph1-11" x="862.796875" y="140"/>
|
||||||
|
<use xlink:href="#glyph1-12" x="867.867188" y="140"/>
|
||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 1011.980469 113 L 1011.980469 107 "/>
|
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 1109.605469 145 L 1109.605469 139 "/>
|
||||||
<g clip-path="url(#clip11)" clip-rule="nonzero">
|
<g clip-path="url(#clip11)" clip-rule="nonzero">
|
||||||
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
|
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
|
||||||
<use xlink:href="#glyph1-18" x="1013.980469" y="108"/>
|
<use xlink:href="#glyph1-16" x="1111.605469" y="140"/>
|
||||||
<use xlink:href="#glyph1-2" x="1016.203125" y="108"/>
|
<use xlink:href="#glyph1-4" x="1116.695312" y="140"/>
|
||||||
<use xlink:href="#glyph1-3" x="1018.746094" y="108"/>
|
<use xlink:href="#glyph1-13" x="1119.238281" y="140"/>
|
||||||
<use xlink:href="#glyph1-2" x="1025.449219" y="108"/>
|
<use xlink:href="#glyph1-2" x="1124.316406" y="140"/>
|
||||||
<use xlink:href="#glyph1-6" x="1027.992188" y="108"/>
|
<use xlink:href="#glyph1-11" x="1129.210938" y="140"/>
|
||||||
|
<use xlink:href="#glyph1-14" x="1134.28125" y="140"/>
|
||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(40%,80%,40%);stroke-opacity:1;stroke-miterlimit:10;" d="M 740.554688 46 L 740.554688 124 "/>
|
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(40%,80%,40%);stroke-opacity:1;stroke-miterlimit:10;" d="M 621.21875 46 L 621.21875 156 "/>
|
||||||
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 834.167969 49 L 834.167969 43 "/>
|
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 899.039062 49 L 899.039062 43 "/>
|
||||||
<g clip-path="url(#clip12)" clip-rule="nonzero">
|
<g clip-path="url(#clip12)" clip-rule="nonzero">
|
||||||
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
|
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
|
||||||
<use xlink:href="#glyph1-19" x="836.167969" y="44"/>
|
<use xlink:href="#glyph1-17" x="901.039062" y="44"/>
|
||||||
<use xlink:href="#glyph1-12" x="841.246094" y="44"/>
|
<use xlink:href="#glyph1-18" x="906.511719" y="44"/>
|
||||||
<use xlink:href="#glyph1-20" x="846.167969" y="44"/>
|
<use xlink:href="#glyph1-18" x="908.734375" y="44"/>
|
||||||
<use xlink:href="#glyph1-16" x="850.566406" y="44"/>
|
<use xlink:href="#glyph1-4" x="910.957031" y="44"/>
|
||||||
<use xlink:href="#glyph1-21" x="855.460938" y="44"/>
|
<use xlink:href="#glyph1-19" x="913.5" y="44"/>
|
||||||
<use xlink:href="#glyph1-14" x="860.53125" y="44"/>
|
<use xlink:href="#glyph1-20" x="917.898438" y="44"/>
|
||||||
<use xlink:href="#glyph1-2" x="865.609375" y="44"/>
|
<use xlink:href="#glyph1-10" x="922.96875" y="44"/>
|
||||||
<use xlink:href="#glyph1-10" x="868.152344" y="44"/>
|
<use xlink:href="#glyph1-18" x="925.191406" y="44"/>
|
||||||
<use xlink:href="#glyph1-11" x="871.289062" y="44"/>
|
<use xlink:href="#glyph1-13" x="927.414062" y="44"/>
|
||||||
<use xlink:href="#glyph1-8" x="876.359375" y="44"/>
|
<use xlink:href="#glyph1-4" x="932.492188" y="44"/>
|
||||||
<use xlink:href="#glyph1-12" x="879.648438" y="44"/>
|
<use xlink:href="#glyph1-21" x="935.035156" y="44"/>
|
||||||
<use xlink:href="#glyph1-13" x="884.570312" y="44"/>
|
<use xlink:href="#glyph1-10" x="937.851562" y="44"/>
|
||||||
<use xlink:href="#glyph1-14" x="889.472656" y="44"/>
|
<use xlink:href="#glyph1-3" x="940.074219" y="44"/>
|
||||||
<use xlink:href="#glyph1-2" x="894.550781" y="44"/>
|
<use xlink:href="#glyph1-14" x="945.152344" y="44"/>
|
||||||
<use xlink:href="#glyph1-15" x="897.09375" y="44"/>
|
<use xlink:href="#glyph1-9" x="950.074219" y="44"/>
|
||||||
<use xlink:href="#glyph1-16" x="899.910156" y="44"/>
|
<use xlink:href="#glyph1-6" x="953.363281" y="44"/>
|
||||||
<use xlink:href="#glyph1-8" x="904.804688" y="44"/>
|
<use xlink:href="#glyph1-4" x="957.53125" y="44"/>
|
||||||
<use xlink:href="#glyph1-17" x="908.09375" y="44"/>
|
<use xlink:href="#glyph1-21" x="960.074219" y="44"/>
|
||||||
<use xlink:href="#glyph1-12" x="912.726562" y="44"/>
|
<use xlink:href="#glyph1-2" x="962.890625" y="44"/>
|
||||||
<use xlink:href="#glyph1-14" x="917.648438" y="44"/>
|
<use xlink:href="#glyph1-9" x="967.785156" y="44"/>
|
||||||
<use xlink:href="#glyph1-22" x="922.726562" y="44"/>
|
<use xlink:href="#glyph1-22" x="971.074219" y="44"/>
|
||||||
<use xlink:href="#glyph1-2" x="925.421875" y="44"/>
|
<use xlink:href="#glyph1-14" x="975.707031" y="44"/>
|
||||||
<use xlink:href="#glyph1-10" x="927.964844" y="44"/>
|
<use xlink:href="#glyph1-13" x="980.628906" y="44"/>
|
||||||
<use xlink:href="#glyph1-16" x="931.101562" y="44"/>
|
|
||||||
<use xlink:href="#glyph1-23" x="935.996094" y="44"/>
|
|
||||||
<use xlink:href="#glyph1-24" x="941.074219" y="44"/>
|
|
||||||
<use xlink:href="#glyph1-25" x="943.960938" y="44"/>
|
|
||||||
<use xlink:href="#glyph1-12" x="946.183594" y="44"/>
|
|
||||||
<use xlink:href="#glyph1-26" x="951.105469" y="44"/>
|
|
||||||
<use xlink:href="#glyph1-12" x="955.839844" y="44"/>
|
|
||||||
<use xlink:href="#glyph1-25" x="960.761719" y="44"/>
|
|
||||||
<use xlink:href="#glyph1-2" x="962.984375" y="44"/>
|
|
||||||
<use xlink:href="#glyph1-20" x="965.527344" y="44"/>
|
|
||||||
<use xlink:href="#glyph1-16" x="969.925781" y="44"/>
|
|
||||||
<use xlink:href="#glyph1-14" x="974.820312" y="44"/>
|
|
||||||
<use xlink:href="#glyph1-12" x="979.898438" y="44"/>
|
|
||||||
<use xlink:href="#glyph1-2" x="984.820312" y="44"/>
|
|
||||||
<use xlink:href="#glyph1-1" x="987.363281" y="44"/>
|
|
||||||
<use xlink:href="#glyph1-9" x="989.585938" y="44"/>
|
|
||||||
<use xlink:href="#glyph1-2" x="993.753906" y="44"/>
|
|
||||||
<use xlink:href="#glyph1-15" x="996.296875" y="44"/>
|
|
||||||
<use xlink:href="#glyph1-1" x="999.113281" y="44"/>
|
|
||||||
<use xlink:href="#glyph1-21" x="1001.335938" y="44"/>
|
|
||||||
<use xlink:href="#glyph1-1" x="1006.40625" y="44"/>
|
|
||||||
<use xlink:href="#glyph1-9" x="1008.628906" y="44"/>
|
|
||||||
<use xlink:href="#glyph1-11" x="1012.796875" y="44"/>
|
|
||||||
<use xlink:href="#glyph1-12" x="1017.867188" y="44"/>
|
|
||||||
<use xlink:href="#glyph1-14" x="1022.789062" y="44"/>
|
|
||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 35.601562 36 L 31.601562 36 L 31.601562 128 L 35.601562 128 "/>
|
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 46.675781 36 L 42.675781 36 L 42.675781 160 L 46.675781 160 "/>
|
||||||
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 1057.957031 36 L 1061.957031 36 L 1061.957031 128 L 1057.957031 128 "/>
|
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 1138.78125 36 L 1142.78125 36 L 1142.78125 160 L 1138.78125 160 "/>
|
||||||
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
|
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
|
||||||
<use xlink:href="#glyph1-9" x="33.601562" y="44"/>
|
<use xlink:href="#glyph1-6" x="44.675781" y="44"/>
|
||||||
<use xlink:href="#glyph1-27" x="37.769531" y="44"/>
|
<use xlink:href="#glyph1-23" x="48.84375" y="44"/>
|
||||||
<use xlink:href="#glyph1-1" x="44.3125" y="44"/>
|
<use xlink:href="#glyph1-10" x="55.386719" y="44"/>
|
||||||
<use xlink:href="#glyph1-10" x="46.535156" y="44"/>
|
<use xlink:href="#glyph1-7" x="57.609375" y="44"/>
|
||||||
<use xlink:href="#glyph1-20" x="49.671875" y="44"/>
|
<use xlink:href="#glyph1-19" x="60.746094" y="44"/>
|
||||||
<use xlink:href="#glyph1-11" x="54.070312" y="44"/>
|
<use xlink:href="#glyph1-20" x="65.144531" y="44"/>
|
||||||
</g>
|
</g>
|
||||||
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 1086.320312 49 L 1086.320312 43 "/>
|
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 1177.296875 49 L 1177.296875 43 "/>
|
||||||
<g clip-path="url(#clip13)" clip-rule="nonzero">
|
<g clip-path="url(#clip13)" clip-rule="nonzero">
|
||||||
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
|
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
|
||||||
<use xlink:href="#glyph1-19" x="1088.320312" y="44"/>
|
<use xlink:href="#glyph1-24" x="1179.296875" y="44"/>
|
||||||
<use xlink:href="#glyph1-27" x="1093.398438" y="44"/>
|
<use xlink:href="#glyph1-23" x="1184.375" y="44"/>
|
||||||
<use xlink:href="#glyph1-1" x="1099.941406" y="44"/>
|
<use xlink:href="#glyph1-10" x="1190.917969" y="44"/>
|
||||||
<use xlink:href="#glyph1-10" x="1102.164062" y="44"/>
|
<use xlink:href="#glyph1-7" x="1193.140625" y="44"/>
|
||||||
<use xlink:href="#glyph1-20" x="1105.300781" y="44"/>
|
<use xlink:href="#glyph1-19" x="1196.277344" y="44"/>
|
||||||
<use xlink:href="#glyph1-11" x="1109.699219" y="44"/>
|
<use xlink:href="#glyph1-20" x="1200.675781" y="44"/>
|
||||||
<use xlink:href="#glyph1-2" x="1114.769531" y="44"/>
|
<use xlink:href="#glyph1-4" x="1205.746094" y="44"/>
|
||||||
<use xlink:href="#glyph1-1" x="1117.3125" y="44"/>
|
<use xlink:href="#glyph1-10" x="1208.289062" y="44"/>
|
||||||
<use xlink:href="#glyph1-9" x="1119.535156" y="44"/>
|
<use xlink:href="#glyph1-6" x="1210.511719" y="44"/>
|
||||||
<use xlink:href="#glyph1-2" x="1123.703125" y="44"/>
|
<use xlink:href="#glyph1-4" x="1214.679688" y="44"/>
|
||||||
<use xlink:href="#glyph1-15" x="1126.246094" y="44"/>
|
<use xlink:href="#glyph1-21" x="1217.222656" y="44"/>
|
||||||
<use xlink:href="#glyph1-1" x="1129.0625" y="44"/>
|
<use xlink:href="#glyph1-10" x="1220.039062" y="44"/>
|
||||||
<use xlink:href="#glyph1-21" x="1131.285156" y="44"/>
|
<use xlink:href="#glyph1-11" x="1222.261719" y="44"/>
|
||||||
<use xlink:href="#glyph1-1" x="1136.355469" y="44"/>
|
<use xlink:href="#glyph1-10" x="1227.332031" y="44"/>
|
||||||
<use xlink:href="#glyph1-9" x="1138.578125" y="44"/>
|
<use xlink:href="#glyph1-6" x="1229.554688" y="44"/>
|
||||||
<use xlink:href="#glyph1-11" x="1142.746094" y="44"/>
|
<use xlink:href="#glyph1-20" x="1233.722656" y="44"/>
|
||||||
<use xlink:href="#glyph1-12" x="1147.816406" y="44"/>
|
<use xlink:href="#glyph1-14" x="1238.792969" y="44"/>
|
||||||
<use xlink:href="#glyph1-14" x="1152.738281" y="44"/>
|
<use xlink:href="#glyph1-13" x="1243.714844" y="44"/>
|
||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(40%,80%,40%);stroke-opacity:1;stroke-miterlimit:10;" d="M 11.480469 46 L 11.480469 60 "/>
|
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(40%,80%,40%);stroke-opacity:1;stroke-miterlimit:10;" d="M 10.746094 46 L 10.746094 60 "/>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 61 KiB |
14
dune-project
14
dune-project
@ -1,6 +1,7 @@
|
|||||||
(lang dune 3.9)
|
(lang dune 3.9)
|
||||||
(name eio)
|
(name eio)
|
||||||
(formatting disabled)
|
(formatting disabled)
|
||||||
|
(subst disabled)
|
||||||
(generate_opam_files true)
|
(generate_opam_files true)
|
||||||
(source (github ocaml-multicore/eio))
|
(source (github ocaml-multicore/eio))
|
||||||
(license ISC)
|
(license ISC)
|
||||||
@ -13,7 +14,7 @@
|
|||||||
(description "An effect-based IO API for multicore OCaml with fibers.")
|
(description "An effect-based IO API for multicore OCaml with fibers.")
|
||||||
(conflicts (seq (< 0.3)))
|
(conflicts (seq (< 0.3)))
|
||||||
(depends
|
(depends
|
||||||
(ocaml (>= 5.1.0))
|
(ocaml (>= 5.2.0))
|
||||||
(bigstringaf (>= 0.9.0))
|
(bigstringaf (>= 0.9.0))
|
||||||
(cstruct (>= 6.0.1))
|
(cstruct (>= 6.0.1))
|
||||||
lwt-dllist
|
lwt-dllist
|
||||||
@ -24,7 +25,7 @@
|
|||||||
(domain-local-await (>= 0.1.0))
|
(domain-local-await (>= 0.1.0))
|
||||||
(crowbar (and (>= 0.2) :with-test))
|
(crowbar (and (>= 0.2) :with-test))
|
||||||
(mtime (>= 2.0.0))
|
(mtime (>= 2.0.0))
|
||||||
(mdx (and (>= 2.2.0) (< 2.4.0) :with-test))
|
(mdx (and (>= 2.4.1) :with-test))
|
||||||
(dscheck (and (>= 0.1.0) :with-test))))
|
(dscheck (and (>= 0.1.0) :with-test))))
|
||||||
(package
|
(package
|
||||||
(name eio_linux)
|
(name eio_linux)
|
||||||
@ -34,11 +35,11 @@
|
|||||||
(depends
|
(depends
|
||||||
(alcotest (and (>= 1.7.0) :with-test))
|
(alcotest (and (>= 1.7.0) :with-test))
|
||||||
(eio (= :version))
|
(eio (= :version))
|
||||||
(mdx (and (>= 2.2.0) :with-test))
|
(mdx (and (>= 2.4.1) :with-test))
|
||||||
(logs (and (>= 0.7.0) :with-test))
|
(logs (and (>= 0.7.0) :with-test))
|
||||||
(fmt (>= 0.8.9))
|
(fmt (>= 0.8.9))
|
||||||
(cmdliner (and (>= 1.1.0) :with-test))
|
(cmdliner (and (>= 1.1.0) :with-test))
|
||||||
(uring (>= 0.7))))
|
(uring (>= 0.9))))
|
||||||
(package
|
(package
|
||||||
(name eio_posix)
|
(name eio_posix)
|
||||||
(allow_empty) ; Work-around for dune bug #6938
|
(allow_empty) ; Work-around for dune bug #6938
|
||||||
@ -47,7 +48,8 @@
|
|||||||
(depends
|
(depends
|
||||||
(eio (= :version))
|
(eio (= :version))
|
||||||
(iomux (>= 0.2))
|
(iomux (>= 0.2))
|
||||||
(mdx (and (>= 2.2.0) :with-test))
|
(mdx (and (>= 2.4.1) :with-test))
|
||||||
|
(conf-bash :with-test)
|
||||||
(fmt (>= 0.8.9))))
|
(fmt (>= 0.8.9))))
|
||||||
(package
|
(package
|
||||||
(name eio_windows)
|
(name eio_windows)
|
||||||
@ -64,7 +66,7 @@
|
|||||||
(synopsis "Effect-based direct-style IO mainloop for OCaml")
|
(synopsis "Effect-based direct-style IO mainloop for OCaml")
|
||||||
(description "Selects an appropriate Eio backend for the current platform.")
|
(description "Selects an appropriate Eio backend for the current platform.")
|
||||||
(depends
|
(depends
|
||||||
(mdx (and (>= 2.2.0) :with-test))
|
(mdx (and (>= 2.4.1) :with-test))
|
||||||
(kcas (and (>= 0.3.0) :with-test))
|
(kcas (and (>= 0.3.0) :with-test))
|
||||||
(yojson (and (>= 2.0.2) :with-test))
|
(yojson (and (>= 2.0.2) :with-test))
|
||||||
(eio_linux (and
|
(eio_linux (and
|
||||||
|
5
eio.opam
5
eio.opam
@ -10,7 +10,7 @@ doc: "https://ocaml-multicore.github.io/eio/"
|
|||||||
bug-reports: "https://github.com/ocaml-multicore/eio/issues"
|
bug-reports: "https://github.com/ocaml-multicore/eio/issues"
|
||||||
depends: [
|
depends: [
|
||||||
"dune" {>= "3.9"}
|
"dune" {>= "3.9"}
|
||||||
"ocaml" {>= "5.1.0"}
|
"ocaml" {>= "5.2.0"}
|
||||||
"bigstringaf" {>= "0.9.0"}
|
"bigstringaf" {>= "0.9.0"}
|
||||||
"cstruct" {>= "6.0.1"}
|
"cstruct" {>= "6.0.1"}
|
||||||
"lwt-dllist"
|
"lwt-dllist"
|
||||||
@ -21,7 +21,7 @@ depends: [
|
|||||||
"domain-local-await" {>= "0.1.0"}
|
"domain-local-await" {>= "0.1.0"}
|
||||||
"crowbar" {>= "0.2" & with-test}
|
"crowbar" {>= "0.2" & with-test}
|
||||||
"mtime" {>= "2.0.0"}
|
"mtime" {>= "2.0.0"}
|
||||||
"mdx" {>= "2.2.0" & < "2.4.0" & with-test}
|
"mdx" {>= "2.4.1" & with-test}
|
||||||
"dscheck" {>= "0.1.0" & with-test}
|
"dscheck" {>= "0.1.0" & with-test}
|
||||||
"odoc" {with-doc}
|
"odoc" {with-doc}
|
||||||
]
|
]
|
||||||
@ -29,7 +29,6 @@ conflicts: [
|
|||||||
"seq" {< "0.3"}
|
"seq" {< "0.3"}
|
||||||
]
|
]
|
||||||
build: [
|
build: [
|
||||||
["dune" "subst"] {dev}
|
|
||||||
[
|
[
|
||||||
"dune"
|
"dune"
|
||||||
"build"
|
"build"
|
||||||
|
@ -12,15 +12,14 @@ depends: [
|
|||||||
"dune" {>= "3.9"}
|
"dune" {>= "3.9"}
|
||||||
"alcotest" {>= "1.7.0" & with-test}
|
"alcotest" {>= "1.7.0" & with-test}
|
||||||
"eio" {= version}
|
"eio" {= version}
|
||||||
"mdx" {>= "2.2.0" & with-test}
|
"mdx" {>= "2.4.1" & with-test}
|
||||||
"logs" {>= "0.7.0" & with-test}
|
"logs" {>= "0.7.0" & with-test}
|
||||||
"fmt" {>= "0.8.9"}
|
"fmt" {>= "0.8.9"}
|
||||||
"cmdliner" {>= "1.1.0" & with-test}
|
"cmdliner" {>= "1.1.0" & with-test}
|
||||||
"uring" {>= "0.7"}
|
"uring" {>= "0.9"}
|
||||||
"odoc" {with-doc}
|
"odoc" {with-doc}
|
||||||
]
|
]
|
||||||
build: [
|
build: [
|
||||||
["dune" "subst"] {dev}
|
|
||||||
[
|
[
|
||||||
"dune"
|
"dune"
|
||||||
"build"
|
"build"
|
||||||
|
@ -10,7 +10,7 @@ doc: "https://ocaml-multicore.github.io/eio/"
|
|||||||
bug-reports: "https://github.com/ocaml-multicore/eio/issues"
|
bug-reports: "https://github.com/ocaml-multicore/eio/issues"
|
||||||
depends: [
|
depends: [
|
||||||
"dune" {>= "3.9"}
|
"dune" {>= "3.9"}
|
||||||
"mdx" {>= "2.2.0" & with-test}
|
"mdx" {>= "2.4.1" & with-test}
|
||||||
"kcas" {>= "0.3.0" & with-test}
|
"kcas" {>= "0.3.0" & with-test}
|
||||||
"yojson" {>= "2.0.2" & with-test}
|
"yojson" {>= "2.0.2" & with-test}
|
||||||
"eio_linux"
|
"eio_linux"
|
||||||
@ -21,7 +21,6 @@ depends: [
|
|||||||
"odoc" {with-doc}
|
"odoc" {with-doc}
|
||||||
]
|
]
|
||||||
build: [
|
build: [
|
||||||
["dune" "subst"] {dev}
|
|
||||||
[
|
[
|
||||||
"dune"
|
"dune"
|
||||||
"build"
|
"build"
|
||||||
|
@ -12,12 +12,12 @@ depends: [
|
|||||||
"dune" {>= "3.9"}
|
"dune" {>= "3.9"}
|
||||||
"eio" {= version}
|
"eio" {= version}
|
||||||
"iomux" {>= "0.2"}
|
"iomux" {>= "0.2"}
|
||||||
"mdx" {>= "2.2.0" & with-test}
|
"mdx" {>= "2.4.1" & with-test}
|
||||||
|
"conf-bash" {with-test}
|
||||||
"fmt" {>= "0.8.9"}
|
"fmt" {>= "0.8.9"}
|
||||||
"odoc" {with-doc}
|
"odoc" {with-doc}
|
||||||
]
|
]
|
||||||
build: [
|
build: [
|
||||||
["dune" "subst"] {dev}
|
|
||||||
[
|
[
|
||||||
"dune"
|
"dune"
|
||||||
"build"
|
"build"
|
||||||
|
@ -17,7 +17,6 @@ depends: [
|
|||||||
"odoc" {with-doc}
|
"odoc" {with-doc}
|
||||||
]
|
]
|
||||||
build: [
|
build: [
|
||||||
["dune" "subst"] {dev}
|
|
||||||
[
|
[
|
||||||
"dune"
|
"dune"
|
||||||
"build"
|
"build"
|
||||||
|
3
examples/fs/dune
Normal file
3
examples/fs/dune
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
(executable
|
||||||
|
(name main)
|
||||||
|
(libraries eio_main))
|
32
examples/fs/main.ml
Normal file
32
examples/fs/main.ml
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
(* Walk the directory tree rooted at the current directory,
|
||||||
|
showing a summary for any .mli files. *)
|
||||||
|
|
||||||
|
let ( / ) = Eio.Path.( / )
|
||||||
|
|
||||||
|
let is_doc_comment = String.starts_with ~prefix:"(** "
|
||||||
|
|
||||||
|
(* Print the first line of [t]'s doc-comment, if any *)
|
||||||
|
let scan_mli t f =
|
||||||
|
Eio.Path.with_lines t (fun lines ->
|
||||||
|
Seq.find is_doc_comment lines
|
||||||
|
|> Option.iter (fun line ->
|
||||||
|
let stop = String.index_from_opt line 4 '*' |> Option.value ~default:(String.length line) in
|
||||||
|
Format.fprintf f "%a: %s@." Eio.Path.pp t (String.sub line 4 (stop - 4))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
(* Walk the tree rooted at [t] and scan any .mli files found. *)
|
||||||
|
let rec scan t f =
|
||||||
|
match Eio.Path.kind ~follow:false t with
|
||||||
|
| `Directory ->
|
||||||
|
Eio.Path.read_dir t |> List.iter (function
|
||||||
|
| "_build" | "_opam" -> () (* Don't examine these directories *)
|
||||||
|
| item when String.starts_with ~prefix:"." item -> () (* Skip hidden items *)
|
||||||
|
| item -> scan (t / item) f
|
||||||
|
)
|
||||||
|
| `Regular_file when Filename.check_suffix (snd t) ".mli" -> scan_mli t f
|
||||||
|
| _ -> ()
|
||||||
|
|
||||||
|
let () =
|
||||||
|
Eio_main.run @@ fun env ->
|
||||||
|
scan (Eio.Stdenv.cwd env) Format.std_formatter
|
@ -218,6 +218,13 @@ val seq : ?stop:bool parser -> 'a parser -> 'a Seq.t parser
|
|||||||
It is not necessary to consume all the elements of the
|
It is not necessary to consume all the elements of the
|
||||||
sequence.
|
sequence.
|
||||||
|
|
||||||
|
Example ([head 4] is a parser that takes 4 lines):
|
||||||
|
|
||||||
|
{[
|
||||||
|
let head n r =
|
||||||
|
r |> Buf_read.(seq line) |> Seq.take n |> List.of_seq
|
||||||
|
]}
|
||||||
|
|
||||||
@param stop This is used before parsing each item.
|
@param stop This is used before parsing each item.
|
||||||
The sequence ends if this returns [true].
|
The sequence ends if this returns [true].
|
||||||
The default is {!at_end_of_input}. *)
|
The default is {!at_end_of_input}. *)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
(library
|
(library
|
||||||
(name eio__core)
|
(name eio__core)
|
||||||
(public_name eio.core)
|
(public_name eio.core)
|
||||||
(libraries cstruct hmap lwt-dllist fmt optint domain-local-await eio.runtime_events))
|
(libraries hmap lwt-dllist fmt optint eio.runtime_events))
|
||||||
|
@ -7,6 +7,7 @@ module Private = struct
|
|||||||
module Suspend = Suspend
|
module Suspend = Suspend
|
||||||
module Cells = Cells
|
module Cells = Cells
|
||||||
module Broadcast = Broadcast
|
module Broadcast = Broadcast
|
||||||
|
module Single_waiter = Single_waiter
|
||||||
module Trace = Trace
|
module Trace = Trace
|
||||||
module Fiber_context = Cancel.Fiber_context
|
module Fiber_context = Cancel.Fiber_context
|
||||||
module Debug = Debug
|
module Debug = Debug
|
||||||
@ -18,6 +19,4 @@ module Private = struct
|
|||||||
| Fork = Fiber.Fork
|
| Fork = Fiber.Fork
|
||||||
| Get_context = Cancel.Get_context
|
| Get_context = Cancel.Get_context
|
||||||
end
|
end
|
||||||
|
|
||||||
module Dla = Dla
|
|
||||||
end
|
end
|
||||||
|
@ -606,6 +606,7 @@ module Private : sig
|
|||||||
|
|
||||||
module Cells = Cells
|
module Cells = Cells
|
||||||
module Broadcast = Broadcast
|
module Broadcast = Broadcast
|
||||||
|
module Single_waiter = Single_waiter
|
||||||
|
|
||||||
(** Every fiber has an associated context. *)
|
(** Every fiber has an associated context. *)
|
||||||
module Fiber_context : sig
|
module Fiber_context : sig
|
||||||
@ -781,8 +782,4 @@ module Private : sig
|
|||||||
val v : t
|
val v : t
|
||||||
(** Backends should use this for {!Eio.Stdenv.debug}. *)
|
(** Backends should use this for {!Eio.Stdenv.debug}. *)
|
||||||
end
|
end
|
||||||
|
|
||||||
module Dla : sig
|
|
||||||
val prepare_for_await : unit -> Domain_local_await.t
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
@ -39,7 +39,8 @@ let fork_daemon ~sw f =
|
|||||||
(* The daemon was cancelled because all non-daemon fibers are finished. *)
|
(* The daemon was cancelled because all non-daemon fibers are finished. *)
|
||||||
()
|
()
|
||||||
| exception ex ->
|
| exception ex ->
|
||||||
Switch.fail sw ex; (* The [with_daemon] ensures this will succeed *)
|
let bt = Printexc.get_raw_backtrace () in
|
||||||
|
Switch.fail ~bt sw ex; (* The [with_daemon] ensures this will succeed *)
|
||||||
) (* else the fiber should report the error to [sw], but [sw] is failed anyway *)
|
) (* else the fiber should report the error to [sw], but [sw] is failed anyway *)
|
||||||
|
|
||||||
let fork_promise ~sw f =
|
let fork_promise ~sw f =
|
||||||
@ -65,7 +66,8 @@ let fork_promise_exn ~sw f =
|
|||||||
match Switch.with_op sw f with
|
match Switch.with_op sw f with
|
||||||
| x -> Promise.resolve r x
|
| x -> Promise.resolve r x
|
||||||
| exception ex ->
|
| exception ex ->
|
||||||
Switch.fail sw ex (* The [with_op] ensures this will succeed *)
|
let bt = Printexc.get_raw_backtrace () in
|
||||||
|
Switch.fail ~bt sw ex (* The [with_op] ensures this will succeed *)
|
||||||
);
|
);
|
||||||
p
|
p
|
||||||
|
|
||||||
@ -226,7 +228,7 @@ module List = struct
|
|||||||
|
|
||||||
let release t =
|
let release t =
|
||||||
t.free_fibers <- t.free_fibers + 1;
|
t.free_fibers <- t.free_fibers + 1;
|
||||||
if t.free_fibers = 1 then Single_waiter.wake t.cond (Ok ())
|
if t.free_fibers = 1 then Single_waiter.wake_if_sleeping t.cond
|
||||||
|
|
||||||
let use t fn x =
|
let use t fn x =
|
||||||
await_free t;
|
await_free t;
|
||||||
|
@ -1,25 +1,32 @@
|
|||||||
(* Allows a single fiber to wait to be notified by another fiber in the same domain.
|
type 'a state =
|
||||||
If multiple fibers need to wait at once, or the notification comes from another domain,
|
| Running
|
||||||
this can't be used. *)
|
| Sleeping of (('a, exn) result -> unit)
|
||||||
|
|
||||||
type 'a t = {
|
type 'a t = 'a state ref
|
||||||
mutable wake : ('a, exn) result -> unit;
|
|
||||||
}
|
|
||||||
|
|
||||||
let create () = { wake = ignore }
|
let create () = ref Running
|
||||||
|
|
||||||
let wake t v = t.wake v
|
let wake t v =
|
||||||
|
match !t with
|
||||||
|
| Running -> false
|
||||||
|
| Sleeping fn ->
|
||||||
|
t := Running;
|
||||||
|
fn v;
|
||||||
|
true
|
||||||
|
|
||||||
|
let wake_if_sleeping t =
|
||||||
|
ignore (wake t (Ok ()) : bool)
|
||||||
|
|
||||||
let await t op id =
|
let await t op id =
|
||||||
let x =
|
let x =
|
||||||
Suspend.enter op @@ fun ctx enqueue ->
|
Suspend.enter op @@ fun ctx enqueue ->
|
||||||
Cancel.Fiber_context.set_cancel_fn ctx (fun ex ->
|
Cancel.Fiber_context.set_cancel_fn ctx (fun ex ->
|
||||||
t.wake <- ignore;
|
t := Running;
|
||||||
enqueue (Error ex)
|
enqueue (Error ex)
|
||||||
);
|
);
|
||||||
t.wake <- (fun x ->
|
t := Sleeping (fun x ->
|
||||||
Cancel.Fiber_context.clear_cancel_fn ctx;
|
Cancel.Fiber_context.clear_cancel_fn ctx;
|
||||||
t.wake <- ignore;
|
t := Running;
|
||||||
enqueue x
|
enqueue x
|
||||||
)
|
)
|
||||||
in
|
in
|
||||||
@ -29,7 +36,7 @@ let await t op id =
|
|||||||
let await_protect t op id =
|
let await_protect t op id =
|
||||||
let x =
|
let x =
|
||||||
Suspend.enter_unchecked op @@ fun _ctx enqueue ->
|
Suspend.enter_unchecked op @@ fun _ctx enqueue ->
|
||||||
t.wake <- (fun x -> t.wake <- ignore; enqueue x)
|
t := Sleeping (fun x -> t := Running; enqueue x)
|
||||||
in
|
in
|
||||||
Trace.get id;
|
Trace.get id;
|
||||||
x
|
x
|
||||||
|
25
lib_eio/core/single_waiter.mli
Normal file
25
lib_eio/core/single_waiter.mli
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
(** Allows a single fiber to wait to be notified by another fiber in the same domain.
|
||||||
|
If multiple fibers need to wait at once, or the notification comes from another domain,
|
||||||
|
this can't be used. *)
|
||||||
|
|
||||||
|
type 'a t
|
||||||
|
(** A handle representing a fiber that might be sleeping.
|
||||||
|
It is either in the Running or Sleeping state. *)
|
||||||
|
|
||||||
|
val create : unit -> 'a t
|
||||||
|
(** [create ()] is a new waiter, initially in the Running state. *)
|
||||||
|
|
||||||
|
val wake : 'a t -> ('a, exn) result -> bool
|
||||||
|
(** [wake t v] resumes [t]'s fiber with value [v] and returns [true] if it was sleeping.
|
||||||
|
If [t] is Running then this just returns [false]. *)
|
||||||
|
|
||||||
|
val wake_if_sleeping : unit t -> unit
|
||||||
|
(** [wake_if_sleeping] is [ignore (wake t (Ok ()))]. *)
|
||||||
|
|
||||||
|
val await : 'a t -> string -> Trace.id -> 'a
|
||||||
|
(** [await t op id] suspends the calling fiber, changing [t]'s state to Sleeping.
|
||||||
|
If the fiber is cancelled, a cancel exception is raised.
|
||||||
|
[op] and [id] are used for tracing. *)
|
||||||
|
|
||||||
|
val await_protect : 'a t -> string -> Trace.id -> 'a
|
||||||
|
(** [await_protect] is like {!await}, but the sleep cannot be cancelled. *)
|
@ -72,7 +72,7 @@ let dec_fibers t =
|
|||||||
if t.daemon_fibers > 0 && t.fibers = t.daemon_fibers then
|
if t.daemon_fibers > 0 && t.fibers = t.daemon_fibers then
|
||||||
Cancel.cancel t.cancel Exit;
|
Cancel.cancel t.cancel Exit;
|
||||||
if t.fibers = 0 then
|
if t.fibers = 0 then
|
||||||
Single_waiter.wake t.waiter (Ok ())
|
Single_waiter.wake_if_sleeping t.waiter
|
||||||
|
|
||||||
let with_op t fn =
|
let with_op t fn =
|
||||||
inc_fibers t;
|
inc_fibers t;
|
||||||
|
@ -70,6 +70,7 @@ module Pi = struct
|
|||||||
val rmdir : t -> path -> unit
|
val rmdir : t -> path -> unit
|
||||||
val rename : t -> path -> _ dir -> path -> unit
|
val rename : t -> path -> _ dir -> path -> unit
|
||||||
val read_link : t -> path -> string
|
val read_link : t -> path -> string
|
||||||
|
val symlink : link_to:path -> t -> path -> unit
|
||||||
val pp : t Fmt.t
|
val pp : t Fmt.t
|
||||||
val native : t -> string -> string option
|
val native : t -> string -> string option
|
||||||
end
|
end
|
||||||
|
@ -105,7 +105,7 @@ let run_full main =
|
|||||||
let result = ref None in
|
let result = ref None in
|
||||||
let `Exit_scheduler =
|
let `Exit_scheduler =
|
||||||
Domain_local_await.using
|
Domain_local_await.using
|
||||||
~prepare_for_await:Eio.Private.Dla.prepare_for_await
|
~prepare_for_await:Eio_utils.Dla.prepare_for_await
|
||||||
~while_running:(fun () ->
|
~while_running:(fun () ->
|
||||||
fork ~new_fiber (fun () -> result := Some (main stdenv))) in
|
fork ~new_fiber (fun () -> result := Some (main stdenv))) in
|
||||||
match !result with
|
match !result with
|
||||||
|
@ -231,6 +231,9 @@ val run_server :
|
|||||||
In such cases you must ensure that [connection_handler] only accesses thread-safe values.
|
In such cases you must ensure that [connection_handler] only accesses thread-safe values.
|
||||||
Note that having more than {!Domain.recommended_domain_count} domains in total is likely to result in bad performance.
|
Note that having more than {!Domain.recommended_domain_count} domains in total is likely to result in bad performance.
|
||||||
|
|
||||||
|
For services that are bottlenecked on CPU rather than IO,
|
||||||
|
you can run a single accept loop and have the handler submit CPU-intensive jobs to an {!module:Executor_pool}.
|
||||||
|
|
||||||
@param max_connections The maximum number of concurrent connections accepted by [s] at any time.
|
@param max_connections The maximum number of concurrent connections accepted by [s] at any time.
|
||||||
The default is [Int.max_int].
|
The default is [Int.max_int].
|
||||||
@param stop Resolving this promise causes [s] to stop accepting new connections.
|
@param stop Resolving this promise causes [s] to stop accepting new connections.
|
||||||
|
@ -1,11 +1,22 @@
|
|||||||
type 'a t = 'a Fs.dir * Fs.path
|
type 'a t = 'a Fs.dir * Fs.path
|
||||||
|
|
||||||
|
(* Like [Filename.is_relative] but always using "/" as the separator. *)
|
||||||
|
let is_relative = function
|
||||||
|
| "" -> true
|
||||||
|
| x -> x.[0] <> '/'
|
||||||
|
|
||||||
|
(* Like [Filename.concat] but always using "/" as the separator. *)
|
||||||
|
let concat a b =
|
||||||
|
let l = String.length a in
|
||||||
|
if l = 0 || a.[l - 1] = '/' then a ^ b
|
||||||
|
else a ^ "/" ^ b
|
||||||
|
|
||||||
let ( / ) (dir, p1) p2 =
|
let ( / ) (dir, p1) p2 =
|
||||||
match p1, p2 with
|
match p1, p2 with
|
||||||
| p1, "" -> (dir, Filename.concat p1 p2)
|
| p1, "" -> (dir, concat p1 p2)
|
||||||
| _, p2 when not (Filename.is_relative p2) -> (dir, p2)
|
| _, p2 when not (is_relative p2) -> (dir, p2)
|
||||||
| ".", p2 -> (dir, p2)
|
| ".", p2 -> (dir, p2)
|
||||||
| p1, p2 -> (dir, Filename.concat p1 p2)
|
| p1, p2 -> (dir, concat p1 p2)
|
||||||
|
|
||||||
let pp f (Resource.T (t, ops), p) =
|
let pp f (Resource.T (t, ops), p) =
|
||||||
let module X = (val (Resource.get ops Fs.Pi.Dir)) in
|
let module X = (val (Resource.get ops Fs.Pi.Dir)) in
|
||||||
@ -199,6 +210,14 @@ let rename t1 t2 =
|
|||||||
let bt = Printexc.get_raw_backtrace () in
|
let bt = Printexc.get_raw_backtrace () in
|
||||||
Exn.reraise_with_context ex bt "renaming %a to %a" pp t1 pp t2
|
Exn.reraise_with_context ex bt "renaming %a to %a" pp t1 pp t2
|
||||||
|
|
||||||
|
let symlink ~link_to source =
|
||||||
|
let (Resource.T (dir, ops), path) = source in
|
||||||
|
let module X = (val (Resource.get ops Fs.Pi.Dir)) in
|
||||||
|
try X.symlink dir path ~link_to
|
||||||
|
with Exn.Io _ as ex ->
|
||||||
|
let bt = Printexc.get_raw_backtrace () in
|
||||||
|
Exn.reraise_with_context ex bt "creating symlink %a -> %s" pp source link_to
|
||||||
|
|
||||||
let rec mkdirs ?(exists_ok=false) ~perm t =
|
let rec mkdirs ?(exists_ok=false) ~perm t =
|
||||||
(* Check parent exists first. *)
|
(* Check parent exists first. *)
|
||||||
split t |> Option.iter (fun (parent, _) ->
|
split t |> Option.iter (fun (parent, _) ->
|
||||||
|
@ -207,3 +207,13 @@ val rename : _ t -> _ t -> unit
|
|||||||
(** [rename old_t new_t] atomically unlinks [old_t] and links it as [new_t].
|
(** [rename old_t new_t] atomically unlinks [old_t] and links it as [new_t].
|
||||||
|
|
||||||
If [new_t] already exists, it is atomically replaced. *)
|
If [new_t] already exists, it is atomically replaced. *)
|
||||||
|
|
||||||
|
val symlink : link_to:string -> _ t -> unit
|
||||||
|
(** [symlink ~link_to t] creates a symbolic link [t] to [link_to].
|
||||||
|
|
||||||
|
[t] is the symlink that is created and [link_to] is the name used in the link.
|
||||||
|
For example, this creates a "current" symlink pointing at "version-1.0":
|
||||||
|
|
||||||
|
{[
|
||||||
|
Eio.Path.symlink (dir / "current") ~link_to:"version-1.0"
|
||||||
|
]} *)
|
||||||
|
@ -89,11 +89,10 @@ let cancel segment cell =
|
|||||||
| In_transition | Resource _ -> assert false (* Can't get here from [Request]. *)
|
| In_transition | Resource _ -> assert false (* Can't get here from [Request]. *)
|
||||||
|
|
||||||
(* If [t] is under capacity, add another (empty) slot. *)
|
(* If [t] is under capacity, add another (empty) slot. *)
|
||||||
let rec maybe_add_slot t =
|
let rec maybe_add_slot t current =
|
||||||
let current = Atomic.get t.slots in
|
|
||||||
if current < t.max_slots then (
|
if current < t.max_slots then (
|
||||||
if Atomic.compare_and_set t.slots current (current + 1) then add t (ref None)
|
if Atomic.compare_and_set t.slots current (current + 1) then add t (ref None)
|
||||||
else maybe_add_slot t (* Concurrent update; try again *)
|
else maybe_add_slot t (Atomic.get t.slots) (* Concurrent update; try again *)
|
||||||
)
|
)
|
||||||
|
|
||||||
(* [run_with t f slot] ensures that [slot] contains a valid resource and then runs [f resource] with it.
|
(* [run_with t f slot] ensures that [slot] contains a valid resource and then runs [f resource] with it.
|
||||||
@ -122,7 +121,19 @@ let run_with t f slot =
|
|||||||
add t slot;
|
add t slot;
|
||||||
Printexc.raise_with_backtrace ex bt
|
Printexc.raise_with_backtrace ex bt
|
||||||
|
|
||||||
let use t f =
|
(* Creates a fresh resource [x], runs [f x], then disposes of [x] *)
|
||||||
|
let run_new_and_dispose t f =
|
||||||
|
let x = t.alloc () in
|
||||||
|
match f x with
|
||||||
|
| r ->
|
||||||
|
t.dispose x;
|
||||||
|
r
|
||||||
|
| exception ex ->
|
||||||
|
let bt = Printexc.get_raw_backtrace () in
|
||||||
|
t.dispose x;
|
||||||
|
Printexc.raise_with_backtrace ex bt
|
||||||
|
|
||||||
|
let use t ?(never_block=false) f =
|
||||||
let segment, cell = Q.next_suspend t.q in
|
let segment, cell = Q.next_suspend t.q in
|
||||||
match Atomic.get cell with
|
match Atomic.get cell with
|
||||||
| Finished | Request _ -> assert false
|
| Finished | Request _ -> assert false
|
||||||
@ -130,9 +141,18 @@ let use t f =
|
|||||||
Atomic.set cell Finished; (* Allow value to be GC'd *)
|
Atomic.set cell Finished; (* Allow value to be GC'd *)
|
||||||
run_with t f slot
|
run_with t f slot
|
||||||
| In_transition ->
|
| In_transition ->
|
||||||
(* It would have been better if more resources were available.
|
let current = Atomic.get t.slots in
|
||||||
If we still have capacity, add a new slot now. *)
|
match current < t.max_slots with
|
||||||
maybe_add_slot t;
|
| false when never_block -> (
|
||||||
|
(* We are at capacity, but cannot block.
|
||||||
|
Create a new resource to run f but don't add it to the pool. *)
|
||||||
|
match Atomic.exchange cell Finished with
|
||||||
|
| Resource slot -> run_with t f slot
|
||||||
|
| _ -> run_new_and_dispose t f
|
||||||
|
)
|
||||||
|
| can_add ->
|
||||||
|
(* Create a slot if not at capacity. *)
|
||||||
|
if can_add then maybe_add_slot t current;
|
||||||
(* No item is available right now. Start waiting *)
|
(* No item is available right now. Start waiting *)
|
||||||
let slot =
|
let slot =
|
||||||
Suspend.enter_unchecked "Pool.acquire" (fun ctx enqueue ->
|
Suspend.enter_unchecked "Pool.acquire" (fun ctx enqueue ->
|
||||||
|
@ -35,6 +35,12 @@ val create :
|
|||||||
If it raises, the exception is passed on to the user,
|
If it raises, the exception is passed on to the user,
|
||||||
but resource is still considered to have been disposed. *)
|
but resource is still considered to have been disposed. *)
|
||||||
|
|
||||||
val use : 'a t -> ('a -> 'b) -> 'b
|
val use : 'a t -> ?never_block:bool -> ('a -> 'b) -> 'b
|
||||||
(** [use t fn] waits for some resource [x] to be available and then runs [f x].
|
(** [use t fn] waits for some resource [x] to be available and then runs [f x].
|
||||||
Afterwards (on success or error), [x] is returned to the pool. *)
|
Afterwards (on success or error), [x] is returned to the pool.
|
||||||
|
|
||||||
|
@param never_block If [true] and the pool has reached maximum capacity,
|
||||||
|
then a fresh resource is created to ensure that this [use]
|
||||||
|
call does not wait for a resource to become available.
|
||||||
|
This resource is immediately disposed after [f x] returns.
|
||||||
|
*)
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
|
type error = ECONNRESET
|
||||||
|
|
||||||
|
exception Unix_error of error * string * string
|
||||||
|
|
||||||
type file_descr = [`Open | `Closed] Atomic.t
|
type file_descr = [`Open | `Closed] Atomic.t
|
||||||
|
|
||||||
let make () = Atomic.make `Open
|
let make () = Atomic.make `Open
|
||||||
|
@ -87,6 +87,11 @@ module Timeout = struct
|
|||||||
| Timeout (clock, d) ->
|
| Timeout (clock, d) ->
|
||||||
Fiber.first (fun () -> Mono.sleep_span clock d; raise Timeout) fn
|
Fiber.first (fun () -> Mono.sleep_span clock d; raise Timeout) fn
|
||||||
|
|
||||||
|
let sleep t =
|
||||||
|
match t with
|
||||||
|
| Unlimited -> Fiber.await_cancel ()
|
||||||
|
| Timeout (clock, d) -> Mono.sleep_span clock d
|
||||||
|
|
||||||
let pp_duration f d =
|
let pp_duration f d =
|
||||||
if d >= 0.001 && d < 0.1 then
|
if d >= 0.001 && d < 0.1 then
|
||||||
Fmt.pf f "%.2gms" (d *. 1000.)
|
Fmt.pf f "%.2gms" (d *. 1000.)
|
||||||
|
@ -70,6 +70,9 @@ module Timeout : sig
|
|||||||
(** [run_exn t fn] runs [fn ()] but cancels it if it takes longer than allowed by timeout [t],
|
(** [run_exn t fn] runs [fn ()] but cancels it if it takes longer than allowed by timeout [t],
|
||||||
raising exception {!exception-Timeout}. *)
|
raising exception {!exception-Timeout}. *)
|
||||||
|
|
||||||
|
val sleep : t -> unit
|
||||||
|
(** [sleep t] sleeps for [t]'s duration. *)
|
||||||
|
|
||||||
val pp : t Fmt.t
|
val pp : t Fmt.t
|
||||||
(** [pp] formats a timeout as a duration (e.g. "5s").
|
(** [pp] formats a timeout as a duration (e.g. "5s").
|
||||||
This is intended for use in error messages and logging and is rounded. *)
|
This is intended for use in error messages and logging and is rounded. *)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
(library
|
(library
|
||||||
(name eio_unix)
|
(name eio_unix)
|
||||||
(public_name eio.unix)
|
(public_name eio.unix)
|
||||||
|
(public_headers include/fork_action.h)
|
||||||
(foreign_stubs
|
(foreign_stubs
|
||||||
(language c)
|
(language c)
|
||||||
(include_dirs include)
|
(include_dirs include)
|
||||||
|
@ -61,18 +61,23 @@ type t = [`Generic | `Unix] Eio.Net.ty r
|
|||||||
|
|
||||||
type _ Effect.t +=
|
type _ Effect.t +=
|
||||||
| Import_socket_stream : Switch.t * bool * Unix.file_descr -> [`Unix_fd | stream_socket_ty] r Effect.t
|
| Import_socket_stream : Switch.t * bool * Unix.file_descr -> [`Unix_fd | stream_socket_ty] r Effect.t
|
||||||
|
| Import_socket_listening : Switch.t * bool * Unix.file_descr -> [`Unix_fd | listening_socket_ty] r Effect.t
|
||||||
| Import_socket_datagram : Switch.t * bool * Unix.file_descr -> [`Unix_fd | datagram_socket_ty] r Effect.t
|
| Import_socket_datagram : Switch.t * bool * Unix.file_descr -> [`Unix_fd | datagram_socket_ty] r Effect.t
|
||||||
| Socketpair_stream : Switch.t * Unix.socket_domain * int ->
|
| Socketpair_stream : Switch.t * Unix.socket_domain * int ->
|
||||||
([`Unix_fd | stream_socket_ty] r * [`Unix_fd | stream_socket_ty] r) Effect.t
|
([`Unix_fd | stream_socket_ty] r * [`Unix_fd | stream_socket_ty] r) Effect.t
|
||||||
| Socketpair_datagram : Switch.t * Unix.socket_domain * int ->
|
| Socketpair_datagram : Switch.t * Unix.socket_domain * int ->
|
||||||
([`Unix_fd | datagram_socket_ty] r * [`Unix_fd | datagram_socket_ty] r) Effect.t
|
([`Unix_fd | datagram_socket_ty] r * [`Unix_fd | datagram_socket_ty] r) Effect.t
|
||||||
|
|
||||||
let open_stream s = (s : _ stream_socket :> [< `Unix_fd | stream_socket_ty] r)
|
let open_stream s = (s : [`Unix_fd | stream_socket_ty] r :> [< `Unix_fd | stream_socket_ty] r)
|
||||||
let open_datagram s = (s : _ datagram_socket :> [< `Unix_fd | datagram_socket_ty] r)
|
let open_listening s = (s : [`Unix_fd | listening_socket_ty] r :> [< `Unix_fd | listening_socket_ty] r)
|
||||||
|
let open_datagram s = (s : [`Unix_fd | datagram_socket_ty] r :> [< `Unix_fd | datagram_socket_ty] r)
|
||||||
|
|
||||||
let import_socket_stream ~sw ~close_unix fd =
|
let import_socket_stream ~sw ~close_unix fd =
|
||||||
open_stream @@ Effect.perform (Import_socket_stream (sw, close_unix, fd))
|
open_stream @@ Effect.perform (Import_socket_stream (sw, close_unix, fd))
|
||||||
|
|
||||||
|
let import_socket_listening ~sw ~close_unix fd =
|
||||||
|
open_listening @@ Effect.perform (Import_socket_listening (sw, close_unix, fd))
|
||||||
|
|
||||||
let import_socket_datagram ~sw ~close_unix fd =
|
let import_socket_datagram ~sw ~close_unix fd =
|
||||||
open_datagram @@ Effect.perform (Import_socket_datagram (sw, close_unix, fd))
|
open_datagram @@ Effect.perform (Import_socket_datagram (sw, close_unix, fd))
|
||||||
|
|
||||||
@ -81,7 +86,8 @@ let socketpair_stream ~sw ?(domain=Unix.PF_UNIX) ?(protocol=0) () =
|
|||||||
(open_stream a, open_stream b)
|
(open_stream a, open_stream b)
|
||||||
|
|
||||||
let socketpair_datagram ~sw ?(domain=Unix.PF_UNIX) ?(protocol=0) () =
|
let socketpair_datagram ~sw ?(domain=Unix.PF_UNIX) ?(protocol=0) () =
|
||||||
Effect.perform (Socketpair_datagram (sw, domain, protocol))
|
let a, b = Effect.perform (Socketpair_datagram (sw, domain, protocol)) in
|
||||||
|
(open_datagram a, open_datagram b)
|
||||||
|
|
||||||
let fd socket =
|
let fd socket =
|
||||||
Option.get (Resource.fd_opt socket)
|
Option.get (Resource.fd_opt socket)
|
||||||
|
@ -55,16 +55,23 @@ end
|
|||||||
|
|
||||||
(** {2 Creating or importing sockets} *)
|
(** {2 Creating or importing sockets} *)
|
||||||
|
|
||||||
val import_socket_stream : sw:Switch.t -> close_unix:bool -> Unix.file_descr -> [`Unix_fd | stream_socket_ty] r
|
val import_socket_stream : sw:Switch.t -> close_unix:bool -> Unix.file_descr -> [< `Unix_fd | stream_socket_ty] r
|
||||||
(** [import_socket_stream ~sw ~close_unix:true fd] is an Eio flow that uses [fd].
|
(** [import_socket_stream ~sw ~close_unix fd] is an Eio flow that uses [fd].
|
||||||
|
|
||||||
It can be cast to e.g. {!source} for a one-way flow.
|
It can be cast to e.g. {!source} for a one-way flow.
|
||||||
The socket object will be closed when [sw] finishes.
|
The socket object will be closed when [sw] finishes.
|
||||||
|
|
||||||
The [close_unix] and [sw] arguments are passed to {!Fd.of_unix}. *)
|
The [close_unix] and [sw] arguments are passed to {!Fd.of_unix}. *)
|
||||||
|
|
||||||
val import_socket_datagram : sw:Switch.t -> close_unix:bool -> Unix.file_descr -> [`Unix_fd | datagram_socket_ty] r
|
val import_socket_listening : sw:Switch.t -> close_unix:bool -> Unix.file_descr -> [< `Unix_fd | listening_socket_ty] r
|
||||||
(** [import_socket_datagram ~sw ~close_unix:true fd] is an Eio datagram socket that uses [fd].
|
(** [import_socket_listening ~sw ~close_unix fd] is an Eio listening socket that uses [fd].
|
||||||
|
|
||||||
|
The socket object will be closed when [sw] finishes.
|
||||||
|
|
||||||
|
The [close_unix] and [sw] arguments are passed to {!Fd.of_unix}. *)
|
||||||
|
|
||||||
|
val import_socket_datagram : sw:Switch.t -> close_unix:bool -> Unix.file_descr -> [< `Unix_fd | datagram_socket_ty] r
|
||||||
|
(** [import_socket_datagram ~sw ~close_unix fd] is an Eio datagram socket that uses [fd].
|
||||||
|
|
||||||
The socket object will be closed when [sw] finishes.
|
The socket object will be closed when [sw] finishes.
|
||||||
|
|
||||||
@ -75,7 +82,7 @@ val socketpair_stream :
|
|||||||
?domain:Unix.socket_domain ->
|
?domain:Unix.socket_domain ->
|
||||||
?protocol:int ->
|
?protocol:int ->
|
||||||
unit ->
|
unit ->
|
||||||
[`Unix_fd | stream_socket_ty] r * [`Unix_fd | stream_socket_ty] r
|
[< `Unix_fd | stream_socket_ty] r * [< `Unix_fd | stream_socket_ty] r
|
||||||
(** [socketpair_stream ~sw ()] returns a connected pair of flows, such that writes to one can be read by the other.
|
(** [socketpair_stream ~sw ()] returns a connected pair of flows, such that writes to one can be read by the other.
|
||||||
|
|
||||||
This creates OS-level resources using [socketpair(2)].
|
This creates OS-level resources using [socketpair(2)].
|
||||||
@ -86,7 +93,7 @@ val socketpair_datagram :
|
|||||||
?domain:Unix.socket_domain ->
|
?domain:Unix.socket_domain ->
|
||||||
?protocol:int ->
|
?protocol:int ->
|
||||||
unit ->
|
unit ->
|
||||||
[`Unix_fd | datagram_socket_ty] r * [`Unix_fd | datagram_socket_ty] r
|
[< `Unix_fd | datagram_socket_ty] r * [< `Unix_fd | datagram_socket_ty] r
|
||||||
(** [socketpair_datagram ~sw ()] returns a connected pair of flows, such that writes to one can be read by the other.
|
(** [socketpair_datagram ~sw ()] returns a connected pair of flows, such that writes to one can be read by the other.
|
||||||
|
|
||||||
This creates OS-level resources using [socketpair(2)].
|
This creates OS-level resources using [socketpair(2)].
|
||||||
@ -100,6 +107,8 @@ val getnameinfo : Eio.Net.Sockaddr.t -> (string * string)
|
|||||||
type _ Effect.t +=
|
type _ Effect.t +=
|
||||||
| Import_socket_stream :
|
| Import_socket_stream :
|
||||||
Switch.t * bool * Unix.file_descr -> [`Unix_fd | stream_socket_ty] r Effect.t (** See {!import_socket_stream} *)
|
Switch.t * bool * Unix.file_descr -> [`Unix_fd | stream_socket_ty] r Effect.t (** See {!import_socket_stream} *)
|
||||||
|
| Import_socket_listening :
|
||||||
|
Switch.t * bool * Unix.file_descr -> [`Unix_fd | listening_socket_ty] r Effect.t (** See {!import_socket_listening} *)
|
||||||
| Import_socket_datagram :
|
| Import_socket_datagram :
|
||||||
Switch.t * bool * Unix.file_descr -> [`Unix_fd | datagram_socket_ty] r Effect.t (** See {!import_socket_datagram} *)
|
Switch.t * bool * Unix.file_descr -> [`Unix_fd | datagram_socket_ty] r Effect.t (** See {!import_socket_datagram} *)
|
||||||
| Socketpair_stream : Eio.Switch.t * Unix.socket_domain * int ->
|
| Socketpair_stream : Eio.Switch.t * Unix.socket_domain * int ->
|
||||||
|
@ -98,7 +98,13 @@ let get t =
|
|||||||
None
|
None
|
||||||
|
|
||||||
let close_fd fd =
|
let close_fd fd =
|
||||||
Eio.Private.Trace.with_span "close" (fun () -> Unix.close fd)
|
Eio.Private.Trace.with_span "close" (fun () ->
|
||||||
|
try
|
||||||
|
Unix.close fd
|
||||||
|
with Unix.Unix_error (ECONNRESET, _, _) ->
|
||||||
|
(* For FreeBSD. See https://github.com/ocaml-multicore/eio/issues/786 *)
|
||||||
|
()
|
||||||
|
)
|
||||||
|
|
||||||
(* Note: we could simplify this a bit by incrementing [t.ops], as [remove] does.
|
(* Note: we could simplify this a bit by incrementing [t.ops], as [remove] does.
|
||||||
However, that makes dscheck too slow. *)
|
However, that makes dscheck too slow. *)
|
||||||
|
@ -7,10 +7,10 @@ let prepare_for_await () =
|
|||||||
| _ -> ()
|
| _ -> ()
|
||||||
and await () =
|
and await () =
|
||||||
if Atomic.get state != `Released then
|
if Atomic.get state != `Released then
|
||||||
Suspend.enter "domain-local-await" @@ fun ctx enqueue ->
|
Eio.Private.Suspend.enter "domain-local-await" @@ fun ctx enqueue ->
|
||||||
let awaiting = `Awaiting enqueue in
|
let awaiting = `Awaiting enqueue in
|
||||||
if Atomic.compare_and_set state `Init awaiting then (
|
if Atomic.compare_and_set state `Init awaiting then (
|
||||||
Cancel.Fiber_context.set_cancel_fn ctx (fun ex ->
|
Eio.Private.Fiber_context.set_cancel_fn ctx (fun ex ->
|
||||||
if Atomic.compare_and_set state awaiting `Released then (
|
if Atomic.compare_and_set state awaiting `Released then (
|
||||||
enqueue (Error ex)
|
enqueue (Error ex)
|
||||||
)
|
)
|
1
lib_eio/utils/dla.mli
Normal file
1
lib_eio/utils/dla.mli
Normal file
@ -0,0 +1 @@
|
|||||||
|
val prepare_for_await : unit -> Domain_local_await.t
|
@ -1,4 +1,4 @@
|
|||||||
(library
|
(library
|
||||||
(name eio_utils)
|
(name eio_utils)
|
||||||
(public_name eio.utils)
|
(public_name eio.utils)
|
||||||
(libraries eio psq fmt optint))
|
(libraries eio psq fmt optint domain-local-await))
|
||||||
|
@ -5,3 +5,4 @@
|
|||||||
module Lf_queue = Lf_queue
|
module Lf_queue = Lf_queue
|
||||||
module Suspended = Suspended
|
module Suspended = Suspended
|
||||||
module Zzz = Zzz
|
module Zzz = Zzz
|
||||||
|
module Dla = Dla
|
||||||
|
@ -38,82 +38,6 @@ let get_dir_fd_opt (Eio.Resource.T (t, ops)) =
|
|||||||
| Some f -> Some (f t)
|
| Some f -> Some (f t)
|
||||||
| None -> None
|
| None -> None
|
||||||
|
|
||||||
(* When copying between a source with an FD and a sink with an FD, we can share the chunk
|
|
||||||
and avoid copying. *)
|
|
||||||
let fast_copy src dst =
|
|
||||||
let fallback () =
|
|
||||||
(* No chunks available. Use regular memory instead. *)
|
|
||||||
let buf = Cstruct.create 4096 in
|
|
||||||
try
|
|
||||||
while true do
|
|
||||||
let got = Low_level.readv src [buf] in
|
|
||||||
Low_level.writev dst [Cstruct.sub buf 0 got]
|
|
||||||
done
|
|
||||||
with End_of_file -> ()
|
|
||||||
in
|
|
||||||
Low_level.with_chunk ~fallback @@ fun chunk ->
|
|
||||||
let chunk_size = Uring.Region.length chunk in
|
|
||||||
try
|
|
||||||
while true do
|
|
||||||
let got = Low_level.read_upto src chunk chunk_size in
|
|
||||||
Low_level.write dst chunk got
|
|
||||||
done
|
|
||||||
with End_of_file -> ()
|
|
||||||
|
|
||||||
(* Try a fast copy using splice. If the FDs don't support that, switch to copying. *)
|
|
||||||
let _fast_copy_try_splice src dst =
|
|
||||||
try
|
|
||||||
while true do
|
|
||||||
let _ : int = Low_level.splice src ~dst ~len:max_int in
|
|
||||||
()
|
|
||||||
done
|
|
||||||
with
|
|
||||||
| End_of_file -> ()
|
|
||||||
| Eio.Exn.Io (Eio.Exn.X Eio_unix.Unix_error ((EAGAIN | EINVAL), "splice", _), _) -> fast_copy src dst
|
|
||||||
|
|
||||||
(* XXX workaround for issue #319, PR #327 *)
|
|
||||||
let fast_copy_try_splice src dst = fast_copy src dst
|
|
||||||
|
|
||||||
let[@tail_mod_cons] rec list_take n = function
|
|
||||||
| [] -> []
|
|
||||||
| x :: xs ->
|
|
||||||
if n = 0 then []
|
|
||||||
else x :: list_take (n - 1) xs
|
|
||||||
|
|
||||||
let truncate_to_iomax xs =
|
|
||||||
if List.compare_length_with xs Uring.iov_max <= 0 then xs
|
|
||||||
else list_take Uring.iov_max xs
|
|
||||||
|
|
||||||
(* Copy using the [Read_source_buffer] optimisation.
|
|
||||||
Avoids a copy if the source already has the data. *)
|
|
||||||
let copy_with_rsb rsb dst =
|
|
||||||
let write xs = Low_level.writev_single dst (truncate_to_iomax xs) in
|
|
||||||
try
|
|
||||||
while true do rsb write done
|
|
||||||
with End_of_file -> ()
|
|
||||||
|
|
||||||
(* Copy by allocating a chunk from the pre-shared buffer and asking
|
|
||||||
the source to write into it. This used when the other methods
|
|
||||||
aren't available. *)
|
|
||||||
let fallback_copy (type src) (module Src : Eio.Flow.Pi.SOURCE with type t = src) src dst =
|
|
||||||
let fallback () =
|
|
||||||
(* No chunks available. Use regular memory instead. *)
|
|
||||||
let buf = Cstruct.create 4096 in
|
|
||||||
try
|
|
||||||
while true do
|
|
||||||
let got = Src.single_read src buf in
|
|
||||||
Low_level.writev dst [Cstruct.sub buf 0 got]
|
|
||||||
done
|
|
||||||
with End_of_file -> ()
|
|
||||||
in
|
|
||||||
Low_level.with_chunk ~fallback @@ fun chunk ->
|
|
||||||
let chunk_cs = Uring.Region.to_cstruct chunk in
|
|
||||||
try
|
|
||||||
while true do
|
|
||||||
let got = Src.single_read src chunk_cs in
|
|
||||||
Low_level.write dst chunk got
|
|
||||||
done
|
|
||||||
with End_of_file -> ()
|
|
||||||
|
|
||||||
module Datagram_socket = struct
|
module Datagram_socket = struct
|
||||||
type tag = [`Generic | `Unix]
|
type tag = [`Generic | `Unix]
|
||||||
@ -145,78 +69,6 @@ let datagram_handler = Eio_unix.Pi.datagram_handler (module Datagram_socket)
|
|||||||
let datagram_socket fd =
|
let datagram_socket fd =
|
||||||
Eio.Resource.T (fd, datagram_handler)
|
Eio.Resource.T (fd, datagram_handler)
|
||||||
|
|
||||||
module Flow = struct
|
|
||||||
type tag = [`Generic | `Unix]
|
|
||||||
|
|
||||||
type t = Eio_unix.Fd.t
|
|
||||||
|
|
||||||
let fd t = t
|
|
||||||
|
|
||||||
let close = Eio_unix.Fd.close
|
|
||||||
|
|
||||||
let is_tty t = Fd.use_exn "isatty" t Unix.isatty
|
|
||||||
|
|
||||||
let stat = Low_level.fstat
|
|
||||||
|
|
||||||
let single_read t buf =
|
|
||||||
if is_tty t then (
|
|
||||||
(* Work-around for https://github.com/axboe/liburing/issues/354
|
|
||||||
(should be fixed in Linux 5.14) *)
|
|
||||||
Low_level.await_readable t
|
|
||||||
);
|
|
||||||
Low_level.readv t [buf]
|
|
||||||
|
|
||||||
let pread t ~file_offset bufs =
|
|
||||||
Low_level.readv ~file_offset t bufs
|
|
||||||
|
|
||||||
let pwrite t ~file_offset bufs =
|
|
||||||
Low_level.writev_single ~file_offset t (truncate_to_iomax bufs)
|
|
||||||
|
|
||||||
let read_methods = []
|
|
||||||
|
|
||||||
let single_write t bufs = Low_level.writev_single t (truncate_to_iomax bufs)
|
|
||||||
|
|
||||||
let copy t ~src =
|
|
||||||
match Eio_unix.Resource.fd_opt src with
|
|
||||||
| Some src -> fast_copy_try_splice src t
|
|
||||||
| None ->
|
|
||||||
let Eio.Resource.T (src, ops) = src in
|
|
||||||
let module Src = (val (Eio.Resource.get ops Eio.Flow.Pi.Source)) in
|
|
||||||
let rec aux = function
|
|
||||||
| Eio.Flow.Read_source_buffer rsb :: _ -> copy_with_rsb (rsb src) t
|
|
||||||
| _ :: xs -> aux xs
|
|
||||||
| [] -> fallback_copy (module Src) src t
|
|
||||||
in
|
|
||||||
aux Src.read_methods
|
|
||||||
|
|
||||||
let shutdown t cmd =
|
|
||||||
Low_level.shutdown t @@ match cmd with
|
|
||||||
| `Receive -> Unix.SHUTDOWN_RECEIVE
|
|
||||||
| `Send -> Unix.SHUTDOWN_SEND
|
|
||||||
| `All -> Unix.SHUTDOWN_ALL
|
|
||||||
|
|
||||||
let send_msg t ~fds data =
|
|
||||||
Low_level.send_msg t ~fds data
|
|
||||||
|
|
||||||
let recv_msg_with_fds t ~sw ~max_fds data =
|
|
||||||
let _addr, n, fds = Low_level.recv_msg_with_fds t ~sw ~max_fds data in
|
|
||||||
n, fds
|
|
||||||
|
|
||||||
let seek = Low_level.lseek
|
|
||||||
let sync = Low_level.fsync
|
|
||||||
let truncate = Low_level.ftruncate
|
|
||||||
end
|
|
||||||
|
|
||||||
let flow_handler = Eio_unix.Pi.flow_handler (module Flow)
|
|
||||||
|
|
||||||
let flow fd =
|
|
||||||
let r = Eio.Resource.T (fd, flow_handler) in
|
|
||||||
(r : [`Unix_fd | Eio_unix.Net.stream_socket_ty | Eio.File.rw_ty] r :>
|
|
||||||
[< `Unix_fd | Eio_unix.Net.stream_socket_ty | Eio.File.rw_ty] r)
|
|
||||||
|
|
||||||
let source fd = (flow fd :> _ Eio_unix.source)
|
|
||||||
let sink fd = (flow fd :> _ Eio_unix.sink)
|
|
||||||
|
|
||||||
module Listening_socket = struct
|
module Listening_socket = struct
|
||||||
type t = Fd.t
|
type t = Fd.t
|
||||||
|
|
||||||
@ -233,7 +85,7 @@ module Listening_socket = struct
|
|||||||
| Unix.ADDR_UNIX path -> `Unix path
|
| Unix.ADDR_UNIX path -> `Unix path
|
||||||
| Unix.ADDR_INET (host, port) -> `Tcp (Eio_unix.Net.Ipaddr.of_unix host, port)
|
| Unix.ADDR_INET (host, port) -> `Tcp (Eio_unix.Net.Ipaddr.of_unix host, port)
|
||||||
in
|
in
|
||||||
let flow = (flow client :> _ Eio.Net.stream_socket) in
|
let flow = (Flow.of_fd client :> _ Eio.Net.stream_socket) in
|
||||||
flow, client_addr
|
flow, client_addr
|
||||||
|
|
||||||
let listening_addr fd =
|
let listening_addr fd =
|
||||||
@ -261,7 +113,7 @@ let connect ~sw connect_addr =
|
|||||||
let sock_unix = Unix.socket ~cloexec:true (socket_domain_of connect_addr) Unix.SOCK_STREAM 0 in
|
let sock_unix = Unix.socket ~cloexec:true (socket_domain_of connect_addr) Unix.SOCK_STREAM 0 in
|
||||||
let sock = Fd.of_unix ~sw ~seekable:false ~close_unix:true sock_unix in
|
let sock = Fd.of_unix ~sw ~seekable:false ~close_unix:true sock_unix in
|
||||||
Low_level.connect sock addr;
|
Low_level.connect sock addr;
|
||||||
(flow sock :> _ Eio_unix.Net.stream_socket)
|
(Flow.of_fd sock :> _ Eio_unix.Net.stream_socket)
|
||||||
|
|
||||||
module Impl = struct
|
module Impl = struct
|
||||||
type t = unit
|
type t = unit
|
||||||
@ -495,7 +347,7 @@ end = struct
|
|||||||
~flags:Uring.Open_flags.cloexec
|
~flags:Uring.Open_flags.cloexec
|
||||||
~perm:0
|
~perm:0
|
||||||
in
|
in
|
||||||
(flow fd :> Eio.File.ro_ty r)
|
(Flow.of_fd fd :> Eio.File.ro_ty r)
|
||||||
|
|
||||||
let open_out t ~sw ~append ~create path =
|
let open_out t ~sw ~append ~create path =
|
||||||
let perm, flags =
|
let perm, flags =
|
||||||
@ -511,7 +363,7 @@ end = struct
|
|||||||
~flags:Uring.Open_flags.(cloexec + flags)
|
~flags:Uring.Open_flags.(cloexec + flags)
|
||||||
~perm
|
~perm
|
||||||
in
|
in
|
||||||
(flow fd :> Eio.File.rw_ty r)
|
(Flow.of_fd fd :> Eio.File.rw_ty r)
|
||||||
|
|
||||||
let native_internal t path =
|
let native_internal t path =
|
||||||
if Filename.is_relative path then (
|
if Filename.is_relative path then (
|
||||||
@ -592,7 +444,7 @@ end = struct
|
|||||||
~flags:Uring.Open_flags.(cloexec + path + (if follow then empty else nofollow))
|
~flags:Uring.Open_flags.(cloexec + path + (if follow then empty else nofollow))
|
||||||
~perm:0
|
~perm:0
|
||||||
in
|
in
|
||||||
Flow.stat fd
|
Low_level.fstat fd
|
||||||
)
|
)
|
||||||
|
|
||||||
let rename t old_path t2 new_path =
|
let rename t old_path t2 new_path =
|
||||||
@ -600,6 +452,9 @@ end = struct
|
|||||||
| Some fd2 -> Low_level.rename t.fd old_path fd2 new_path
|
| Some fd2 -> Low_level.rename t.fd old_path fd2 new_path
|
||||||
| None -> raise (Unix.Unix_error (Unix.EXDEV, "rename-dst", new_path))
|
| None -> raise (Unix.Unix_error (Unix.EXDEV, "rename-dst", new_path))
|
||||||
|
|
||||||
|
let symlink ~link_to t path =
|
||||||
|
Low_level.symlink ~link_to t.fd path
|
||||||
|
|
||||||
let pp f t = Fmt.string f (String.escaped t.label)
|
let pp f t = Fmt.string f (String.escaped t.label)
|
||||||
|
|
||||||
let fd t = t.fd
|
let fd t = t.fd
|
||||||
@ -619,26 +474,13 @@ end
|
|||||||
|
|
||||||
let dir ~label ~path fd = Eio.Resource.T (Dir.v ~label ~path fd, Dir_handler.v)
|
let dir ~label ~path fd = Eio.Resource.T (Dir.v ~label ~path fd, Dir_handler.v)
|
||||||
|
|
||||||
module Secure_random = struct
|
|
||||||
type t = unit
|
|
||||||
let single_read () buf = Low_level.getrandom buf; Cstruct.length buf
|
|
||||||
let read_methods = []
|
|
||||||
end
|
|
||||||
|
|
||||||
let secure_random =
|
|
||||||
let ops = Eio.Flow.Pi.source (module Secure_random) in
|
|
||||||
Eio.Resource.T ((), ops)
|
|
||||||
|
|
||||||
let stdenv ~run_event_loop =
|
let stdenv ~run_event_loop =
|
||||||
let stdin = source Eio_unix.Fd.stdin in
|
|
||||||
let stdout = sink Eio_unix.Fd.stdout in
|
|
||||||
let stderr = sink Eio_unix.Fd.stderr in
|
|
||||||
let fs = (dir ~label:"fs" ~path:"" Fs, "") in
|
let fs = (dir ~label:"fs" ~path:"" Fs, "") in
|
||||||
let cwd = (dir ~label:"cwd" ~path:"" Cwd, "") in
|
let cwd = (dir ~label:"cwd" ~path:"" Cwd, "") in
|
||||||
object (_ : stdenv)
|
object (_ : stdenv)
|
||||||
method stdin = stdin
|
method stdin = Flow.stdin
|
||||||
method stdout = stdout
|
method stdout = Flow.stdout
|
||||||
method stderr = stderr
|
method stderr = Flow.stderr
|
||||||
method net = net
|
method net = net
|
||||||
method process_mgr = process_mgr
|
method process_mgr = process_mgr
|
||||||
method domain_mgr = domain_mgr ~run_event_loop
|
method domain_mgr = domain_mgr ~run_event_loop
|
||||||
@ -646,7 +488,7 @@ let stdenv ~run_event_loop =
|
|||||||
method mono_clock = mono_clock
|
method mono_clock = mono_clock
|
||||||
method fs = (fs :> Eio.Fs.dir_ty Eio.Path.t)
|
method fs = (fs :> Eio.Fs.dir_ty Eio.Path.t)
|
||||||
method cwd = (cwd :> Eio.Fs.dir_ty Eio.Path.t)
|
method cwd = (cwd :> Eio.Fs.dir_ty Eio.Path.t)
|
||||||
method secure_random = secure_random
|
method secure_random = Flow.secure_random
|
||||||
method debug = Eio.Private.Debug.v
|
method debug = Eio.Private.Debug.v
|
||||||
method backend_id = "linux"
|
method backend_id = "linux"
|
||||||
end
|
end
|
||||||
@ -660,7 +502,11 @@ let run_event_loop (type a) ?fallback config (main : _ -> a) arg : a =
|
|||||||
| Eio_unix.Private.Get_monotonic_clock -> Some (fun k -> continue k mono_clock)
|
| Eio_unix.Private.Get_monotonic_clock -> Some (fun k -> continue k mono_clock)
|
||||||
| Eio_unix.Net.Import_socket_stream (sw, close_unix, fd) -> Some (fun k ->
|
| Eio_unix.Net.Import_socket_stream (sw, close_unix, fd) -> Some (fun k ->
|
||||||
let fd = Fd.of_unix ~sw ~seekable:false ~close_unix fd in
|
let fd = Fd.of_unix ~sw ~seekable:false ~close_unix fd in
|
||||||
continue k (flow fd :> _ Eio_unix.Net.stream_socket)
|
continue k (Flow.of_fd fd :> _ Eio_unix.Net.stream_socket)
|
||||||
|
)
|
||||||
|
| Eio_unix.Net.Import_socket_listening (sw, close_unix, fd) -> Some (fun k ->
|
||||||
|
let fd = Fd.of_unix ~sw ~seekable:false ~close_unix fd in
|
||||||
|
continue k (listening_socket fd)
|
||||||
)
|
)
|
||||||
| Eio_unix.Net.Import_socket_datagram (sw, close_unix, fd) -> Some (fun k ->
|
| Eio_unix.Net.Import_socket_datagram (sw, close_unix, fd) -> Some (fun k ->
|
||||||
let fd = Fd.of_unix ~sw ~seekable:false ~close_unix fd in
|
let fd = Fd.of_unix ~sw ~seekable:false ~close_unix fd in
|
||||||
@ -669,8 +515,8 @@ let run_event_loop (type a) ?fallback config (main : _ -> a) arg : a =
|
|||||||
| Eio_unix.Net.Socketpair_stream (sw, domain, protocol) -> Some (fun k ->
|
| Eio_unix.Net.Socketpair_stream (sw, domain, protocol) -> Some (fun k ->
|
||||||
match
|
match
|
||||||
let a, b = Unix.socketpair ~cloexec:true domain Unix.SOCK_STREAM protocol in
|
let a, b = Unix.socketpair ~cloexec:true domain Unix.SOCK_STREAM protocol in
|
||||||
let a = Fd.of_unix ~sw ~seekable:false ~close_unix:true a |> flow in
|
let a = Fd.of_unix ~sw ~seekable:false ~close_unix:true a |> Flow.of_fd in
|
||||||
let b = Fd.of_unix ~sw ~seekable:false ~close_unix:true b |> flow in
|
let b = Fd.of_unix ~sw ~seekable:false ~close_unix:true b |> Flow.of_fd in
|
||||||
((a :> _ Eio_unix.Net.stream_socket), (b :> _ Eio_unix.Net.stream_socket))
|
((a :> _ Eio_unix.Net.stream_socket), (b :> _ Eio_unix.Net.stream_socket))
|
||||||
with
|
with
|
||||||
| r -> continue k r
|
| r -> continue k r
|
||||||
@ -691,8 +537,8 @@ let run_event_loop (type a) ?fallback config (main : _ -> a) arg : a =
|
|||||||
| Eio_unix.Private.Pipe sw -> Some (fun k ->
|
| Eio_unix.Private.Pipe sw -> Some (fun k ->
|
||||||
match
|
match
|
||||||
let r, w = Low_level.pipe ~sw in
|
let r, w = Low_level.pipe ~sw in
|
||||||
let r = (flow r :> _ Eio_unix.source) in
|
let r = (Flow.of_fd r :> _ Eio_unix.source) in
|
||||||
let w = (flow w :> _ Eio_unix.sink) in
|
let w = (Flow.of_fd w :> _ Eio_unix.sink) in
|
||||||
(r, w)
|
(r, w)
|
||||||
with
|
with
|
||||||
| r -> continue k r
|
| r -> continue k r
|
||||||
|
@ -39,7 +39,11 @@
|
|||||||
#ifndef SYS_clone3
|
#ifndef SYS_clone3
|
||||||
# define SYS_clone3 435
|
# define SYS_clone3 435
|
||||||
# define CLONE_PIDFD 0x00001000
|
# define CLONE_PIDFD 0x00001000
|
||||||
struct clone_args {
|
#endif
|
||||||
|
|
||||||
|
// struct clone_args isn't defined in linux-lts headers, so define it here
|
||||||
|
// Note that this struct is versioned by size. See linux/sched.h for details
|
||||||
|
struct caml_eio_clone_args {
|
||||||
uint64_t flags;
|
uint64_t flags;
|
||||||
uint64_t pidfd;
|
uint64_t pidfd;
|
||||||
uint64_t child_tid;
|
uint64_t child_tid;
|
||||||
@ -48,11 +52,7 @@ struct clone_args {
|
|||||||
uint64_t stack;
|
uint64_t stack;
|
||||||
uint64_t stack_size;
|
uint64_t stack_size;
|
||||||
uint64_t tls;
|
uint64_t tls;
|
||||||
uint64_t set_tid;
|
|
||||||
uint64_t set_tid_size;
|
|
||||||
uint64_t cgroup;
|
|
||||||
};
|
};
|
||||||
#endif
|
|
||||||
|
|
||||||
// Make sure we have enough space for at least one entry.
|
// Make sure we have enough space for at least one entry.
|
||||||
#define DIRENT_BUF_SIZE (PATH_MAX + sizeof(struct dirent64))
|
#define DIRENT_BUF_SIZE (PATH_MAX + sizeof(struct dirent64))
|
||||||
@ -97,6 +97,24 @@ CAMLprim value caml_eio_renameat(value v_old_fd, value v_old_path, value v_new_f
|
|||||||
CAMLreturn(Val_unit);
|
CAMLreturn(Val_unit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CAMLprim value caml_eio_symlinkat(value v_old_path, value v_new_fd, value v_new_path) {
|
||||||
|
CAMLparam2(v_old_path, v_new_path);
|
||||||
|
char *old_path;
|
||||||
|
char *new_path;
|
||||||
|
int ret;
|
||||||
|
caml_unix_check_path(v_old_path, "symlinkat-old");
|
||||||
|
caml_unix_check_path(v_new_path, "symlinkat-new");
|
||||||
|
old_path = caml_stat_strdup(String_val(v_old_path));
|
||||||
|
new_path = caml_stat_strdup(String_val(v_new_path));
|
||||||
|
caml_enter_blocking_section();
|
||||||
|
ret = symlinkat(old_path, Int_val(v_new_fd), new_path);
|
||||||
|
caml_leave_blocking_section();
|
||||||
|
caml_stat_free(old_path);
|
||||||
|
caml_stat_free(new_path);
|
||||||
|
if (ret == -1) uerror("symlinkat", v_old_path);
|
||||||
|
CAMLreturn(Val_unit);
|
||||||
|
}
|
||||||
|
|
||||||
CAMLprim value caml_eio_getrandom(value v_ba, value v_off, value v_len) {
|
CAMLprim value caml_eio_getrandom(value v_ba, value v_off, value v_len) {
|
||||||
CAMLparam1(v_ba);
|
CAMLparam1(v_ba);
|
||||||
ssize_t ret;
|
ssize_t ret;
|
||||||
@ -160,9 +178,9 @@ static int pidfd_open(pid_t pid, unsigned int flags) {
|
|||||||
|
|
||||||
/* Like clone3, but falls back to fork if not supported.
|
/* Like clone3, but falls back to fork if not supported.
|
||||||
Also, raises exceptions rather then returning an error. */
|
Also, raises exceptions rather then returning an error. */
|
||||||
static pid_t clone3_with_fallback(struct clone_args *cl_args) {
|
static pid_t clone3_with_fallback(struct caml_eio_clone_args *cl_args) {
|
||||||
int *pidfd = (int *)(uintptr_t) cl_args->pidfd;
|
int *pidfd = (int *)(uintptr_t) cl_args->pidfd;
|
||||||
pid_t child_pid = syscall(SYS_clone3, cl_args, sizeof(struct clone_args));
|
pid_t child_pid = syscall(SYS_clone3, cl_args, sizeof(struct caml_eio_clone_args));
|
||||||
|
|
||||||
if (child_pid >= 0)
|
if (child_pid >= 0)
|
||||||
return child_pid; /* Success! */
|
return child_pid; /* Success! */
|
||||||
@ -198,7 +216,7 @@ CAMLprim value caml_eio_clone3(value v_errors, value v_actions) {
|
|||||||
CAMLlocal1(v_result);
|
CAMLlocal1(v_result);
|
||||||
pid_t child_pid;
|
pid_t child_pid;
|
||||||
int pidfd = -1; /* Is automatically close-on-exec */
|
int pidfd = -1; /* Is automatically close-on-exec */
|
||||||
struct clone_args cl_args = {
|
struct caml_eio_clone_args cl_args = {
|
||||||
.flags = CLONE_PIDFD,
|
.flags = CLONE_PIDFD,
|
||||||
.pidfd = (uintptr_t) &pidfd,
|
.pidfd = (uintptr_t) &pidfd,
|
||||||
.exit_signal = SIGCHLD, /* Needed for wait4 to work if we exit before exec */
|
.exit_signal = SIGCHLD, /* Needed for wait4 to work if we exit before exec */
|
||||||
|
157
lib_eio_linux/flow.ml
Normal file
157
lib_eio_linux/flow.ml
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
open Eio.Std
|
||||||
|
|
||||||
|
(* When copying between a source with an FD and a sink with an FD, we can share the chunk
|
||||||
|
and avoid copying. *)
|
||||||
|
let fast_copy src dst =
|
||||||
|
let fallback () =
|
||||||
|
(* No chunks available. Use regular memory instead. *)
|
||||||
|
let buf = Cstruct.create 4096 in
|
||||||
|
try
|
||||||
|
while true do
|
||||||
|
let got = Low_level.readv src [buf] in
|
||||||
|
Low_level.writev dst [Cstruct.sub buf 0 got]
|
||||||
|
done
|
||||||
|
with End_of_file -> ()
|
||||||
|
in
|
||||||
|
Low_level.with_chunk ~fallback @@ fun chunk ->
|
||||||
|
let chunk_size = Uring.Region.length chunk in
|
||||||
|
try
|
||||||
|
while true do
|
||||||
|
let got = Low_level.read_upto src chunk chunk_size in
|
||||||
|
Low_level.write dst chunk got
|
||||||
|
done
|
||||||
|
with End_of_file -> ()
|
||||||
|
|
||||||
|
(* Try a fast copy using splice. If the FDs don't support that, switch to copying. *)
|
||||||
|
let _fast_copy_try_splice src dst =
|
||||||
|
try
|
||||||
|
while true do
|
||||||
|
let _ : int = Low_level.splice src ~dst ~len:max_int in
|
||||||
|
()
|
||||||
|
done
|
||||||
|
with
|
||||||
|
| End_of_file -> ()
|
||||||
|
| Eio.Exn.Io (Eio.Exn.X Eio_unix.Unix_error ((EAGAIN | EINVAL), "splice", _), _) -> fast_copy src dst
|
||||||
|
|
||||||
|
(* XXX workaround for issue #319, PR #327 *)
|
||||||
|
let fast_copy_try_splice src dst = fast_copy src dst
|
||||||
|
|
||||||
|
let[@tail_mod_cons] rec list_take n = function
|
||||||
|
| [] -> []
|
||||||
|
| x :: xs ->
|
||||||
|
if n = 0 then []
|
||||||
|
else x :: list_take (n - 1) xs
|
||||||
|
|
||||||
|
let truncate_to_iomax xs =
|
||||||
|
if List.compare_length_with xs Uring.iov_max <= 0 then xs
|
||||||
|
else list_take Uring.iov_max xs
|
||||||
|
|
||||||
|
(* Copy using the [Read_source_buffer] optimisation.
|
||||||
|
Avoids a copy if the source already has the data. *)
|
||||||
|
let copy_with_rsb rsb dst =
|
||||||
|
let write xs = Low_level.writev_single dst (truncate_to_iomax xs) in
|
||||||
|
try
|
||||||
|
while true do rsb write done
|
||||||
|
with End_of_file -> ()
|
||||||
|
|
||||||
|
(* Copy by allocating a chunk from the pre-shared buffer and asking
|
||||||
|
the source to write into it. This used when the other methods
|
||||||
|
aren't available. *)
|
||||||
|
let fallback_copy (type src) (module Src : Eio.Flow.Pi.SOURCE with type t = src) src dst =
|
||||||
|
let fallback () =
|
||||||
|
(* No chunks available. Use regular memory instead. *)
|
||||||
|
let buf = Cstruct.create 4096 in
|
||||||
|
try
|
||||||
|
while true do
|
||||||
|
let got = Src.single_read src buf in
|
||||||
|
Low_level.writev dst [Cstruct.sub buf 0 got]
|
||||||
|
done
|
||||||
|
with End_of_file -> ()
|
||||||
|
in
|
||||||
|
Low_level.with_chunk ~fallback @@ fun chunk ->
|
||||||
|
let chunk_cs = Uring.Region.to_cstruct chunk in
|
||||||
|
try
|
||||||
|
while true do
|
||||||
|
let got = Src.single_read src chunk_cs in
|
||||||
|
Low_level.write dst chunk got
|
||||||
|
done
|
||||||
|
with End_of_file -> ()
|
||||||
|
|
||||||
|
module Impl = struct
|
||||||
|
type tag = [`Generic | `Unix]
|
||||||
|
|
||||||
|
type t = Eio_unix.Fd.t
|
||||||
|
|
||||||
|
let fd t = t
|
||||||
|
|
||||||
|
let close = Eio_unix.Fd.close
|
||||||
|
|
||||||
|
let stat = Low_level.fstat
|
||||||
|
|
||||||
|
let single_read t buf =
|
||||||
|
Low_level.readv t [buf]
|
||||||
|
|
||||||
|
let pread t ~file_offset bufs =
|
||||||
|
Low_level.readv ~file_offset t bufs
|
||||||
|
|
||||||
|
let pwrite t ~file_offset bufs =
|
||||||
|
Low_level.writev_single ~file_offset t (truncate_to_iomax bufs)
|
||||||
|
|
||||||
|
let read_methods = []
|
||||||
|
|
||||||
|
let single_write t bufs = Low_level.writev_single t (truncate_to_iomax bufs)
|
||||||
|
|
||||||
|
let copy t ~src =
|
||||||
|
match Eio_unix.Resource.fd_opt src with
|
||||||
|
| Some src -> fast_copy_try_splice src t
|
||||||
|
| None ->
|
||||||
|
let Eio.Resource.T (src, ops) = src in
|
||||||
|
let module Src = (val (Eio.Resource.get ops Eio.Flow.Pi.Source)) in
|
||||||
|
let rec aux = function
|
||||||
|
| Eio.Flow.Read_source_buffer rsb :: _ -> copy_with_rsb (rsb src) t
|
||||||
|
| _ :: xs -> aux xs
|
||||||
|
| [] -> fallback_copy (module Src) src t
|
||||||
|
in
|
||||||
|
aux Src.read_methods
|
||||||
|
|
||||||
|
let shutdown t cmd =
|
||||||
|
Low_level.shutdown t @@ match cmd with
|
||||||
|
| `Receive -> Unix.SHUTDOWN_RECEIVE
|
||||||
|
| `Send -> Unix.SHUTDOWN_SEND
|
||||||
|
| `All -> Unix.SHUTDOWN_ALL
|
||||||
|
|
||||||
|
let send_msg t ~fds data =
|
||||||
|
Low_level.send_msg t ~fds data
|
||||||
|
|
||||||
|
let recv_msg_with_fds t ~sw ~max_fds data =
|
||||||
|
let _addr, n, fds = Low_level.recv_msg_with_fds t ~sw ~max_fds data in
|
||||||
|
n, fds
|
||||||
|
|
||||||
|
let seek = Low_level.lseek
|
||||||
|
let sync = Low_level.fsync
|
||||||
|
let truncate = Low_level.ftruncate
|
||||||
|
end
|
||||||
|
|
||||||
|
let flow_handler = Eio_unix.Pi.flow_handler (module Impl)
|
||||||
|
|
||||||
|
let of_fd fd =
|
||||||
|
let r = Eio.Resource.T (fd, flow_handler) in
|
||||||
|
(r : [`Unix_fd | Eio_unix.Net.stream_socket_ty | Eio.File.rw_ty] r :>
|
||||||
|
[< `Unix_fd | Eio_unix.Net.stream_socket_ty | Eio.File.rw_ty] r)
|
||||||
|
|
||||||
|
let source fd = (of_fd fd :> Eio_unix.source_ty r)
|
||||||
|
let sink fd = (of_fd fd :> Eio_unix.sink_ty r)
|
||||||
|
|
||||||
|
let stdin = source Eio_unix.Fd.stdin
|
||||||
|
let stdout = sink Eio_unix.Fd.stdout
|
||||||
|
let stderr = sink Eio_unix.Fd.stderr
|
||||||
|
|
||||||
|
module Secure_random = struct
|
||||||
|
type t = unit
|
||||||
|
let single_read () buf = Low_level.getrandom buf; Cstruct.length buf
|
||||||
|
let read_methods = []
|
||||||
|
end
|
||||||
|
|
||||||
|
let secure_random =
|
||||||
|
let ops = Eio.Flow.Pi.source (module Secure_random) in
|
||||||
|
Eio.Resource.T ((), ops)
|
9
lib_eio_linux/flow.mli
Normal file
9
lib_eio_linux/flow.mli
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
open Eio.Std
|
||||||
|
|
||||||
|
val of_fd : Eio_unix.Fd.t -> [< `Unix_fd | Eio_unix.Net.stream_socket_ty | Eio.File.rw_ty] r
|
||||||
|
|
||||||
|
val stdin : Eio_unix.source_ty r
|
||||||
|
val stdout : Eio_unix.sink_ty r
|
||||||
|
val stderr : Eio_unix.sink_ty r
|
||||||
|
|
||||||
|
val secure_random : Eio.Flow.source_ty r
|
@ -207,11 +207,39 @@ let write ?file_offset:off fd buf len =
|
|||||||
raise @@ Err.wrap (Uring.error_of_errno res) "write" ""
|
raise @@ Err.wrap (Uring.error_of_errno res) "write" ""
|
||||||
)
|
)
|
||||||
|
|
||||||
let alloc_fixed () = Effect.perform Sched.Alloc
|
let alloc_fixed () =
|
||||||
|
let s = Sched.get () in
|
||||||
|
match s.mem with
|
||||||
|
| None -> None
|
||||||
|
| Some mem ->
|
||||||
|
match Uring.Region.alloc mem with
|
||||||
|
| buf -> Some buf
|
||||||
|
| exception Uring.Region.No_space -> None
|
||||||
|
|
||||||
let alloc_fixed_or_wait () = Effect.perform Sched.Alloc_or_wait
|
let alloc_fixed_or_wait () =
|
||||||
|
let s = Sched.get () in
|
||||||
|
match s.mem with
|
||||||
|
| None -> failwith "No fixed buffer available"
|
||||||
|
| Some mem ->
|
||||||
|
match Uring.Region.alloc mem with
|
||||||
|
| buf -> buf
|
||||||
|
| exception Uring.Region.No_space ->
|
||||||
|
let id = Eio.Private.Trace.mint_id () in
|
||||||
|
let trigger = Eio.Private.Single_waiter.create () in
|
||||||
|
let node = Lwt_dllist.add_r trigger s.mem_q in
|
||||||
|
try
|
||||||
|
Eio.Private.Single_waiter.await trigger "alloc_fixed_or_wait" id
|
||||||
|
with ex ->
|
||||||
|
Lwt_dllist.remove node;
|
||||||
|
raise ex
|
||||||
|
|
||||||
let free_fixed buf = Effect.perform (Sched.Free buf)
|
let rec free_fixed buf =
|
||||||
|
let s = Sched.get () in
|
||||||
|
match Lwt_dllist.take_opt_l s.mem_q with
|
||||||
|
| None -> Uring.Region.free buf
|
||||||
|
| Some k ->
|
||||||
|
if not (Eio.Private.Single_waiter.wake k (Ok buf)) then
|
||||||
|
free_fixed buf (* [k] was already cancelled, but not yet removed from the queue *)
|
||||||
|
|
||||||
let splice src ~dst ~len =
|
let splice src ~dst ~len =
|
||||||
Fd.use_exn "splice-src" src @@ fun src ->
|
Fd.use_exn "splice-src" src @@ fun src ->
|
||||||
@ -330,6 +358,8 @@ external eio_mkdirat : Unix.file_descr -> string -> Unix.file_perm -> unit = "ca
|
|||||||
|
|
||||||
external eio_renameat : Unix.file_descr -> string -> Unix.file_descr -> string -> unit = "caml_eio_renameat"
|
external eio_renameat : Unix.file_descr -> string -> Unix.file_descr -> string -> unit = "caml_eio_renameat"
|
||||||
|
|
||||||
|
external eio_symlinkat : string -> Unix.file_descr -> string -> unit = "caml_eio_symlinkat"
|
||||||
|
|
||||||
external eio_getrandom : Cstruct.buffer -> int -> int -> int = "caml_eio_getrandom"
|
external eio_getrandom : Cstruct.buffer -> int -> int -> int = "caml_eio_getrandom"
|
||||||
|
|
||||||
external eio_getdents : Unix.file_descr -> string list = "caml_eio_getdents"
|
external eio_getdents : Unix.file_descr -> string list = "caml_eio_getdents"
|
||||||
@ -450,6 +480,12 @@ let rename old_dir old_path new_dir new_path =
|
|||||||
new_parent new_leaf
|
new_parent new_leaf
|
||||||
with Unix.Unix_error (code, name, arg) -> raise @@ Err.wrap_fs code name arg
|
with Unix.Unix_error (code, name, arg) -> raise @@ Err.wrap_fs code name arg
|
||||||
|
|
||||||
|
let symlink ~link_to dir path =
|
||||||
|
with_parent_dir "symlinkat-new" dir path @@ fun parent leaf ->
|
||||||
|
try
|
||||||
|
eio_symlinkat link_to parent leaf
|
||||||
|
with Unix.Unix_error (code, name, arg) -> raise @@ Err.wrap_fs code name arg
|
||||||
|
|
||||||
let shutdown socket command =
|
let shutdown socket command =
|
||||||
try
|
try
|
||||||
Fd.use_exn "shutdown" socket @@ fun fd ->
|
Fd.use_exn "shutdown" socket @@ fun fd ->
|
||||||
@ -563,7 +599,10 @@ module Process = struct
|
|||||||
let exit_status, set_exit_status = Promise.create () in
|
let exit_status, set_exit_status = Promise.create () in
|
||||||
let t =
|
let t =
|
||||||
Fd.use_exn "errors-w" errors_w @@ fun errors_w ->
|
Fd.use_exn "errors-w" errors_w @@ fun errors_w ->
|
||||||
let pid, pid_fd = eio_spawn errors_w c_actions in
|
let pid, pid_fd =
|
||||||
|
Eio.Private.Trace.with_span "spawn" @@ fun () ->
|
||||||
|
eio_spawn errors_w c_actions
|
||||||
|
in
|
||||||
let pid_fd = Fd.of_unix ~sw ~seekable:false ~close_unix:true pid_fd in
|
let pid_fd = Fd.of_unix ~sw ~seekable:false ~close_unix:true pid_fd in
|
||||||
{ pid; pid_fd; exit_status }
|
{ pid; pid_fd; exit_status }
|
||||||
in
|
in
|
||||||
|
@ -150,6 +150,9 @@ val unlink : rmdir:bool -> dir_fd -> string -> unit
|
|||||||
val rename : dir_fd -> string -> dir_fd -> string -> unit
|
val rename : dir_fd -> string -> dir_fd -> string -> unit
|
||||||
(** [rename old_dir old_path new_dir new_path] renames [old_dir / old_path] as [new_dir / new_path]. *)
|
(** [rename old_dir old_path new_dir new_path] renames [old_dir / old_path] as [new_dir / new_path]. *)
|
||||||
|
|
||||||
|
val symlink : link_to:string -> dir_fd -> string -> unit
|
||||||
|
(** [symlink ~link_to dir path] creates a new symlink at [dir / path] pointing to [link_to]. *)
|
||||||
|
|
||||||
val pipe : sw:Switch.t -> fd * fd
|
val pipe : sw:Switch.t -> fd * fd
|
||||||
(** [pipe ~sw] returns a pair [r, w] with the readable and writeable ends of a new pipe. *)
|
(** [pipe ~sw] returns a pair [r, w] with the readable and writeable ends of a new pipe. *)
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
CAMLprim value caml_eio_eventfd(value);
|
CAMLprim value caml_eio_eventfd(value);
|
||||||
CAMLprim value caml_eio_mkdirat(value, value, value);
|
CAMLprim value caml_eio_mkdirat(value, value, value);
|
||||||
CAMLprim value caml_eio_renameat(value, value, value, value);
|
CAMLprim value caml_eio_renameat(value, value, value, value);
|
||||||
|
CAMLprim value caml_eio_symlinkat(value, value, value);
|
||||||
CAMLprim value caml_eio_getrandom(value, value, value);
|
CAMLprim value caml_eio_getrandom(value, value, value);
|
||||||
CAMLprim value caml_eio_getdents(value);
|
CAMLprim value caml_eio_getdents(value);
|
||||||
CAMLprim value caml_eio_clone3(value, value);
|
CAMLprim value caml_eio_clone3(value, value);
|
||||||
|
@ -50,7 +50,7 @@ type t = {
|
|||||||
uring: io_job Uring.t;
|
uring: io_job Uring.t;
|
||||||
mem: Uring.Region.t option;
|
mem: Uring.Region.t option;
|
||||||
io_q: (t -> unit) Queue.t; (* waiting for room on [uring] *)
|
io_q: (t -> unit) Queue.t; (* waiting for room on [uring] *)
|
||||||
mem_q : Uring.Region.chunk Suspended.t Queue.t;
|
mem_q : Uring.Region.chunk Eio.Private.Single_waiter.t Lwt_dllist.t;
|
||||||
|
|
||||||
(* The queue of runnable fibers ready to be resumed. Note: other domains can also add work items here. *)
|
(* The queue of runnable fibers ready to be resumed. Note: other domains can also add work items here. *)
|
||||||
run_q : runnable Lf_queue.t;
|
run_q : runnable Lf_queue.t;
|
||||||
@ -74,9 +74,9 @@ type t = {
|
|||||||
type _ Effect.t +=
|
type _ Effect.t +=
|
||||||
| Enter : (t -> 'a Suspended.t -> unit) -> 'a Effect.t
|
| Enter : (t -> 'a Suspended.t -> unit) -> 'a Effect.t
|
||||||
| Cancel : io_job Uring.job -> unit Effect.t
|
| Cancel : io_job Uring.job -> unit Effect.t
|
||||||
| Alloc : Uring.Region.chunk option Effect.t
|
| Get : t Effect.t
|
||||||
| Alloc_or_wait : Uring.Region.chunk Effect.t
|
|
||||||
| Free : Uring.Region.chunk -> unit Effect.t
|
let get () = Effect.perform Get
|
||||||
|
|
||||||
let wake_buffer =
|
let wake_buffer =
|
||||||
let b = Bytes.create 8 in
|
let b = Bytes.create 8 in
|
||||||
@ -113,7 +113,10 @@ let enter op fn =
|
|||||||
Effect.perform (Enter fn)
|
Effect.perform (Enter fn)
|
||||||
|
|
||||||
let submit uring =
|
let submit uring =
|
||||||
Trace.with_span "submit" (fun () -> Uring.submit uring)
|
if Uring.sqe_ready uring > 0 then
|
||||||
|
Trace.with_span "submit" (fun () -> Uring.submit uring)
|
||||||
|
else
|
||||||
|
0
|
||||||
|
|
||||||
let rec enqueue_job t fn =
|
let rec enqueue_job t fn =
|
||||||
match fn () with
|
match fn () with
|
||||||
@ -228,7 +231,6 @@ let rec schedule ({run_q; sleep_q; mem_q; uring; _} as st) : [`Exit_scheduler] =
|
|||||||
Lf_queue.push run_q IO; (* Re-inject IO job in the run queue *)
|
Lf_queue.push run_q IO; (* Re-inject IO job in the run queue *)
|
||||||
handle_complete st ~runnable result
|
handle_complete st ~runnable result
|
||||||
| None ->
|
| None ->
|
||||||
ignore (submit uring : int);
|
|
||||||
let timeout =
|
let timeout =
|
||||||
match next_due with
|
match next_due with
|
||||||
| `Wait_until time ->
|
| `Wait_until time ->
|
||||||
@ -239,12 +241,13 @@ let rec schedule ({run_q; sleep_q; mem_q; uring; _} as st) : [`Exit_scheduler] =
|
|||||||
| `Nothing -> None
|
| `Nothing -> None
|
||||||
in
|
in
|
||||||
if not (Lf_queue.is_empty st.run_q) then (
|
if not (Lf_queue.is_empty st.run_q) then (
|
||||||
|
ignore (submit uring : int);
|
||||||
Lf_queue.push run_q IO; (* Re-inject IO job in the run queue *)
|
Lf_queue.push run_q IO; (* Re-inject IO job in the run queue *)
|
||||||
schedule st
|
schedule st
|
||||||
) else if timeout = None && Uring.active_ops uring = 0 then (
|
) else if timeout = None && Uring.active_ops uring = 0 then (
|
||||||
(* Nothing further can happen at this point.
|
(* Nothing further can happen at this point.
|
||||||
If there are no events in progress but also still no memory available, something has gone wrong! *)
|
If there are no events in progress but also still no memory available, something has gone wrong! *)
|
||||||
assert (Queue.length mem_q = 0);
|
assert (Lwt_dllist.length mem_q = 0);
|
||||||
Lf_queue.close st.run_q; (* Just to catch bugs if something tries to enqueue later *)
|
Lf_queue.close st.run_q; (* Just to catch bugs if something tries to enqueue later *)
|
||||||
`Exit_scheduler
|
`Exit_scheduler
|
||||||
) else (
|
) else (
|
||||||
@ -254,7 +257,14 @@ let rec schedule ({run_q; sleep_q; mem_q; uring; _} as st) : [`Exit_scheduler] =
|
|||||||
If [need_wakeup] is still [true], this is fine because we don't promise to do that.
|
If [need_wakeup] is still [true], this is fine because we don't promise to do that.
|
||||||
If [need_wakeup = false], a wake-up event will arrive and wake us up soon. *)
|
If [need_wakeup = false], a wake-up event will arrive and wake us up soon. *)
|
||||||
Trace.suspend_domain Begin;
|
Trace.suspend_domain Begin;
|
||||||
let result = Uring.wait ?timeout uring in
|
let result =
|
||||||
|
(* Hack: liburing automatically retries [io_uring_enter] if an
|
||||||
|
interrupt is received and no timeout is set. However, we need
|
||||||
|
to return to OCaml mode so any pending signal handlers can
|
||||||
|
run. See: https://github.com/ocaml-multicore/eio/issues/732 *)
|
||||||
|
let timeout = Option.value timeout ~default:1e9 in
|
||||||
|
Uring.wait ~timeout uring
|
||||||
|
in
|
||||||
Trace.suspend_domain End;
|
Trace.suspend_domain End;
|
||||||
Atomic.set st.need_wakeup false;
|
Atomic.set st.need_wakeup false;
|
||||||
Lf_queue.push run_q IO; (* Re-inject IO job in the run queue *)
|
Lf_queue.push run_q IO; (* Re-inject IO job in the run queue *)
|
||||||
@ -267,6 +277,7 @@ let rec schedule ({run_q; sleep_q; mem_q; uring; _} as st) : [`Exit_scheduler] =
|
|||||||
) else (
|
) else (
|
||||||
(* Someone added a new job while we were setting [need_wakeup] to [true].
|
(* Someone added a new job while we were setting [need_wakeup] to [true].
|
||||||
They might or might not have seen that, so we can't be sure they'll send an event. *)
|
They might or might not have seen that, so we can't be sure they'll send an event. *)
|
||||||
|
ignore (submit uring : int);
|
||||||
Atomic.set st.need_wakeup false;
|
Atomic.set st.need_wakeup false;
|
||||||
Lf_queue.push run_q IO; (* Re-inject IO job in the run queue *)
|
Lf_queue.push run_q IO; (* Re-inject IO job in the run queue *)
|
||||||
schedule st
|
schedule st
|
||||||
@ -328,21 +339,6 @@ and complete_rw_req st ({len; cur_off; action; _} as req) res =
|
|||||||
| _, Exactly len -> Suspended.continue action len
|
| _, Exactly len -> Suspended.continue action len
|
||||||
| n, Upto _ -> Suspended.continue action n
|
| n, Upto _ -> Suspended.continue action n
|
||||||
|
|
||||||
let alloc_buf_or_wait st k =
|
|
||||||
match st.mem with
|
|
||||||
| None -> Suspended.discontinue k (Failure "No fixed buffer available")
|
|
||||||
| Some mem ->
|
|
||||||
match Uring.Region.alloc mem with
|
|
||||||
| buf -> Suspended.continue k buf
|
|
||||||
| exception Uring.Region.No_space ->
|
|
||||||
Queue.push k st.mem_q;
|
|
||||||
schedule st
|
|
||||||
|
|
||||||
let free_buf st buf =
|
|
||||||
match Queue.take_opt st.mem_q with
|
|
||||||
| None -> Uring.Region.free buf
|
|
||||||
| Some k -> enqueue_thread st k buf
|
|
||||||
|
|
||||||
let rec enqueue_poll_add fd poll_mask st action =
|
let rec enqueue_poll_add fd poll_mask st action =
|
||||||
Trace.log "poll_add";
|
Trace.log "poll_add";
|
||||||
let retry = with_cancel_hook ~action st (fun () ->
|
let retry = with_cancel_hook ~action st (fun () ->
|
||||||
@ -400,8 +396,9 @@ let run ~extra_effects st main arg =
|
|||||||
Fiber_context.destroy fiber;
|
Fiber_context.destroy fiber;
|
||||||
Printexc.raise_with_backtrace ex (Printexc.get_raw_backtrace ())
|
Printexc.raise_with_backtrace ex (Printexc.get_raw_backtrace ())
|
||||||
);
|
);
|
||||||
effc = fun (type a) (e : a Effect.t) ->
|
effc = fun (type a) (e : a Effect.t) : ((a, _) continuation -> _) option ->
|
||||||
match e with
|
match e with
|
||||||
|
| Get -> Some (fun k -> continue k st)
|
||||||
| Enter fn -> Some (fun k ->
|
| Enter fn -> Some (fun k ->
|
||||||
match Fiber_context.get_error fiber with
|
match Fiber_context.get_error fiber with
|
||||||
| Some e -> discontinue k e
|
| Some e -> discontinue k e
|
||||||
@ -456,22 +453,6 @@ let run ~extra_effects st main arg =
|
|||||||
Eio_unix.Private.Thread_pool.submit st.thread_pool ~ctx:fiber ~enqueue fn;
|
Eio_unix.Private.Thread_pool.submit st.thread_pool ~ctx:fiber ~enqueue fn;
|
||||||
schedule st
|
schedule st
|
||||||
)
|
)
|
||||||
| Alloc -> Some (fun k ->
|
|
||||||
match st.mem with
|
|
||||||
| None -> continue k None
|
|
||||||
| Some mem ->
|
|
||||||
match Uring.Region.alloc mem with
|
|
||||||
| buf -> continue k (Some buf)
|
|
||||||
| exception Uring.Region.No_space -> continue k None
|
|
||||||
)
|
|
||||||
| Alloc_or_wait -> Some (fun k ->
|
|
||||||
let k = { Suspended.k; fiber } in
|
|
||||||
alloc_buf_or_wait st k
|
|
||||||
)
|
|
||||||
| Free buf -> Some (fun k ->
|
|
||||||
free_buf st buf;
|
|
||||||
continue k ()
|
|
||||||
)
|
|
||||||
| e -> extra_effects.effc e
|
| e -> extra_effects.effc e
|
||||||
}
|
}
|
||||||
in
|
in
|
||||||
@ -479,7 +460,7 @@ let run ~extra_effects st main arg =
|
|||||||
let `Exit_scheduler =
|
let `Exit_scheduler =
|
||||||
let new_fiber = Fiber_context.make_root () in
|
let new_fiber = Fiber_context.make_root () in
|
||||||
Domain_local_await.using
|
Domain_local_await.using
|
||||||
~prepare_for_await:Eio.Private.Dla.prepare_for_await
|
~prepare_for_await:Eio_utils.Dla.prepare_for_await
|
||||||
~while_running:(fun () ->
|
~while_running:(fun () ->
|
||||||
fork ~new_fiber (fun () ->
|
fork ~new_fiber (fun () ->
|
||||||
Switch.run_protected ~name:"eio_linux" (fun sw ->
|
Switch.run_protected ~name:"eio_linux" (fun sw ->
|
||||||
@ -536,11 +517,14 @@ let with_sched ?(fallback=no_fallback) config fn =
|
|||||||
| exception Unix.Unix_error(EPERM, _, _) -> fallback (`Msg "io_uring is not available (permission denied)")
|
| exception Unix.Unix_error(EPERM, _, _) -> fallback (`Msg "io_uring is not available (permission denied)")
|
||||||
| uring ->
|
| uring ->
|
||||||
let probe = Uring.get_probe uring in
|
let probe = Uring.get_probe uring in
|
||||||
if not (Uring.op_supported probe Uring.Op.shutdown) then (
|
if not (Uring.op_supported probe Uring.Op.mkdirat) then (
|
||||||
Uring.exit uring;
|
Uring.exit uring;
|
||||||
fallback (`Msg "Linux >= 5.11 is required for io_uring support")
|
fallback (`Msg "Linux >= 5.15 is required for io_uring support")
|
||||||
) else (
|
) else (
|
||||||
statx_works := Uring.op_supported probe Uring.Op.msg_ring;
|
(* The reason for an if here is to make sure we only set it once, when
|
||||||
|
the first domain is starting. This is just to avoid a tsan warning. *)
|
||||||
|
if not !statx_works && Uring.op_supported probe Uring.Op.msg_ring then
|
||||||
|
statx_works := true;
|
||||||
match
|
match
|
||||||
let mem =
|
let mem =
|
||||||
let fixed_buf_len = block_size * n_blocks in
|
let fixed_buf_len = block_size * n_blocks in
|
||||||
@ -555,7 +539,7 @@ let with_sched ?(fallback=no_fallback) config fn =
|
|||||||
Lf_queue.push run_q IO;
|
Lf_queue.push run_q IO;
|
||||||
let sleep_q = Zzz.create () in
|
let sleep_q = Zzz.create () in
|
||||||
let io_q = Queue.create () in
|
let io_q = Queue.create () in
|
||||||
let mem_q = Queue.create () in
|
let mem_q = Lwt_dllist.create () in
|
||||||
with_eventfd @@ fun eventfd ->
|
with_eventfd @@ fun eventfd ->
|
||||||
let thread_pool = Eio_unix.Private.Thread_pool.create ~sleep_q in
|
let thread_pool = Eio_unix.Private.Thread_pool.create ~sleep_q in
|
||||||
fn { mem; uring; run_q; io_q; mem_q; eventfd; need_wakeup = Atomic.make false; sleep_q; thread_pool }
|
fn { mem; uring; run_q; io_q; mem_q; eventfd; need_wakeup = Atomic.make false; sleep_q; thread_pool }
|
||||||
|
@ -130,7 +130,7 @@ let test_read_exact () =
|
|||||||
let ( / ) = Eio.Path.( / ) in
|
let ( / ) = Eio.Path.( / ) in
|
||||||
let path = env#cwd / "test.data" in
|
let path = env#cwd / "test.data" in
|
||||||
let msg = "hello" in
|
let msg = "hello" in
|
||||||
Eio.Path.save path ("!" ^ msg) ~create:(`Exclusive 0o600);
|
Eio.Path.save path ("!" ^ msg) ~create:(`Or_truncate 0o600);
|
||||||
Switch.run @@ fun sw ->
|
Switch.run @@ fun sw ->
|
||||||
let fd = Eio_linux.Low_level.openat2 ~sw
|
let fd = Eio_linux.Low_level.openat2 ~sw
|
||||||
~access:`R
|
~access:`R
|
||||||
@ -162,7 +162,7 @@ let test_statx () =
|
|||||||
Eio_linux.run ~queue_depth:4 @@ fun env ->
|
Eio_linux.run ~queue_depth:4 @@ fun env ->
|
||||||
let ( / ) = Eio.Path.( / ) in
|
let ( / ) = Eio.Path.( / ) in
|
||||||
let path = env#cwd / "test2.data" in
|
let path = env#cwd / "test2.data" in
|
||||||
Eio.Path.with_open_out path ~create:(`Exclusive 0o600) @@ fun file ->
|
Eio.Path.with_open_out path ~create:(`Or_truncate 0o600) @@ fun file ->
|
||||||
Eio.Flow.copy_string "hello" file;
|
Eio.Flow.copy_string "hello" file;
|
||||||
let buf = Uring.Statx.create () in
|
let buf = Uring.Statx.create () in
|
||||||
let test expected_len ~follow dir path =
|
let test expected_len ~follow dir path =
|
||||||
@ -198,18 +198,65 @@ let test_statx () =
|
|||||||
);
|
);
|
||||||
()
|
()
|
||||||
|
|
||||||
|
(* Ensure that an OCaml signal handler will run, even if we're sleeping in liburing at the time.
|
||||||
|
The problem here is that [__sys_io_uring_enter2] doesn't return EINTR, because it did successfully
|
||||||
|
submit an item. This causes liburing to retry without giving our OCaml signal handler a chance to run.
|
||||||
|
Note: we can't run this test with a timeout because liburing does return in that case! *)
|
||||||
|
let test_signal_race () =
|
||||||
|
Eio_linux.run @@ fun _env ->
|
||||||
|
let cond = Eio.Condition.create () in
|
||||||
|
let handle _ = Eio.Condition.broadcast cond in
|
||||||
|
Sys.(set_signal sigalrm) (Signal_handle handle);
|
||||||
|
Fiber.both
|
||||||
|
(fun () -> Eio.Condition.await_no_mutex cond)
|
||||||
|
(fun () -> ignore (Unix.setitimer ITIMER_REAL { it_interval = 0.; it_value = 0.001 } : Unix.interval_timer_status))
|
||||||
|
|
||||||
|
let test_alloc_fixed_or_wait () =
|
||||||
|
Eio_linux.run ~n_blocks:1 @@ fun _env ->
|
||||||
|
let block = Eio_linux.Low_level.alloc_fixed_or_wait () in
|
||||||
|
(* We have to wait for the block, but get cancelled while waiting. *)
|
||||||
|
begin
|
||||||
|
try
|
||||||
|
Fiber.both
|
||||||
|
(fun () -> ignore (Eio_linux.Low_level.alloc_fixed_or_wait () : Uring.Region.chunk))
|
||||||
|
(fun () -> raise Exit);
|
||||||
|
with Exit -> ()
|
||||||
|
end;
|
||||||
|
(* We have to wait for the block, and get it when the old one is freed. *)
|
||||||
|
Fiber.both
|
||||||
|
(fun () ->
|
||||||
|
let x = Eio_linux.Low_level.alloc_fixed_or_wait () in
|
||||||
|
Eio_linux.Low_level.free_fixed x
|
||||||
|
)
|
||||||
|
(fun () ->
|
||||||
|
Eio_linux.Low_level.free_fixed block
|
||||||
|
);
|
||||||
|
(* The old block is passed to the waiting fiber, but it's cancelled. *)
|
||||||
|
let block = Eio_linux.Low_level.alloc_fixed_or_wait () in
|
||||||
|
Fiber.both
|
||||||
|
(fun () ->
|
||||||
|
Fiber.first
|
||||||
|
(fun () -> ignore (Eio_linux.Low_level.alloc_fixed_or_wait ()); assert false)
|
||||||
|
(fun () -> ())
|
||||||
|
)
|
||||||
|
(fun () -> Eio_linux.Low_level.free_fixed block);
|
||||||
|
let block = Eio_linux.Low_level.alloc_fixed_or_wait () in
|
||||||
|
Eio_linux.Low_level.free_fixed block
|
||||||
|
|
||||||
let () =
|
let () =
|
||||||
let open Alcotest in
|
let open Alcotest in
|
||||||
run "eio_linux" [
|
run "eio_linux" [
|
||||||
"io", [
|
"io", [
|
||||||
test_case "copy" `Quick test_copy;
|
test_case "copy" `Quick test_copy;
|
||||||
test_case "direct_copy" `Quick test_direct_copy;
|
test_case "direct_copy" `Quick test_direct_copy;
|
||||||
test_case "poll_add" `Quick test_poll_add;
|
test_case "poll_add" `Quick test_poll_add;
|
||||||
test_case "poll_add_busy" `Quick test_poll_add_busy;
|
test_case "poll_add_busy" `Quick test_poll_add_busy;
|
||||||
test_case "iovec" `Quick test_iovec;
|
test_case "iovec" `Quick test_iovec;
|
||||||
test_case "no_sqe" `Quick test_no_sqe;
|
test_case "no_sqe" `Quick test_no_sqe;
|
||||||
test_case "read_exact" `Quick test_read_exact;
|
test_case "read_exact" `Quick test_read_exact;
|
||||||
test_case "expose_backend" `Quick test_expose_backend;
|
test_case "expose_backend" `Quick test_expose_backend;
|
||||||
test_case "statx" `Quick test_statx;
|
test_case "statx" `Quick test_statx;
|
||||||
|
test_case "signal_race" `Quick test_signal_race;
|
||||||
|
test_case "alloc-fixed-or-wait" `Quick test_alloc_fixed_or_wait;
|
||||||
];
|
];
|
||||||
]
|
]
|
||||||
|
@ -48,6 +48,11 @@ let run_event_loop fn x =
|
|||||||
Unix.set_nonblock unix_fd;
|
Unix.set_nonblock unix_fd;
|
||||||
continue k (Flow.of_fd fd :> _ Eio_unix.Net.stream_socket)
|
continue k (Flow.of_fd fd :> _ Eio_unix.Net.stream_socket)
|
||||||
)
|
)
|
||||||
|
| Eio_unix.Net.Import_socket_listening (sw, close_unix, unix_fd) -> Some (fun k ->
|
||||||
|
let fd = Fd.of_unix ~sw ~blocking:false ~close_unix unix_fd in
|
||||||
|
Unix.set_nonblock unix_fd;
|
||||||
|
continue k (Net.listening_socket ~hook:Switch.null_hook fd)
|
||||||
|
)
|
||||||
| Eio_unix.Net.Import_socket_datagram (sw, close_unix, unix_fd) -> Some (fun k ->
|
| Eio_unix.Net.Import_socket_datagram (sw, close_unix, unix_fd) -> Some (fun k ->
|
||||||
let fd = Fd.of_unix ~sw ~blocking:false ~close_unix unix_fd in
|
let fd = Fd.of_unix ~sw ~blocking:false ~close_unix unix_fd in
|
||||||
Unix.set_nonblock unix_fd;
|
Unix.set_nonblock unix_fd;
|
||||||
|
@ -384,6 +384,27 @@ CAMLprim value caml_eio_posix_renameat(value v_old_fd, value v_old_path, value v
|
|||||||
CAMLreturn(Val_unit);
|
CAMLreturn(Val_unit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CAMLprim value caml_eio_posix_symlinkat(value v_old_path, value v_new_fd, value v_new_path) {
|
||||||
|
CAMLparam2(v_old_path, v_new_path);
|
||||||
|
size_t old_path_len = caml_string_length(v_old_path);
|
||||||
|
size_t new_path_len = caml_string_length(v_new_path);
|
||||||
|
char *old_path;
|
||||||
|
char *new_path;
|
||||||
|
int ret;
|
||||||
|
caml_unix_check_path(v_old_path, "symlinkat-old");
|
||||||
|
caml_unix_check_path(v_new_path, "symlinkat-new");
|
||||||
|
old_path = caml_stat_alloc(old_path_len + new_path_len + 2);
|
||||||
|
new_path = old_path + old_path_len + 1;
|
||||||
|
memcpy(old_path, String_val(v_old_path), old_path_len + 1);
|
||||||
|
memcpy(new_path, String_val(v_new_path), new_path_len + 1);
|
||||||
|
caml_enter_blocking_section();
|
||||||
|
ret = symlinkat(old_path, Int_val(v_new_fd), new_path);
|
||||||
|
caml_leave_blocking_section();
|
||||||
|
caml_stat_free_preserving_errno(old_path);
|
||||||
|
if (ret == -1) uerror("symlinkat", v_old_path);
|
||||||
|
CAMLreturn(Val_unit);
|
||||||
|
}
|
||||||
|
|
||||||
CAMLprim value caml_eio_posix_spawn(value v_errors, value v_actions) {
|
CAMLprim value caml_eio_posix_spawn(value v_errors, value v_actions) {
|
||||||
CAMLparam1(v_actions);
|
CAMLparam1(v_actions);
|
||||||
pid_t child_pid;
|
pid_t child_pid;
|
||||||
|
@ -94,6 +94,9 @@ end = struct
|
|||||||
| None -> invalid_arg "Target is not an eio_posix directory!"
|
| None -> invalid_arg "Target is not an eio_posix directory!"
|
||||||
| Some new_dir -> Err.run (Low_level.rename t.fd old_path new_dir) new_path
|
| Some new_dir -> Err.run (Low_level.rename t.fd old_path new_dir) new_path
|
||||||
|
|
||||||
|
let symlink ~link_to t path =
|
||||||
|
Err.run (Low_level.symlink ~link_to t.fd) path
|
||||||
|
|
||||||
let open_dir t ~sw path =
|
let open_dir t ~sw path =
|
||||||
let flags = Low_level.Open_flags.(rdonly + directory +? path) in
|
let flags = Low_level.Open_flags.(rdonly + directory +? path) in
|
||||||
let fd = Err.run (Low_level.openat ~sw ~mode:0 t.fd path) flags in
|
let fd = Err.run (Low_level.openat ~sw ~mode:0 t.fd path) flags in
|
||||||
|
@ -8,7 +8,7 @@ let optional_flags = [
|
|||||||
|
|
||||||
let () =
|
let () =
|
||||||
C.main ~name:"discover" (fun c ->
|
C.main ~name:"discover" (fun c ->
|
||||||
let c_flags = ["-D_LARGEFILE64_SOURCE"; "-D_XOPEN_SOURCE=700"; "-D_DARWIN_C_SOURCE"; "-D_GNU_SOURCE"] in
|
let c_flags = ["-D_LARGEFILE64_SOURCE"; "-D_XOPEN_SOURCE=700"; "-D_DARWIN_C_SOURCE"; "-D_GNU_SOURCE"; "-D_BSD_SOURCE"] in
|
||||||
let includes = ["sys/types.h"; "sys/stat.h"; "fcntl.h"] in
|
let includes = ["sys/types.h"; "sys/stat.h"; "fcntl.h"] in
|
||||||
let extra_flags, missing_defs =
|
let extra_flags, missing_defs =
|
||||||
C.C_define.import c ~c_flags ~includes
|
C.C_define.import c ~c_flags ~includes
|
||||||
|
@ -263,7 +263,8 @@ module Resolve = struct
|
|||||||
| new_base ->
|
| new_base ->
|
||||||
state.dir_stack <- Tmp (new_base, state.dir_stack);
|
state.dir_stack <- Tmp (new_base, state.dir_stack);
|
||||||
resolve state xs
|
resolve state xs
|
||||||
| exception (Unix.Unix_error ((ENOTDIR | EMLINK | EUNKNOWNERR _), _, _) as e) ->
|
| exception (Unix.Unix_error ((ELOOP | ENOTDIR | EMLINK | EUNKNOWNERR _), _, _) as e) ->
|
||||||
|
(* Note: Linux uses ELOOP or ENOTDIR. FreeBSD uses EMLINK. NetBSD uses EFTYPE. *)
|
||||||
match Eio_unix.Private.read_link_unix base x with
|
match Eio_unix.Private.read_link_unix base x with
|
||||||
| target ->
|
| target ->
|
||||||
decr_max_follows state x;
|
decr_max_follows state x;
|
||||||
@ -414,6 +415,14 @@ let rename old_dir old_path new_dir new_path =
|
|||||||
let new_dir = Option.value new_dir ~default:at_fdcwd in
|
let new_dir = Option.value new_dir ~default:at_fdcwd in
|
||||||
eio_renameat old_dir old_path new_dir new_path
|
eio_renameat old_dir old_path new_dir new_path
|
||||||
|
|
||||||
|
external eio_symlinkat : string -> Unix.file_descr -> string -> unit = "caml_eio_posix_symlinkat"
|
||||||
|
|
||||||
|
let symlink ~link_to new_dir new_path =
|
||||||
|
in_worker_thread "symlink" @@ fun () ->
|
||||||
|
Resolve.with_parent "symlink-new" new_dir new_path @@ fun new_dir new_path ->
|
||||||
|
let new_dir = Option.value new_dir ~default:at_fdcwd in
|
||||||
|
eio_symlinkat link_to new_dir new_path
|
||||||
|
|
||||||
let read_link dirfd path =
|
let read_link dirfd path =
|
||||||
in_worker_thread "read_link" @@ fun () ->
|
in_worker_thread "read_link" @@ fun () ->
|
||||||
Resolve.with_parent "read_link" dirfd path @@ fun dirfd path ->
|
Resolve.with_parent "read_link" dirfd path @@ fun dirfd path ->
|
||||||
@ -549,6 +558,7 @@ module Process = struct
|
|||||||
let t =
|
let t =
|
||||||
let pid =
|
let pid =
|
||||||
Fd.use_exn "errors-w" errors_w @@ fun errors_w ->
|
Fd.use_exn "errors-w" errors_w @@ fun errors_w ->
|
||||||
|
Eio.Private.Trace.with_span "spawn" @@ fun () ->
|
||||||
eio_spawn errors_w c_actions
|
eio_spawn errors_w c_actions
|
||||||
in
|
in
|
||||||
Fd.close errors_w;
|
Fd.close errors_w;
|
||||||
|
@ -78,6 +78,10 @@ val mkdir : mode:int -> dir_fd -> string -> unit
|
|||||||
val unlink : dir:bool -> dir_fd -> string -> unit
|
val unlink : dir:bool -> dir_fd -> string -> unit
|
||||||
val rename : dir_fd -> string -> dir_fd -> string -> unit
|
val rename : dir_fd -> string -> dir_fd -> string -> unit
|
||||||
|
|
||||||
|
val symlink : link_to:string -> dir_fd -> string -> unit
|
||||||
|
(** [symlink ~link_to dir path] will create a new symlink at [dir / path]
|
||||||
|
linking to [link_to]. *)
|
||||||
|
|
||||||
val readdir : dir_fd -> string -> string array
|
val readdir : dir_fd -> string -> string array
|
||||||
|
|
||||||
val readv : fd -> Cstruct.t array -> int
|
val readv : fd -> Cstruct.t array -> int
|
||||||
|
@ -14,6 +14,7 @@ CAMLprim value caml_eio_posix_fdopendir(value);
|
|||||||
CAMLprim value caml_eio_posix_mkdirat(value, value, value);
|
CAMLprim value caml_eio_posix_mkdirat(value, value, value);
|
||||||
CAMLprim value caml_eio_posix_unlinkat(value, value, value);
|
CAMLprim value caml_eio_posix_unlinkat(value, value, value);
|
||||||
CAMLprim value caml_eio_posix_renameat(value, value, value, value);
|
CAMLprim value caml_eio_posix_renameat(value, value, value, value);
|
||||||
|
CAMLprim value caml_eio_posix_symlinkat(value, value, value);
|
||||||
CAMLprim value caml_eio_posix_make_stat(value);
|
CAMLprim value caml_eio_posix_make_stat(value);
|
||||||
CAMLprim value caml_eio_posix_fstatat(value, value, value, value);
|
CAMLprim value caml_eio_posix_fstatat(value, value, value, value);
|
||||||
CAMLprim value caml_eio_posix_fstat(value, value);
|
CAMLprim value caml_eio_posix_fstat(value, value);
|
||||||
|
@ -379,7 +379,7 @@ let run ~extra_effects t main x =
|
|||||||
let `Exit_scheduler =
|
let `Exit_scheduler =
|
||||||
let new_fiber = Fiber_context.make_root () in
|
let new_fiber = Fiber_context.make_root () in
|
||||||
Domain_local_await.using
|
Domain_local_await.using
|
||||||
~prepare_for_await:Eio.Private.Dla.prepare_for_await
|
~prepare_for_await:Eio_utils.Dla.prepare_for_await
|
||||||
~while_running:(fun () ->
|
~while_running:(fun () ->
|
||||||
fork ~new_fiber (fun () ->
|
fork ~new_fiber (fun () ->
|
||||||
Eio_unix.Private.Thread_pool.run t.thread_pool @@ fun () ->
|
Eio_unix.Private.Thread_pool.run t.thread_pool @@ fun () ->
|
||||||
|
@ -3,8 +3,8 @@
|
|||||||
(enabled_if (= %{os_type} "Unix"))
|
(enabled_if (= %{os_type} "Unix"))
|
||||||
(deps (package eio_posix)))
|
(deps (package eio_posix)))
|
||||||
|
|
||||||
(test
|
(tests
|
||||||
(name open_beneath)
|
(names open_beneath test_await)
|
||||||
(package eio_posix)
|
(package eio_posix)
|
||||||
(build_if (= %{os_type} "Unix"))
|
(build_if (= %{os_type} "Unix"))
|
||||||
(libraries eio_posix))
|
(libraries eio_posix))
|
||||||
|
25
lib_eio_posix/test/test_await.ml
Normal file
25
lib_eio_posix/test/test_await.ml
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
open Eio.Std
|
||||||
|
|
||||||
|
let () =
|
||||||
|
Eio_posix.run @@ fun _ ->
|
||||||
|
let a, b = Unix.(socketpair PF_UNIX SOCK_STREAM 0) in
|
||||||
|
(* Start awaiting readable/writable state, but cancel immediately. *)
|
||||||
|
try
|
||||||
|
Eio.Cancel.sub (fun cc ->
|
||||||
|
Fiber.all [
|
||||||
|
(fun () -> Eio_unix.await_readable a);
|
||||||
|
(fun () -> Eio_unix.await_writable b);
|
||||||
|
(fun () -> Eio.Cancel.cancel cc Exit);
|
||||||
|
];
|
||||||
|
assert false
|
||||||
|
)
|
||||||
|
with Eio.Cancel.Cancelled _ ->
|
||||||
|
(* Now wait for something else. Will fail if the old FDs are still being waited on. *)
|
||||||
|
let c, d = Unix.(socketpair PF_UNIX SOCK_STREAM 0) in
|
||||||
|
Unix.close a;
|
||||||
|
Unix.close b;
|
||||||
|
Fiber.first
|
||||||
|
(fun () -> Eio_unix.await_readable c)
|
||||||
|
(fun () -> Eio_unix.await_writable d);
|
||||||
|
Unix.close c;
|
||||||
|
Unix.close d
|
@ -48,6 +48,11 @@ let run_event_loop fn x =
|
|||||||
(try Unix.set_nonblock unix_fd with Unix.Unix_error (Unix.ENOTSOCK, _, _) -> ());
|
(try Unix.set_nonblock unix_fd with Unix.Unix_error (Unix.ENOTSOCK, _, _) -> ());
|
||||||
continue k (Flow.of_fd fd :> _ Eio_unix.Net.stream_socket)
|
continue k (Flow.of_fd fd :> _ Eio_unix.Net.stream_socket)
|
||||||
)
|
)
|
||||||
|
| Eio_unix.Net.Import_socket_listening (sw, close_unix, unix_fd) -> Some (fun k ->
|
||||||
|
let fd = Fd.of_unix ~sw ~blocking:false ~close_unix unix_fd in
|
||||||
|
Unix.set_nonblock unix_fd;
|
||||||
|
continue k (Net.listening_socket ~hook:Switch.null_hook fd)
|
||||||
|
)
|
||||||
| Eio_unix.Net.Import_socket_datagram (sw, close_unix, unix_fd) -> Some (fun k ->
|
| Eio_unix.Net.Import_socket_datagram (sw, close_unix, unix_fd) -> Some (fun k ->
|
||||||
let fd = Fd.of_unix ~sw ~blocking:false ~close_unix unix_fd in
|
let fd = Fd.of_unix ~sw ~blocking:false ~close_unix unix_fd in
|
||||||
Unix.set_nonblock unix_fd;
|
Unix.set_nonblock unix_fd;
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
(foreign_stubs
|
(foreign_stubs
|
||||||
(language c)
|
(language c)
|
||||||
(include_dirs ../lib_eio/unix/include)
|
(include_dirs ../lib_eio/unix/include)
|
||||||
(names eio_windows_stubs eio_windows_cstruct_stubs))
|
(names eio_windows_stubs))
|
||||||
(c_library_flags :standard -lbcrypt -lntdll)
|
(c_library_flags :standard -lbcrypt -lntdll)
|
||||||
(libraries eio eio.unix eio.utils fmt))
|
(libraries eio eio.unix eio.utils fmt))
|
||||||
|
|
||||||
|
@ -1,149 +0,0 @@
|
|||||||
/* From mirage/ocaml-cstruct
|
|
||||||
Copyright (c) 2012 Anil Madhavapeddy <anil@recoil.org>
|
|
||||||
Copyright (c) 2012 Pierre Chambart
|
|
||||||
Copyright (c) Christiano F. Haesbaert <haesbaert@haesbaert.org>
|
|
||||||
Copyright (c) Citrix Inc
|
|
||||||
Copyright (c) David Sheets <sheets@alum.mit.edu>
|
|
||||||
Copyright (c) Drup <drupyog@zoho.com>
|
|
||||||
Copyright (c) Hannes Mehnert <hannes@mehnert.org>
|
|
||||||
Copyright (c) Jeremy Yallop <yallop@gmail.com>
|
|
||||||
Copyright (c) Mindy Preston <meetup@yomimono.org>
|
|
||||||
Copyright (c) Nicolas Ojeda Bar <n.oje.bar@gmail.com>
|
|
||||||
Copyright (c) Richard Mortier <mort@cantab.net>
|
|
||||||
Copyright (c) Rudi Grinberg <rudi.grinberg@gmail.com>
|
|
||||||
Copyright (c) Thomas Gazagnaire <thomas@gazagnaire.com>
|
|
||||||
Copyright (c) Thomas Leonard <talex5@gmail.com>
|
|
||||||
Copyright (c) Vincent Bernardoff <vb@luminar.eu.org>
|
|
||||||
Copyright (c) pqwy <david@numm.org>
|
|
||||||
|
|
||||||
Permission to use, copy, modify, and distribute this software for any
|
|
||||||
purpose with or without fee is hereby granted, provided that the above
|
|
||||||
copyright notice and this permission notice appear in all copies.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
||||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
||||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
||||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
||||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
||||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
|
|
||||||
#include <caml/mlvalues.h>
|
|
||||||
#include <caml/memory.h>
|
|
||||||
#include <caml/custom.h>
|
|
||||||
#include <caml/callback.h>
|
|
||||||
#include <caml/alloc.h>
|
|
||||||
#include <caml/unixsupport.h>
|
|
||||||
#include <caml/bigarray.h>
|
|
||||||
#include <caml/threads.h>
|
|
||||||
#include <caml/fail.h>
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <errno.h>
|
|
||||||
|
|
||||||
CAMLprim value eio_windows_cstruct_read(value val_fd, value val_c)
|
|
||||||
{
|
|
||||||
CAMLparam2(val_fd, val_c);
|
|
||||||
CAMLlocal3(val_buf, val_ofs, val_len);
|
|
||||||
uint8_t *buf;
|
|
||||||
size_t len;
|
|
||||||
ssize_t n = 0;
|
|
||||||
int win32err = 0;
|
|
||||||
SOCKET s;
|
|
||||||
HANDLE h;
|
|
||||||
DWORD numread;
|
|
||||||
int ok;
|
|
||||||
|
|
||||||
val_buf = Field(val_c, 0);
|
|
||||||
val_ofs = Field(val_c, 1);
|
|
||||||
val_len = Field(val_c, 2);
|
|
||||||
|
|
||||||
buf = (uint8_t *)Caml_ba_data_val(val_buf) + Long_val(val_ofs);
|
|
||||||
len = (size_t)Long_val(val_len);
|
|
||||||
|
|
||||||
switch (Descr_kind_val(val_fd))
|
|
||||||
{
|
|
||||||
case KIND_SOCKET:
|
|
||||||
s = Socket_val(val_fd);
|
|
||||||
|
|
||||||
caml_release_runtime_system();
|
|
||||||
n = recv(s, buf, len, 0);
|
|
||||||
win32err = WSAGetLastError();
|
|
||||||
caml_acquire_runtime_system();
|
|
||||||
|
|
||||||
if (n == SOCKET_ERROR)
|
|
||||||
{
|
|
||||||
win32_maperr(win32err);
|
|
||||||
uerror("stub_cstruct_read", Nothing);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case KIND_HANDLE:
|
|
||||||
h = Handle_val(val_fd);
|
|
||||||
caml_release_runtime_system();
|
|
||||||
ok = ReadFile(h, buf, len, &numread, NULL);
|
|
||||||
win32err = GetLastError();
|
|
||||||
n = numread;
|
|
||||||
caml_acquire_runtime_system();
|
|
||||||
|
|
||||||
if (!ok)
|
|
||||||
{
|
|
||||||
win32_maperr(win32err);
|
|
||||||
uerror("stub_cstruct_read", Nothing);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
caml_failwith("unknown Descr_kind_val");
|
|
||||||
}
|
|
||||||
|
|
||||||
CAMLreturn(Val_int(n));
|
|
||||||
}
|
|
||||||
|
|
||||||
CAMLprim value eio_windows_cstruct_write(value val_fd, value val_c)
|
|
||||||
{
|
|
||||||
CAMLparam2(val_fd, val_c);
|
|
||||||
CAMLlocal3(val_buf, val_ofs, val_len);
|
|
||||||
val_buf = Field(val_c, 0);
|
|
||||||
val_ofs = Field(val_c, 1);
|
|
||||||
val_len = Field(val_c, 2);
|
|
||||||
void *buf = (char *)Caml_ba_data_val(val_buf) + Long_val(val_ofs);
|
|
||||||
size_t len = Long_val(val_len);
|
|
||||||
ssize_t n = 0;
|
|
||||||
|
|
||||||
int win32err = 0;
|
|
||||||
switch (Descr_kind_val(val_fd))
|
|
||||||
{
|
|
||||||
case KIND_SOCKET:
|
|
||||||
SOCKET s = Socket_val(val_fd);
|
|
||||||
|
|
||||||
caml_release_runtime_system();
|
|
||||||
n = send(s, buf, len, 0);
|
|
||||||
win32err = WSAGetLastError();
|
|
||||||
caml_acquire_runtime_system();
|
|
||||||
|
|
||||||
if (n == SOCKET_ERROR)
|
|
||||||
{
|
|
||||||
win32_maperr(win32err);
|
|
||||||
unix_error(errno, "stub_cstruct_write", Nothing);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case KIND_HANDLE:
|
|
||||||
HANDLE h = Handle_val(val_fd);
|
|
||||||
DWORD numwritten;
|
|
||||||
caml_release_runtime_system();
|
|
||||||
int ok = WriteFile(h, buf, len, &numwritten, NULL);
|
|
||||||
win32err = GetLastError();
|
|
||||||
|
|
||||||
n = numwritten;
|
|
||||||
caml_acquire_runtime_system();
|
|
||||||
|
|
||||||
if (!ok)
|
|
||||||
{
|
|
||||||
win32_maperr(win32err);
|
|
||||||
uerror("stub_cstruct_write", Nothing);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
caml_failwith("unknown Descr_kind_val");
|
|
||||||
}
|
|
||||||
|
|
||||||
CAMLreturn(Val_int(n));
|
|
||||||
}
|
|
@ -237,7 +237,7 @@ CAMLprim value caml_eio_windows_unlinkat(value v_dirfd, value v_pathname, value
|
|||||||
|
|
||||||
if (!NT_SUCCESS(r)) {
|
if (!NT_SUCCESS(r)) {
|
||||||
caml_win32_maperr(RtlNtStatusToDosError(r));
|
caml_win32_maperr(RtlNtStatusToDosError(r));
|
||||||
uerror("openat", Nothing);
|
uerror("openat", v_pathname);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now close the file to delete it
|
// Now close the file to delete it
|
||||||
@ -252,6 +252,11 @@ CAMLprim value caml_eio_windows_renameat(value v_old_fd, value v_old_path, value
|
|||||||
uerror("renameat is not supported on windows yet", Nothing);
|
uerror("renameat is not supported on windows yet", Nothing);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CAMLprim value caml_eio_windows_symlinkat(value v_old_path, value v_new_fd, value v_new_path)
|
||||||
|
{
|
||||||
|
uerror("symlinkat is not supported on windows yet", Nothing);
|
||||||
|
}
|
||||||
|
|
||||||
CAMLprim value caml_eio_windows_spawn(value v_errors, value v_actions)
|
CAMLprim value caml_eio_windows_spawn(value v_errors, value v_actions)
|
||||||
{
|
{
|
||||||
uerror("processes are not supported on windows yet", Nothing);
|
uerror("processes are not supported on windows yet", Nothing);
|
||||||
|
@ -20,7 +20,7 @@ let wrap code name arg =
|
|||||||
| ENOENT -> Eio.Fs.err (Not_found e)
|
| ENOENT -> Eio.Fs.err (Not_found e)
|
||||||
| EXDEV | EACCES | EPERM -> Eio.Fs.err (Permission_denied e)
|
| EXDEV | EACCES | EPERM -> Eio.Fs.err (Permission_denied e)
|
||||||
| ECONNREFUSED -> Eio.Net.err (Connection_failure (Refused e))
|
| ECONNREFUSED -> Eio.Net.err (Connection_failure (Refused e))
|
||||||
| ECONNRESET | EPIPE -> Eio.Net.err (Connection_reset e)
|
| ECONNRESET | EPIPE | ECONNABORTED -> Eio.Net.err (Connection_reset e)
|
||||||
| _ -> unclassified_error e
|
| _ -> unclassified_error e
|
||||||
|
|
||||||
let run fn x =
|
let run fn x =
|
||||||
|
@ -82,7 +82,7 @@ end = struct
|
|||||||
let dir = resolve t dir in
|
let dir = resolve t dir in
|
||||||
Switch.run @@ fun sw ->
|
Switch.run @@ fun sw ->
|
||||||
let open Low_level in
|
let open Low_level in
|
||||||
let dirfd = Low_level.openat ~sw ~nofollow:true dir Flags.Open.(generic_read + synchronise) Flags.Disposition.(open_if) Flags.Create.(directory) in
|
let dirfd = Err.run (Low_level.openat ~sw ~nofollow:true dir Flags.Open.(generic_read + synchronise) Flags.Disposition.(open_if)) Flags.Create.(directory) in
|
||||||
fn (Some dirfd) leaf
|
fn (Some dirfd) leaf
|
||||||
)
|
)
|
||||||
) else fn None path
|
) else fn None path
|
||||||
@ -172,6 +172,10 @@ end = struct
|
|||||||
with_parent_dir new_dir new_path @@ fun new_dir new_path ->
|
with_parent_dir new_dir new_path @@ fun new_dir new_path ->
|
||||||
Err.run (Low_level.rename ?old_dir old_path ?new_dir) new_path
|
Err.run (Low_level.rename ?old_dir old_path ?new_dir) new_path
|
||||||
|
|
||||||
|
let symlink ~link_to t path =
|
||||||
|
with_parent_dir t path @@ fun dirfd path ->
|
||||||
|
Err.run (Low_level.symlink ~link_to dirfd) path
|
||||||
|
|
||||||
let close t = t.closed <- true
|
let close t = t.closed <- true
|
||||||
|
|
||||||
let open_dir t ~sw path =
|
let open_dir t ~sw path =
|
||||||
|
@ -39,17 +39,25 @@ let rec do_nonblocking ty fn fd =
|
|||||||
do_nonblocking ty fn fd
|
do_nonblocking ty fn fd
|
||||||
|
|
||||||
let read fd buf start len =
|
let read fd buf start len =
|
||||||
|
await_readable fd;
|
||||||
Fd.use_exn "read" fd @@ fun fd ->
|
Fd.use_exn "read" fd @@ fun fd ->
|
||||||
do_nonblocking Read (fun fd -> Unix.read fd buf start len) fd
|
do_nonblocking Read (fun fd -> Unix.read fd buf start len) fd
|
||||||
|
|
||||||
let read_cstruct fd buf =
|
let read_cstruct fd (buf:Cstruct.t) =
|
||||||
|
await_readable fd;
|
||||||
Fd.use_exn "read_cstruct" fd @@ fun fd ->
|
Fd.use_exn "read_cstruct" fd @@ fun fd ->
|
||||||
do_nonblocking Read (fun fd -> Unix_cstruct.read fd buf) fd
|
do_nonblocking Read (fun fd -> Unix.read_bigarray fd buf.buffer buf.off buf.len) fd
|
||||||
|
|
||||||
let write fd buf start len =
|
let write fd buf start len =
|
||||||
|
await_writable fd;
|
||||||
Fd.use_exn "write" fd @@ fun fd ->
|
Fd.use_exn "write" fd @@ fun fd ->
|
||||||
do_nonblocking Write (fun fd -> Unix.write fd buf start len) fd
|
do_nonblocking Write (fun fd -> Unix.write fd buf start len) fd
|
||||||
|
|
||||||
|
let write_cstruct fd (buf:Cstruct.t) =
|
||||||
|
await_writable fd;
|
||||||
|
Fd.use_exn "write_cstruct" fd @@ fun fd ->
|
||||||
|
do_nonblocking Write (fun fd -> Unix.write_bigarray fd buf.buffer buf.off buf.len) fd
|
||||||
|
|
||||||
let sleep_until time =
|
let sleep_until time =
|
||||||
Sched.enter @@ fun t k ->
|
Sched.enter @@ fun t k ->
|
||||||
Sched.await_timeout t k time
|
Sched.await_timeout t k time
|
||||||
@ -148,8 +156,11 @@ let readv fd bufs =
|
|||||||
do_nonblocking Read (fun fd -> eio_readv fd bufs) fd
|
do_nonblocking Read (fun fd -> eio_readv fd bufs) fd
|
||||||
|
|
||||||
let writev fd bufs =
|
let writev fd bufs =
|
||||||
Fd.use_exn "writev" fd @@ fun fd ->
|
let rec loop buf = if Cstruct.length buf > 0 then begin
|
||||||
do_nonblocking Write (fun fd -> Unix_cstruct.writev fd bufs) fd
|
let n = write_cstruct fd buf in
|
||||||
|
loop @@ Cstruct.shift buf n
|
||||||
|
end in
|
||||||
|
List.iter loop bufs
|
||||||
|
|
||||||
let preadv ~file_offset fd bufs =
|
let preadv ~file_offset fd bufs =
|
||||||
Fd.use_exn "preadv" fd @@ fun fd ->
|
Fd.use_exn "preadv" fd @@ fun fd ->
|
||||||
@ -234,6 +245,14 @@ let rename ?old_dir old_path ?new_dir new_path =
|
|||||||
in_worker_thread @@ fun () ->
|
in_worker_thread @@ fun () ->
|
||||||
eio_renameat old_dir old_path new_dir new_path
|
eio_renameat old_dir old_path new_dir new_path
|
||||||
|
|
||||||
|
|
||||||
|
external eio_symlinkat : string -> Unix.file_descr option -> string -> unit = "caml_eio_windows_symlinkat"
|
||||||
|
|
||||||
|
let symlink ~link_to new_dir new_path =
|
||||||
|
with_dirfd "symlink-new" new_dir @@ fun new_dir ->
|
||||||
|
in_worker_thread @@ fun () ->
|
||||||
|
eio_symlinkat link_to new_dir new_path
|
||||||
|
|
||||||
let lseek fd off cmd =
|
let lseek fd off cmd =
|
||||||
Fd.use_exn "lseek" fd @@ fun fd ->
|
Fd.use_exn "lseek" fd @@ fun fd ->
|
||||||
let cmd =
|
let cmd =
|
||||||
|
@ -22,6 +22,7 @@ val sleep_until : Mtime.t -> unit
|
|||||||
val read : fd -> bytes -> int -> int -> int
|
val read : fd -> bytes -> int -> int -> int
|
||||||
val read_cstruct : fd -> Cstruct.t -> int
|
val read_cstruct : fd -> Cstruct.t -> int
|
||||||
val write : fd -> bytes -> int -> int -> int
|
val write : fd -> bytes -> int -> int -> int
|
||||||
|
val write_cstruct : fd -> Cstruct.t -> int
|
||||||
|
|
||||||
val socket : sw:Switch.t -> Unix.socket_domain -> Unix.socket_type -> int -> fd
|
val socket : sw:Switch.t -> Unix.socket_domain -> Unix.socket_type -> int -> fd
|
||||||
val connect : fd -> Unix.sockaddr -> unit
|
val connect : fd -> Unix.sockaddr -> unit
|
||||||
@ -48,6 +49,10 @@ val mkdir : ?dirfd:fd -> ?nofollow:bool -> mode:int -> string -> unit
|
|||||||
val unlink : ?dirfd:fd -> dir:bool -> string -> unit
|
val unlink : ?dirfd:fd -> dir:bool -> string -> unit
|
||||||
val rename : ?old_dir:fd -> string -> ?new_dir:fd -> string -> unit
|
val rename : ?old_dir:fd -> string -> ?new_dir:fd -> string -> unit
|
||||||
|
|
||||||
|
val symlink : link_to:string -> fd option -> string -> unit
|
||||||
|
(** [symlink ~link_to dir path] will create a new symlink at [dir / path]
|
||||||
|
linking to [link_to]. *)
|
||||||
|
|
||||||
val readdir : string -> string array
|
val readdir : string -> string array
|
||||||
|
|
||||||
val readv : fd -> Cstruct.t array -> int
|
val readv : fd -> Cstruct.t array -> int
|
||||||
|
@ -83,24 +83,23 @@ let datagram_handler = Eio_unix.Pi.datagram_handler (module Datagram_socket)
|
|||||||
let datagram_socket fd =
|
let datagram_socket fd =
|
||||||
Eio.Resource.T (fd, datagram_handler)
|
Eio.Resource.T (fd, datagram_handler)
|
||||||
|
|
||||||
(* https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml *)
|
|
||||||
let getaddrinfo ~service node =
|
let getaddrinfo ~service node =
|
||||||
let to_eio_sockaddr_t {Unix.ai_family; ai_addr; ai_socktype; ai_protocol; _ } =
|
(* OCaml's [Unix.getaddrinfo] on Windows doesn't set [ai_protocol] to
|
||||||
match ai_family, ai_socktype, ai_addr with
|
anything useful, so you can't tell which addresses are TCP and which are
|
||||||
| (Unix.PF_INET | PF_INET6),
|
UDP. So, do two separate queries. *)
|
||||||
(Unix.SOCK_STREAM | SOCK_DGRAM),
|
let get ty k =
|
||||||
Unix.ADDR_INET (inet_addr,port) -> (
|
Unix.getaddrinfo node service [AI_SOCKTYPE ty]
|
||||||
match ai_protocol with
|
|> List.filter_map (function
|
||||||
| 6 -> Some (`Tcp (Eio_unix.Net.Ipaddr.of_unix inet_addr, port))
|
| {Unix.ai_addr = ADDR_INET (host, port); _} ->
|
||||||
| 17 -> Some (`Udp (Eio_unix.Net.Ipaddr.of_unix inet_addr, port))
|
Some (k (Eio_unix.Net.Ipaddr.of_unix host, port))
|
||||||
| _ -> None)
|
| _ -> None
|
||||||
| _ -> None
|
)
|
||||||
in
|
in
|
||||||
Err.run (Eio_unix.run_in_systhread ~label:"getaddrinfo") @@ fun () ->
|
Err.run (Eio_unix.run_in_systhread ~label:"getaddrinfo") @@ fun () ->
|
||||||
let rec aux () =
|
let rec aux () =
|
||||||
try
|
try
|
||||||
Unix.getaddrinfo node service []
|
get SOCK_STREAM (fun x -> `Tcp x) @
|
||||||
|> List.filter_map to_eio_sockaddr_t
|
get SOCK_DGRAM (fun x -> `Udp x)
|
||||||
with Unix.Unix_error (EINTR, _, _) -> aux ()
|
with Unix.Unix_error (EINTR, _, _) -> aux ()
|
||||||
in
|
in
|
||||||
aux ()
|
aux ()
|
||||||
|
@ -271,6 +271,8 @@ let await_readable t (k : unit Suspended.t) fd =
|
|||||||
if was_empty then update t waiters fd;
|
if was_empty then update t waiters fd;
|
||||||
Fiber_context.set_cancel_fn k.fiber (fun ex ->
|
Fiber_context.set_cancel_fn k.fiber (fun ex ->
|
||||||
Lwt_dllist.remove node;
|
Lwt_dllist.remove node;
|
||||||
|
if Lwt_dllist.is_empty waiters.read then
|
||||||
|
update t waiters fd;
|
||||||
t.active_ops <- t.active_ops - 1;
|
t.active_ops <- t.active_ops - 1;
|
||||||
enqueue_failed_thread t k ex
|
enqueue_failed_thread t k ex
|
||||||
);
|
);
|
||||||
@ -287,6 +289,8 @@ let await_writable t (k : unit Suspended.t) fd =
|
|||||||
if was_empty then update t waiters fd;
|
if was_empty then update t waiters fd;
|
||||||
Fiber_context.set_cancel_fn k.fiber (fun ex ->
|
Fiber_context.set_cancel_fn k.fiber (fun ex ->
|
||||||
Lwt_dllist.remove node;
|
Lwt_dllist.remove node;
|
||||||
|
if Lwt_dllist.is_empty waiters.write then
|
||||||
|
update t waiters fd;
|
||||||
t.active_ops <- t.active_ops - 1;
|
t.active_ops <- t.active_ops - 1;
|
||||||
enqueue_failed_thread t k ex
|
enqueue_failed_thread t k ex
|
||||||
);
|
);
|
||||||
@ -370,7 +374,7 @@ let run ~extra_effects t main x =
|
|||||||
let `Exit_scheduler =
|
let `Exit_scheduler =
|
||||||
let new_fiber = Fiber_context.make_root () in
|
let new_fiber = Fiber_context.make_root () in
|
||||||
Domain_local_await.using
|
Domain_local_await.using
|
||||||
~prepare_for_await:Eio.Private.Dla.prepare_for_await
|
~prepare_for_await:Eio_utils.Dla.prepare_for_await
|
||||||
~while_running:(fun () ->
|
~while_running:(fun () ->
|
||||||
fork ~new_fiber (fun () ->
|
fork ~new_fiber (fun () ->
|
||||||
Eio_unix.Private.Thread_pool.run t.thread_pool @@ fun () ->
|
Eio_unix.Private.Thread_pool.run t.thread_pool @@ fun () ->
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
open Eio.Std
|
||||||
|
|
||||||
module Timeout = struct
|
module Timeout = struct
|
||||||
let test clock () =
|
let test clock () =
|
||||||
let t0 = Unix.gettimeofday () in
|
let t0 = Unix.gettimeofday () in
|
||||||
@ -48,6 +50,35 @@ module Dla = struct
|
|||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
module Await_fd = struct
|
||||||
|
let test_cancel () =
|
||||||
|
let a, b = Unix.(socketpair PF_UNIX SOCK_STREAM 0) in
|
||||||
|
(* Start awaiting readable/writable state, but cancel immediately. *)
|
||||||
|
try
|
||||||
|
Eio.Cancel.sub (fun cc ->
|
||||||
|
Fiber.all [
|
||||||
|
(fun () -> Eio_unix.await_readable a);
|
||||||
|
(fun () -> Eio_unix.await_writable b);
|
||||||
|
(fun () -> Eio.Cancel.cancel cc Exit);
|
||||||
|
];
|
||||||
|
assert false
|
||||||
|
)
|
||||||
|
with Eio.Cancel.Cancelled _ ->
|
||||||
|
(* Now wait for something else. Will fail if the old FDs are still being waited on. *)
|
||||||
|
let c, d = Unix.(socketpair PF_UNIX SOCK_STREAM 0) in
|
||||||
|
Unix.close a;
|
||||||
|
Unix.close b;
|
||||||
|
Fiber.first
|
||||||
|
(fun () -> Eio_unix.await_readable c)
|
||||||
|
(fun () -> Eio_unix.await_writable d);
|
||||||
|
Unix.close c;
|
||||||
|
Unix.close d
|
||||||
|
|
||||||
|
let tests = [
|
||||||
|
"cancel", `Quick, test_cancel;
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
let () =
|
let () =
|
||||||
Eio_windows.run @@ fun env ->
|
Eio_windows.run @@ fun env ->
|
||||||
@ -56,5 +87,6 @@ let () =
|
|||||||
"fs", Test_fs.tests env;
|
"fs", Test_fs.tests env;
|
||||||
"timeout", Timeout.tests env;
|
"timeout", Timeout.tests env;
|
||||||
"random", Random.tests env;
|
"random", Random.tests env;
|
||||||
"dla", Dla.tests
|
"dla", Dla.tests;
|
||||||
|
"await", Await_fd.tests;
|
||||||
]
|
]
|
@ -158,6 +158,9 @@ let test_symlink env () =
|
|||||||
Unix.mkdir "another" 0o700;
|
Unix.mkdir "another" 0o700;
|
||||||
print_endline @@ Unix.realpath "to-subdir" |}
|
print_endline @@ Unix.realpath "to-subdir" |}
|
||||||
*)
|
*)
|
||||||
|
if not (Unix.has_symlink ()) then
|
||||||
|
Printf.printf "Skipping test_symlink on systems that don't support symlinks.\n"
|
||||||
|
else
|
||||||
let cwd = Eio.Stdenv.cwd env in
|
let cwd = Eio.Stdenv.cwd env in
|
||||||
try_mkdir (cwd / "sandbox");
|
try_mkdir (cwd / "sandbox");
|
||||||
Unix.symlink ~to_dir:true ".." "sandbox\\to-root";
|
Unix.symlink ~to_dir:true ".." "sandbox\\to-root";
|
||||||
@ -277,4 +280,5 @@ let tests env = [
|
|||||||
"unlink", `Quick, test_unlink env;
|
"unlink", `Quick, test_unlink env;
|
||||||
"failing-unlink", `Quick, try_failing_unlink env;
|
"failing-unlink", `Quick, try_failing_unlink env;
|
||||||
"rmdir", `Quick, test_remove_dir env;
|
"rmdir", `Quick, test_remove_dir env;
|
||||||
|
"mkdirs", `Quick, test_mkdirs env;
|
||||||
]
|
]
|
94
tests/fs.md
94
tests/fs.md
@ -89,11 +89,17 @@ let try_stat path =
|
|||||||
traceln "%a -> %s" Eio.Path.pp path a
|
traceln "%a -> %s" Eio.Path.pp path a
|
||||||
else
|
else
|
||||||
traceln "%a -> %s / %s" Eio.Path.pp path a b
|
traceln "%a -> %s / %s" Eio.Path.pp path a b
|
||||||
|
|
||||||
|
let try_symlink ~link_to path =
|
||||||
|
match Path.symlink ~link_to path with
|
||||||
|
| s -> traceln "symlink %a -> %S" Path.pp path link_to
|
||||||
|
| exception ex -> traceln "@[<h>%a@]" Eio.Exn.pp ex
|
||||||
```
|
```
|
||||||
|
|
||||||
# Basic test cases
|
# Basic test cases
|
||||||
|
|
||||||
Creating a file and reading it back:
|
Creating a file and reading it back:
|
||||||
|
|
||||||
```ocaml
|
```ocaml
|
||||||
# run ~clear:["test-file"] @@ fun env ->
|
# run ~clear:["test-file"] @@ fun env ->
|
||||||
let cwd = Eio.Stdenv.cwd env in
|
let cwd = Eio.Stdenv.cwd env in
|
||||||
@ -104,6 +110,7 @@ Creating a file and reading it back:
|
|||||||
```
|
```
|
||||||
|
|
||||||
Check the file got the correct permissions (subject to the umask set above):
|
Check the file got the correct permissions (subject to the umask set above):
|
||||||
|
|
||||||
```ocaml
|
```ocaml
|
||||||
# Printf.printf "Perm = %o\n" ((Unix.stat "test-file").st_perm);;
|
# Printf.printf "Perm = %o\n" ((Unix.stat "test-file").st_perm);;
|
||||||
Perm = 644
|
Perm = 644
|
||||||
@ -113,6 +120,7 @@ Perm = 644
|
|||||||
# Sandboxing
|
# Sandboxing
|
||||||
|
|
||||||
Trying to use cwd to access a file outside of that subtree fails:
|
Trying to use cwd to access a file outside of that subtree fails:
|
||||||
|
|
||||||
```ocaml
|
```ocaml
|
||||||
# run @@ fun env ->
|
# run @@ fun env ->
|
||||||
let cwd = Eio.Stdenv.cwd env in
|
let cwd = Eio.Stdenv.cwd env in
|
||||||
@ -123,6 +131,7 @@ Exception: Eio.Io Fs Permission_denied _,
|
|||||||
```
|
```
|
||||||
|
|
||||||
Trying to use cwd to access an absolute path fails:
|
Trying to use cwd to access an absolute path fails:
|
||||||
|
|
||||||
```ocaml
|
```ocaml
|
||||||
# run @@ fun env ->
|
# run @@ fun env ->
|
||||||
let cwd = Eio.Stdenv.cwd env in
|
let cwd = Eio.Stdenv.cwd env in
|
||||||
@ -135,6 +144,7 @@ Exception: Eio.Io Fs Permission_denied _,
|
|||||||
# Creation modes
|
# Creation modes
|
||||||
|
|
||||||
Exclusive create fails if already exists:
|
Exclusive create fails if already exists:
|
||||||
|
|
||||||
```ocaml
|
```ocaml
|
||||||
# run @@ fun env ->
|
# run @@ fun env ->
|
||||||
let cwd = Eio.Stdenv.cwd env in
|
let cwd = Eio.Stdenv.cwd env in
|
||||||
@ -146,6 +156,7 @@ Exception: Eio.Io Fs Already_exists _,
|
|||||||
```
|
```
|
||||||
|
|
||||||
If-missing create succeeds if already exists:
|
If-missing create succeeds if already exists:
|
||||||
|
|
||||||
```ocaml
|
```ocaml
|
||||||
# run @@ fun env ->
|
# run @@ fun env ->
|
||||||
let cwd = Eio.Stdenv.cwd env in
|
let cwd = Eio.Stdenv.cwd env in
|
||||||
@ -158,6 +169,7 @@ If-missing create succeeds if already exists:
|
|||||||
```
|
```
|
||||||
|
|
||||||
Truncate create succeeds if already exists, and truncates:
|
Truncate create succeeds if already exists, and truncates:
|
||||||
|
|
||||||
```ocaml
|
```ocaml
|
||||||
# run @@ fun env ->
|
# run @@ fun env ->
|
||||||
let cwd = Eio.Stdenv.cwd env in
|
let cwd = Eio.Stdenv.cwd env in
|
||||||
@ -172,6 +184,7 @@ Truncate create succeeds if already exists, and truncates:
|
|||||||
```
|
```
|
||||||
|
|
||||||
Error if no create and doesn't exist:
|
Error if no create and doesn't exist:
|
||||||
|
|
||||||
```ocaml
|
```ocaml
|
||||||
# run @@ fun env ->
|
# run @@ fun env ->
|
||||||
let cwd = Eio.Stdenv.cwd env in
|
let cwd = Eio.Stdenv.cwd env in
|
||||||
@ -183,6 +196,7 @@ Exception: Eio.Io Fs Not_found _,
|
|||||||
```
|
```
|
||||||
|
|
||||||
Appending to an existing file:
|
Appending to an existing file:
|
||||||
|
|
||||||
```ocaml
|
```ocaml
|
||||||
# run @@ fun env ->
|
# run @@ fun env ->
|
||||||
let cwd = Eio.Stdenv.cwd env in
|
let cwd = Eio.Stdenv.cwd env in
|
||||||
@ -215,12 +229,13 @@ Appending to an existing file:
|
|||||||
```
|
```
|
||||||
|
|
||||||
Creating directories with nesting, symlinks, etc:
|
Creating directories with nesting, symlinks, etc:
|
||||||
|
|
||||||
```ocaml
|
```ocaml
|
||||||
# run ~clear:["to-subdir"; "to-root"; "dangle"] @@ fun env ->
|
# run ~clear:["to-subdir"; "to-root"; "dangle"] @@ fun env ->
|
||||||
Unix.symlink "/" "to-root";
|
|
||||||
Unix.symlink "subdir" "to-subdir";
|
|
||||||
Unix.symlink "foo" "dangle";
|
|
||||||
let cwd = Eio.Stdenv.cwd env in
|
let cwd = Eio.Stdenv.cwd env in
|
||||||
|
Path.symlink ~link_to:"/" (cwd / "to-root");
|
||||||
|
Path.symlink ~link_to:"subdir" (cwd / "to-subdir");
|
||||||
|
Path.symlink ~link_to:"foo" (cwd / "dangle");
|
||||||
try_mkdir (cwd / "subdir");
|
try_mkdir (cwd / "subdir");
|
||||||
try_mkdir (cwd / "to-subdir/nested");
|
try_mkdir (cwd / "to-subdir/nested");
|
||||||
try_mkdir (cwd / "to-root/tmp/foo");
|
try_mkdir (cwd / "to-root/tmp/foo");
|
||||||
@ -384,10 +399,10 @@ Reads and writes follow symlinks, but unlink operates on the symlink itself:
|
|||||||
let file2 = cwd / "file2" in
|
let file2 = cwd / "file2" in
|
||||||
try_write_file ~create:(`Exclusive 0o600) file1 "data1";
|
try_write_file ~create:(`Exclusive 0o600) file1 "data1";
|
||||||
try_write_file ~create:(`Exclusive 0o400) file2 "data2";
|
try_write_file ~create:(`Exclusive 0o400) file2 "data2";
|
||||||
Unix.symlink "dir1/file1" "link1";
|
Path.symlink ~link_to:"dir1/file1" (cwd / "link1");
|
||||||
Unix.symlink "../file2" "dir1/link2";
|
Path.symlink ~link_to:"../file2" (cwd / "dir1/link2");
|
||||||
Unix.symlink "dir1" "linkdir";
|
Path.symlink ~link_to:"dir1" (cwd / "linkdir");
|
||||||
Unix.symlink "/" "linkroot";
|
Path.symlink ~link_to:"/" (cwd / "linkroot");
|
||||||
try_read_file file1;
|
try_read_file file1;
|
||||||
try_read_file (cwd / "link1");
|
try_read_file (cwd / "link1");
|
||||||
try_read_file (cwd / "linkdir" / "file1");
|
try_read_file (cwd / "linkdir" / "file1");
|
||||||
@ -504,6 +519,7 @@ Removing something that doesn't exist or is out of scope:
|
|||||||
# Limiting to a subdirectory
|
# Limiting to a subdirectory
|
||||||
|
|
||||||
Create a sandbox, write a file with it, then read it from outside:
|
Create a sandbox, write a file with it, then read it from outside:
|
||||||
|
|
||||||
```ocaml
|
```ocaml
|
||||||
# run ~clear:["sandbox"] @@ fun env ->
|
# run ~clear:["sandbox"] @@ fun env ->
|
||||||
Switch.run @@ fun sw ->
|
Switch.run @@ fun sw ->
|
||||||
@ -540,10 +556,10 @@ Create a sandbox, write a file with it, then read it from outside:
|
|||||||
reject (cwd / "/");
|
reject (cwd / "/");
|
||||||
test (cwd / "foo/bar/..");
|
test (cwd / "foo/bar/..");
|
||||||
test (fs / "foo/bar");
|
test (fs / "foo/bar");
|
||||||
Unix.symlink ".." "foo/up";
|
Path.symlink ~link_to:".." (cwd / "foo/up");
|
||||||
test (cwd / "foo/up/foo/bar");
|
test (cwd / "foo/up/foo/bar");
|
||||||
reject (cwd / "foo/up/../bar");
|
reject (cwd / "foo/up/../bar");
|
||||||
Unix.symlink "/" "foo/root";
|
Path.symlink ~link_to:"/" (cwd / "foo/root");
|
||||||
reject (cwd / "foo/root/..");
|
reject (cwd / "foo/root/..");
|
||||||
reject (cwd / "missing");
|
reject (cwd / "missing");
|
||||||
+open_dir <cwd:foo/bar> -> OK
|
+open_dir <cwd:foo/bar> -> OK
|
||||||
@ -566,6 +582,7 @@ Create a sandbox, write a file with it, then read it from outside:
|
|||||||
|
|
||||||
We create a directory and chdir into it.
|
We create a directory and chdir into it.
|
||||||
Using `cwd` we can't access the parent, but using `fs` we can:
|
Using `cwd` we can't access the parent, but using `fs` we can:
|
||||||
|
|
||||||
```ocaml
|
```ocaml
|
||||||
# run ~clear:["fs-test"; "outside-cwd"] @@ fun env ->
|
# run ~clear:["fs-test"; "outside-cwd"] @@ fun env ->
|
||||||
let cwd = Eio.Stdenv.cwd env in
|
let cwd = Eio.Stdenv.cwd env in
|
||||||
@ -604,7 +621,7 @@ Reading directory entries under `cwd` and outside of `cwd`.
|
|||||||
try_read_dir (tmpdir / ".");
|
try_read_dir (tmpdir / ".");
|
||||||
try_read_dir (tmpdir / "..");
|
try_read_dir (tmpdir / "..");
|
||||||
try_read_dir (tmpdir / "test-3");
|
try_read_dir (tmpdir / "test-3");
|
||||||
Unix.symlink "test-1" "readdir/link-1";
|
Path.symlink ~link_to:"test-1" (cwd / "readdir/link-1");
|
||||||
try_read_dir (tmpdir / "link-1");
|
try_read_dir (tmpdir / "link-1");
|
||||||
+mkdir <cwd:readdir> -> ok
|
+mkdir <cwd:readdir> -> ok
|
||||||
+mkdir <readdir:test-1> -> ok
|
+mkdir <readdir:test-1> -> ok
|
||||||
@ -650,6 +667,29 @@ Exception: Eio.Io Fs Permission_denied _,
|
|||||||
opening <cwd:/dev/null>
|
opening <cwd:/dev/null>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Symlinking and sandboxing:
|
||||||
|
|
||||||
|
```ocaml
|
||||||
|
# run ~clear:["hello.txt"; "world.txt"] @@ fun env ->
|
||||||
|
let cwd = Eio.Stdenv.cwd env in
|
||||||
|
Path.save ~create:(`Exclusive 0o600) (cwd / "hello.txt") "Hello World!";
|
||||||
|
try_symlink ~link_to:"hello.txt" (cwd / "../world.txt");
|
||||||
|
try_symlink ~link_to:"hello.txt" (cwd / "/world.txt");
|
||||||
|
try_symlink ~link_to:"hello.txt" (cwd / "world.txt");
|
||||||
|
traceln "world.txt -> hello.txt: %s" (Path.load (cwd / "world.txt"));
|
||||||
|
try_symlink ~link_to:"hello.txt" (cwd / "world.txt");
|
||||||
|
try_symlink ~link_to:"/" (cwd / "root");
|
||||||
|
try_read_dir (cwd / "root");;
|
||||||
|
+Eio.Io Fs Permission_denied _, creating symlink <cwd:../world.txt> -> hello.txt
|
||||||
|
+Eio.Io Fs Permission_denied _, creating symlink <cwd:/world.txt> -> hello.txt
|
||||||
|
+symlink <cwd:world.txt> -> "hello.txt"
|
||||||
|
+world.txt -> hello.txt: Hello World!
|
||||||
|
+Eio.Io Fs Already_exists _, creating symlink <cwd:world.txt> -> hello.txt
|
||||||
|
+symlink <cwd:root> -> "/"
|
||||||
|
+Eio.Io Fs Permission_denied _, reading directory <cwd:root>
|
||||||
|
- : unit = ()
|
||||||
|
```
|
||||||
|
|
||||||
## Streamling lines
|
## Streamling lines
|
||||||
|
|
||||||
```ocaml
|
```ocaml
|
||||||
@ -781,15 +821,15 @@ Unconfined:
|
|||||||
let cwd = Eio.Stdenv.cwd env in
|
let cwd = Eio.Stdenv.cwd env in
|
||||||
Switch.run @@ fun sw ->
|
Switch.run @@ fun sw ->
|
||||||
try_mkdir (cwd / "stat_subdir2");
|
try_mkdir (cwd / "stat_subdir2");
|
||||||
Unix.symlink "stat_subdir2" "symlink";
|
Path.symlink ~link_to:"stat_subdir2" (cwd / "symlink");
|
||||||
Unix.symlink "missing" "broken-symlink";
|
Path.symlink ~link_to:"missing" (cwd / "broken-symlink");
|
||||||
try_stat (cwd / "stat_subdir2");
|
try_stat (cwd / "stat_subdir2");
|
||||||
try_stat (cwd / "symlink");
|
try_stat (cwd / "symlink");
|
||||||
try_stat (cwd / "broken-symlink");
|
try_stat (cwd / "broken-symlink");
|
||||||
try_stat cwd;
|
try_stat cwd;
|
||||||
try_stat (cwd / "..");
|
try_stat (cwd / "..");
|
||||||
try_stat (cwd / "stat_subdir2/..");
|
try_stat (cwd / "stat_subdir2/..");
|
||||||
Unix.symlink ".." "parent-symlink";
|
Path.symlink ~link_to:".." (cwd / "parent-symlink");
|
||||||
try_stat (cwd / "parent-symlink");
|
try_stat (cwd / "parent-symlink");
|
||||||
try_stat (cwd / "missing1" / "missing2");
|
try_stat (cwd / "missing1" / "missing2");
|
||||||
+mkdir <cwd:stat_subdir2> -> ok
|
+mkdir <cwd:stat_subdir2> -> ok
|
||||||
@ -811,7 +851,7 @@ Unconfined:
|
|||||||
let fs = Eio.Stdenv.fs env in
|
let fs = Eio.Stdenv.fs env in
|
||||||
let cwd = Eio.Stdenv.cwd env in
|
let cwd = Eio.Stdenv.cwd env in
|
||||||
Switch.run @@ fun sw ->
|
Switch.run @@ fun sw ->
|
||||||
Unix.symlink "file" "symlink";
|
Path.symlink ~link_to:"file" (cwd / "symlink");
|
||||||
try_read_link (cwd / "symlink");
|
try_read_link (cwd / "symlink");
|
||||||
try_read_link (fs / "symlink");
|
try_read_link (fs / "symlink");
|
||||||
try_write_file (cwd / "file") "data" ~create:(`Exclusive 0o600);
|
try_write_file (cwd / "file") "data" ~create:(`Exclusive 0o600);
|
||||||
@ -945,3 +985,29 @@ Exception: Failure "Simulated error".
|
|||||||
+seek from end: 9
|
+seek from end: 9
|
||||||
- : unit = ()
|
- : unit = ()
|
||||||
```
|
```
|
||||||
|
|
||||||
|
# Extending paths
|
||||||
|
|
||||||
|
```ocaml
|
||||||
|
# run @@ fun env ->
|
||||||
|
let base = fst env#cwd in
|
||||||
|
List.iter (fun (a, b) -> traceln "%S / %S = %S" a b (snd ((base, a) / b))) [
|
||||||
|
"foo", "bar";
|
||||||
|
"foo/", "bar";
|
||||||
|
"foo", "/bar";
|
||||||
|
"foo", "";
|
||||||
|
"foo/", "";
|
||||||
|
"", "";
|
||||||
|
"", "bar";
|
||||||
|
"/", "";
|
||||||
|
]
|
||||||
|
+"foo" / "bar" = "foo/bar"
|
||||||
|
+"foo/" / "bar" = "foo/bar"
|
||||||
|
+"foo" / "/bar" = "/bar"
|
||||||
|
+"foo" / "" = "foo/"
|
||||||
|
+"foo/" / "" = "foo/"
|
||||||
|
+"" / "" = ""
|
||||||
|
+"" / "bar" = "bar"
|
||||||
|
+"/" / "" = "/"
|
||||||
|
- : unit = ()
|
||||||
|
```
|
||||||
|
@ -373,6 +373,29 @@ Wrapping a Unix FD as an Eio stream socket:
|
|||||||
- : unit = ()
|
- : unit = ()
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Wrapping a Unix FD as a listening Eio socket:
|
||||||
|
|
||||||
|
```ocaml
|
||||||
|
# run @@ fun ~net sw ->
|
||||||
|
let l = Unix.(socket PF_INET SOCK_STREAM 0) in
|
||||||
|
Unix.bind l (Unix.ADDR_INET (Unix.inet_addr_loopback, 8082));
|
||||||
|
Unix.listen l 40;
|
||||||
|
let l = Eio_unix.Net.import_socket_listening ~sw ~close_unix:true l in
|
||||||
|
Fiber.both
|
||||||
|
(fun () -> run_server ~sw l)
|
||||||
|
(fun () ->
|
||||||
|
run_client ~sw ~net ~addr:(`Tcp (Eio.Net.Ipaddr.V4.loopback, 8082));
|
||||||
|
traceln "Client finished - cancelling server";
|
||||||
|
raise Graceful_shutdown
|
||||||
|
);;
|
||||||
|
+Connecting to server...
|
||||||
|
+Server accepted connection from client
|
||||||
|
+Server received: "Hello from client"
|
||||||
|
+Client received: "Bye"
|
||||||
|
+Client finished - cancelling server
|
||||||
|
Exception: Graceful_shutdown.
|
||||||
|
```
|
||||||
|
|
||||||
Wrapping a Unix FD as an datagram Eio socket:
|
Wrapping a Unix FD as an datagram Eio socket:
|
||||||
|
|
||||||
```ocaml
|
```ocaml
|
||||||
@ -597,7 +620,9 @@ Exception: Eio.Io Fs Not_found _,
|
|||||||
```ocaml
|
```ocaml
|
||||||
# Eio_main.run @@ fun env ->
|
# Eio_main.run @@ fun env ->
|
||||||
let sockaddr = `Tcp (Eio.Net.Ipaddr.V4.loopback, 80) in
|
let sockaddr = `Tcp (Eio.Net.Ipaddr.V4.loopback, 80) in
|
||||||
Eio.Net.getnameinfo env#net sockaddr;;
|
let (host, service) = Eio.Net.getnameinfo env#net sockaddr in
|
||||||
|
let service = if service = "www" then "http" else service in (* OpenBSD *)
|
||||||
|
(host, service)
|
||||||
- : string * string = ("localhost", "http")
|
- : string * string = ("localhost", "http")
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -77,6 +77,37 @@ Two uses with a capacity of 2; they run in parallel:
|
|||||||
- : unit = ()
|
- : unit = ()
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Capacity of 1; two uses that cannot block and two normal uses; first 2 are parallel, next 2 are sequential.
|
||||||
|
Note that the pool always suspends the calling fiber when creating a new slot,
|
||||||
|
even if the fiber ends up providing the new slot to itself,
|
||||||
|
which is why the items get assigned out of order in this test.
|
||||||
|
|
||||||
|
```ocaml
|
||||||
|
# Eio_mock.Backend.run @@ fun () ->
|
||||||
|
let p0, r0 = Promise.create () in
|
||||||
|
let p1, r1 = Promise.create () in
|
||||||
|
let t = create 1 [p0; p1] ~dispose in
|
||||||
|
Fiber.all [
|
||||||
|
(fun () -> P.use t ~never_block:true (fun x -> traceln "A: using item %d" x; Fiber.yield (); traceln "A done"));
|
||||||
|
(fun () -> P.use t ~never_block:true (fun x -> traceln "B: using item %d" x; Fiber.yield (); traceln "B done"));
|
||||||
|
(fun () -> P.use t (fun x -> traceln "C: using item %d" x; Fiber.yield (); traceln "C done"));
|
||||||
|
(fun () -> P.use t (fun x -> traceln "D: using item %d" x; Fiber.yield (); traceln "D done"));
|
||||||
|
(fun () -> Promise.resolve r0 (Ok 0); Promise.resolve r1 (Ok 1));
|
||||||
|
];
|
||||||
|
+Creating item 0
|
||||||
|
+Creating item 1
|
||||||
|
+A: using item 1
|
||||||
|
+B: using item 0
|
||||||
|
+A done
|
||||||
|
+B done
|
||||||
|
+disposing 0
|
||||||
|
+C: using item 1
|
||||||
|
+C done
|
||||||
|
+D: using item 1
|
||||||
|
+D done
|
||||||
|
- : unit = ()
|
||||||
|
```
|
||||||
|
|
||||||
## Cancellation
|
## Cancellation
|
||||||
|
|
||||||
```ocaml
|
```ocaml
|
||||||
|
@ -145,24 +145,24 @@ If a command fails, we get shown the arguments (quoted if necessary):
|
|||||||
|
|
||||||
```ocaml
|
```ocaml
|
||||||
# run @@ fun mgr env ->
|
# run @@ fun mgr env ->
|
||||||
Process.run mgr ["bash"; "-c"; "exit 3"; ""; "foo"; "\"bar\""];;
|
Process.run mgr ["sh"; "-c"; "exit 3"; ""; "foo"; "\"bar\""];;
|
||||||
Exception:
|
Exception:
|
||||||
Eio.Io Process Child_error Exited (code 3),
|
Eio.Io Process Child_error Exited (code 3),
|
||||||
running command: bash -c "exit 3" "" foo "\"bar\""
|
running command: sh -c "exit 3" "" foo "\"bar\""
|
||||||
```
|
```
|
||||||
|
|
||||||
Exit code success can be determined by is_success (Process.run):
|
Exit code success can be determined by is_success (Process.run):
|
||||||
|
|
||||||
```ocaml
|
```ocaml
|
||||||
# run @@ fun mgr env ->
|
# run @@ fun mgr env ->
|
||||||
Process.run ~is_success:(Int.equal 3) mgr ["bash"; "-c"; "exit 3"];;
|
Process.run ~is_success:(Int.equal 3) mgr ["sh"; "-c"; "exit 3"];;
|
||||||
- : unit = ()
|
- : unit = ()
|
||||||
|
|
||||||
# run @@ fun mgr env ->
|
# run @@ fun mgr env ->
|
||||||
Process.run ~is_success:(Int.equal 3) mgr ["bash"; "-c"; "exit 0"];;
|
Process.run ~is_success:(Int.equal 3) mgr ["sh"; "-c"; "exit 0"];;
|
||||||
Exception:
|
Exception:
|
||||||
Eio.Io Process Child_error Exited (code 0),
|
Eio.Io Process Child_error Exited (code 0),
|
||||||
running command: bash -c "exit 0"
|
running command: sh -c "exit 0"
|
||||||
```
|
```
|
||||||
|
|
||||||
Exit code success can be determined by is_success (Process.parse_out):
|
Exit code success can be determined by is_success (Process.parse_out):
|
||||||
|
@ -231,3 +231,21 @@ Cancellation:
|
|||||||
+Cancel sleeper
|
+Cancel sleeper
|
||||||
Exception: Invalid_argument "No further events scheduled on mock clock".
|
Exception: Invalid_argument "No further events scheduled on mock clock".
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Sleep:
|
||||||
|
|
||||||
|
```ocaml
|
||||||
|
# try
|
||||||
|
Eio_mock.Backend.run_full @@ fun env ->
|
||||||
|
let timeout = Eio.Time.Timeout.seconds env#mono_clock 2. in
|
||||||
|
Eio.Time.Timeout.sleep timeout;
|
||||||
|
traceln "Timeout done";
|
||||||
|
Eio.Time.Timeout.(sleep none);
|
||||||
|
assert false
|
||||||
|
with Eio_mock.Backend.Deadlock_detected ->
|
||||||
|
traceln "Never finished";;
|
||||||
|
+mock time is now 2
|
||||||
|
+Timeout done
|
||||||
|
+Never finished
|
||||||
|
- : unit = ()
|
||||||
|
```
|
||||||
|
Loading…
x
Reference in New Issue
Block a user